mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2024-11-29 06:50:59 +00:00
Port everything over to gstreamer-rs
And remove all unneeded code and unused external crates.
This commit is contained in:
parent
5ae66e191c
commit
7e7433037d
27 changed files with 1194 additions and 4214 deletions
|
@ -8,7 +8,7 @@ license = "MIT/Apache-2.0"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
url = "1.1"
|
url = "1.1"
|
||||||
gst-plugin = { path="../gst-plugin" }
|
gst-plugin = { path="../gst-plugin" }
|
||||||
slog = "2.0"
|
gstreamer = { git = "https://github.com/sdroege/gstreamer-rs", features = ["v1_10"] }
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "gstrsfile"
|
name = "gstrsfile"
|
||||||
|
|
|
@ -15,11 +15,9 @@ use std::convert::From;
|
||||||
|
|
||||||
use gst_plugin::error::*;
|
use gst_plugin::error::*;
|
||||||
use gst_plugin::sink::*;
|
use gst_plugin::sink::*;
|
||||||
use gst_plugin::buffer::*;
|
|
||||||
use gst_plugin::utils::*;
|
|
||||||
use gst_plugin::log::*;
|
|
||||||
|
|
||||||
use slog::Logger;
|
use gst;
|
||||||
|
use gst::prelude::*;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum StreamingState {
|
enum StreamingState {
|
||||||
|
@ -30,30 +28,31 @@ enum StreamingState {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FileSink {
|
pub struct FileSink {
|
||||||
streaming_state: StreamingState,
|
streaming_state: StreamingState,
|
||||||
logger: Logger,
|
cat: gst::DebugCategory,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileSink {
|
impl FileSink {
|
||||||
pub fn new(element: Element) -> FileSink {
|
pub fn new(_sink: &RsSinkWrapper) -> FileSink {
|
||||||
FileSink {
|
FileSink {
|
||||||
streaming_state: StreamingState::Stopped,
|
streaming_state: StreamingState::Stopped,
|
||||||
logger: Logger::root(
|
cat: gst::DebugCategory::new(
|
||||||
GstDebugDrain::new(Some(&element), "rsfilesink", 0, "Rust file sink"),
|
"rsfilesink",
|
||||||
o!(),
|
gst::DebugColorFlags::empty(),
|
||||||
|
"Rust file source",
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_boxed(element: Element) -> Box<Sink> {
|
pub fn new_boxed(sink: &RsSinkWrapper) -> Box<Sink> {
|
||||||
Box::new(FileSink::new(element))
|
Box::new(FileSink::new(sink))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_uri(uri: &Url) -> Result<(), UriError> {
|
fn validate_uri(uri: &Url) -> Result<(), UriError> {
|
||||||
let _ = try!(uri.to_file_path().or_else(|_| {
|
let _ = try!(uri.to_file_path().or_else(|_| {
|
||||||
Err(UriError::new(
|
Err(UriError::new(
|
||||||
UriErrorKind::UnsupportedProtocol,
|
gst::URIError::UnsupportedProtocol,
|
||||||
Some(format!("Unsupported file URI '{}'", uri.as_str())),
|
format!("Unsupported file URI '{}'", uri.as_str()),
|
||||||
))
|
))
|
||||||
}));
|
}));
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -64,28 +63,37 @@ impl Sink for FileSink {
|
||||||
Box::new(validate_uri)
|
Box::new(validate_uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start(&mut self, uri: Url) -> Result<(), ErrorMessage> {
|
fn start(&mut self, sink: &RsSinkWrapper, uri: Url) -> Result<(), ErrorMessage> {
|
||||||
if let StreamingState::Started { .. } = self.streaming_state {
|
if let StreamingState::Started { .. } = self.streaming_state {
|
||||||
return Err(error_msg!(SinkError::Failure, ["Sink already started"]));
|
return Err(error_msg!(
|
||||||
|
gst::LibraryError::Failed,
|
||||||
|
["Sink already started"]
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let location = try!(uri.to_file_path().or_else(|_| {
|
let location = try!(uri.to_file_path().or_else(|_| {
|
||||||
error!(self.logger, "Unsupported file URI '{}'", uri.as_str());
|
gst_error!(
|
||||||
|
self.cat,
|
||||||
|
obj: sink,
|
||||||
|
"Unsupported file URI '{}'",
|
||||||
|
uri.as_str()
|
||||||
|
);
|
||||||
Err(error_msg!(
|
Err(error_msg!(
|
||||||
SinkError::Failure,
|
gst::LibraryError::Failed,
|
||||||
["Unsupported file URI '{}'", uri.as_str()]
|
["Unsupported file URI '{}'", uri.as_str()]
|
||||||
))
|
))
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
let file = try!(File::create(location.as_path()).or_else(|err| {
|
let file = try!(File::create(location.as_path()).or_else(|err| {
|
||||||
error!(
|
gst_error!(
|
||||||
self.logger,
|
self.cat,
|
||||||
|
obj: sink,
|
||||||
"Could not open file for writing: {}",
|
"Could not open file for writing: {}",
|
||||||
err.to_string()
|
err.to_string()
|
||||||
);
|
);
|
||||||
Err(error_msg!(
|
Err(error_msg!(
|
||||||
SinkError::OpenFailed,
|
gst::ResourceError::OpenWrite,
|
||||||
[
|
[
|
||||||
"Could not open file for writing '{}': {}",
|
"Could not open file for writing '{}': {}",
|
||||||
location.to_str().unwrap_or("Non-UTF8 path"),
|
location.to_str().unwrap_or("Non-UTF8 path"),
|
||||||
|
@ -94,7 +102,7 @@ impl Sink for FileSink {
|
||||||
))
|
))
|
||||||
}));
|
}));
|
||||||
|
|
||||||
debug!(self.logger, "Opened file {:?}", file);
|
gst_debug!(self.cat, obj: sink, "Opened file {:?}", file);
|
||||||
|
|
||||||
self.streaming_state = StreamingState::Started {
|
self.streaming_state = StreamingState::Started {
|
||||||
file: file,
|
file: file,
|
||||||
|
@ -104,17 +112,17 @@ impl Sink for FileSink {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop(&mut self) -> Result<(), ErrorMessage> {
|
fn stop(&mut self, _sink: &RsSinkWrapper) -> Result<(), ErrorMessage> {
|
||||||
self.streaming_state = StreamingState::Stopped;
|
self.streaming_state = StreamingState::Stopped;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&mut self, buffer: &Buffer) -> Result<(), FlowError> {
|
fn render(&mut self, sink: &RsSinkWrapper, buffer: &gst::BufferRef) -> Result<(), FlowError> {
|
||||||
let logger = &self.logger;
|
let cat = self.cat;
|
||||||
let streaming_state = &mut self.streaming_state;
|
let streaming_state = &mut self.streaming_state;
|
||||||
|
|
||||||
trace!(logger, "Rendering {:?}", buffer);
|
gst_trace!(cat, obj: sink, "Rendering {:?}", buffer);
|
||||||
|
|
||||||
let (file, position) = match *streaming_state {
|
let (file, position) = match *streaming_state {
|
||||||
StreamingState::Started {
|
StreamingState::Started {
|
||||||
|
@ -123,25 +131,26 @@ impl Sink for FileSink {
|
||||||
} => (file, position),
|
} => (file, position),
|
||||||
StreamingState::Stopped => {
|
StreamingState::Stopped => {
|
||||||
return Err(FlowError::Error(
|
return Err(FlowError::Error(
|
||||||
error_msg!(SinkError::Failure, ["Not started yet"]),
|
error_msg!(gst::LibraryError::Failed, ["Not started yet"]),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let map = match buffer.map_read() {
|
let map = match buffer.map_readable() {
|
||||||
None => {
|
None => {
|
||||||
return Err(FlowError::Error(
|
return Err(FlowError::Error(error_msg!(
|
||||||
error_msg!(SinkError::Failure, ["Failed to map buffer"]),
|
gst::LibraryError::Failed,
|
||||||
));
|
["Failed to map buffer"]
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
Some(map) => map,
|
Some(map) => map,
|
||||||
};
|
};
|
||||||
let data = map.as_slice();
|
let data = map.as_slice();
|
||||||
|
|
||||||
try!(file.write_all(data).or_else(|err| {
|
try!(file.write_all(data).or_else(|err| {
|
||||||
error!(logger, "Failed to write: {}", err);
|
gst_error!(cat, obj: sink, "Failed to write: {}", err);
|
||||||
Err(FlowError::Error(error_msg!(
|
Err(FlowError::Error(error_msg!(
|
||||||
SinkError::WriteFailed,
|
gst::ResourceError::Write,
|
||||||
["Failed to write: {}", err]
|
["Failed to write: {}", err]
|
||||||
)))
|
)))
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -13,11 +13,9 @@ use url::Url;
|
||||||
|
|
||||||
use gst_plugin::error::*;
|
use gst_plugin::error::*;
|
||||||
use gst_plugin::source::*;
|
use gst_plugin::source::*;
|
||||||
use gst_plugin::buffer::*;
|
|
||||||
use gst_plugin::log::*;
|
|
||||||
use gst_plugin::utils::*;
|
|
||||||
|
|
||||||
use slog::Logger;
|
use gst;
|
||||||
|
use gst::prelude::*;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum StreamingState {
|
enum StreamingState {
|
||||||
|
@ -28,30 +26,31 @@ enum StreamingState {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FileSrc {
|
pub struct FileSrc {
|
||||||
streaming_state: StreamingState,
|
streaming_state: StreamingState,
|
||||||
logger: Logger,
|
cat: gst::DebugCategory,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileSrc {
|
impl FileSrc {
|
||||||
pub fn new(element: Element) -> FileSrc {
|
pub fn new(_src: &RsSrcWrapper) -> FileSrc {
|
||||||
FileSrc {
|
FileSrc {
|
||||||
streaming_state: StreamingState::Stopped,
|
streaming_state: StreamingState::Stopped,
|
||||||
logger: Logger::root(
|
cat: gst::DebugCategory::new(
|
||||||
GstDebugDrain::new(Some(&element), "rsfilesrc", 0, "Rust file source"),
|
"rsfilesrc",
|
||||||
o!(),
|
gst::DebugColorFlags::empty(),
|
||||||
|
"Rust file source",
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_boxed(element: Element) -> Box<Source> {
|
pub fn new_boxed(src: &RsSrcWrapper) -> Box<Source> {
|
||||||
Box::new(FileSrc::new(element))
|
Box::new(FileSrc::new(src))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_uri(uri: &Url) -> Result<(), UriError> {
|
fn validate_uri(uri: &Url) -> Result<(), UriError> {
|
||||||
let _ = try!(uri.to_file_path().or_else(|_| {
|
let _ = try!(uri.to_file_path().or_else(|_| {
|
||||||
Err(UriError::new(
|
Err(UriError::new(
|
||||||
UriErrorKind::UnsupportedProtocol,
|
gst::URIError::UnsupportedProtocol,
|
||||||
Some(format!("Unsupported file URI '{}'", uri.as_str())),
|
format!("Unsupported file URI '{}'", uri.as_str()),
|
||||||
))
|
))
|
||||||
}));
|
}));
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -62,11 +61,11 @@ impl Source for FileSrc {
|
||||||
Box::new(validate_uri)
|
Box::new(validate_uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_seekable(&self) -> bool {
|
fn is_seekable(&self, _src: &RsSrcWrapper) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_size(&self) -> Option<u64> {
|
fn get_size(&self, _src: &RsSrcWrapper) -> Option<u64> {
|
||||||
if let StreamingState::Started { ref file, .. } = self.streaming_state {
|
if let StreamingState::Started { ref file, .. } = self.streaming_state {
|
||||||
file.metadata().ok().map(|m| m.len())
|
file.metadata().ok().map(|m| m.len())
|
||||||
} else {
|
} else {
|
||||||
|
@ -74,27 +73,36 @@ impl Source for FileSrc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start(&mut self, uri: Url) -> Result<(), ErrorMessage> {
|
fn start(&mut self, src: &RsSrcWrapper, uri: Url) -> Result<(), ErrorMessage> {
|
||||||
if let StreamingState::Started { .. } = self.streaming_state {
|
if let StreamingState::Started { .. } = self.streaming_state {
|
||||||
return Err(error_msg!(SourceError::Failure, ["Source already started"]));
|
return Err(error_msg!(
|
||||||
|
gst::LibraryError::Failed,
|
||||||
|
["Source already started"]
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let location = try!(uri.to_file_path().or_else(|_| {
|
let location = try!(uri.to_file_path().or_else(|_| {
|
||||||
error!(self.logger, "Unsupported file URI '{}'", uri.as_str());
|
gst_error!(
|
||||||
|
self.cat,
|
||||||
|
obj: src,
|
||||||
|
"Unsupported file URI '{}'",
|
||||||
|
uri.as_str()
|
||||||
|
);
|
||||||
Err(error_msg!(
|
Err(error_msg!(
|
||||||
SourceError::Failure,
|
gst::LibraryError::Failed,
|
||||||
["Unsupported file URI '{}'", uri.as_str()]
|
["Unsupported file URI '{}'", uri.as_str()]
|
||||||
))
|
))
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let file = try!(File::open(location.as_path()).or_else(|err| {
|
let file = try!(File::open(location.as_path()).or_else(|err| {
|
||||||
error!(
|
gst_error!(
|
||||||
self.logger,
|
self.cat,
|
||||||
|
obj: src,
|
||||||
"Could not open file for reading: {}",
|
"Could not open file for reading: {}",
|
||||||
err.to_string()
|
err.to_string()
|
||||||
);
|
);
|
||||||
Err(error_msg!(
|
Err(error_msg!(
|
||||||
SourceError::OpenFailed,
|
gst::ResourceError::OpenRead,
|
||||||
[
|
[
|
||||||
"Could not open file for reading '{}': {}",
|
"Could not open file for reading '{}': {}",
|
||||||
location.to_str().unwrap_or("Non-UTF8 path"),
|
location.to_str().unwrap_or("Non-UTF8 path"),
|
||||||
|
@ -103,7 +111,7 @@ impl Source for FileSrc {
|
||||||
))
|
))
|
||||||
}));
|
}));
|
||||||
|
|
||||||
debug!(self.logger, "Opened file {:?}", file);
|
gst_debug!(self.cat, obj: src, "Opened file {:?}", file);
|
||||||
|
|
||||||
self.streaming_state = StreamingState::Started {
|
self.streaming_state = StreamingState::Started {
|
||||||
file: file,
|
file: file,
|
||||||
|
@ -113,14 +121,20 @@ impl Source for FileSrc {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop(&mut self) -> Result<(), ErrorMessage> {
|
fn stop(&mut self, _src: &RsSrcWrapper) -> Result<(), ErrorMessage> {
|
||||||
self.streaming_state = StreamingState::Stopped;
|
self.streaming_state = StreamingState::Stopped;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fill(&mut self, offset: u64, _: u32, buffer: &mut Buffer) -> Result<(), FlowError> {
|
fn fill(
|
||||||
let logger = &self.logger;
|
&mut self,
|
||||||
|
src: &RsSrcWrapper,
|
||||||
|
offset: u64,
|
||||||
|
_: u32,
|
||||||
|
buffer: &mut gst::BufferRef,
|
||||||
|
) -> Result<(), FlowError> {
|
||||||
|
let cat = self.cat;
|
||||||
let streaming_state = &mut self.streaming_state;
|
let streaming_state = &mut self.streaming_state;
|
||||||
|
|
||||||
let (file, position) = match *streaming_state {
|
let (file, position) = match *streaming_state {
|
||||||
|
@ -130,16 +144,16 @@ impl Source for FileSrc {
|
||||||
} => (file, position),
|
} => (file, position),
|
||||||
StreamingState::Stopped => {
|
StreamingState::Stopped => {
|
||||||
return Err(FlowError::Error(
|
return Err(FlowError::Error(
|
||||||
error_msg!(SourceError::Failure, ["Not started yet"]),
|
error_msg!(gst::LibraryError::Failed, ["Not started yet"]),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if *position != offset {
|
if *position != offset {
|
||||||
try!(file.seek(SeekFrom::Start(offset)).or_else(|err| {
|
try!(file.seek(SeekFrom::Start(offset)).or_else(|err| {
|
||||||
error!(logger, "Failed to seek to {}: {:?}", offset, err);
|
gst_error!(cat, obj: src, "Failed to seek to {}: {:?}", offset, err);
|
||||||
Err(FlowError::Error(error_msg!(
|
Err(FlowError::Error(error_msg!(
|
||||||
SourceError::SeekFailed,
|
gst::ResourceError::Seek,
|
||||||
["Failed to seek to {}: {}", offset, err.to_string()]
|
["Failed to seek to {}: {}", offset, err.to_string()]
|
||||||
)))
|
)))
|
||||||
}));
|
}));
|
||||||
|
@ -147,11 +161,12 @@ impl Source for FileSrc {
|
||||||
}
|
}
|
||||||
|
|
||||||
let size = {
|
let size = {
|
||||||
let mut map = match buffer.map_readwrite() {
|
let mut map = match buffer.map_writable() {
|
||||||
None => {
|
None => {
|
||||||
return Err(FlowError::Error(
|
return Err(FlowError::Error(error_msg!(
|
||||||
error_msg!(SourceError::Failure, ["Failed to map buffer"]),
|
gst::LibraryError::Failed,
|
||||||
));
|
["Failed to map buffer"]
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
Some(map) => map,
|
Some(map) => map,
|
||||||
};
|
};
|
||||||
|
@ -159,9 +174,9 @@ impl Source for FileSrc {
|
||||||
let data = map.as_mut_slice();
|
let data = map.as_mut_slice();
|
||||||
|
|
||||||
try!(file.read(data).or_else(|err| {
|
try!(file.read(data).or_else(|err| {
|
||||||
error!(logger, "Failed to read: {:?}", err);
|
gst_error!(cat, obj: src, "Failed to read: {:?}", err);
|
||||||
Err(FlowError::Error(error_msg!(
|
Err(FlowError::Error(error_msg!(
|
||||||
SourceError::ReadFailed,
|
gst::ResourceError::Read,
|
||||||
["Failed to read at {}: {}", offset, err.to_string()]
|
["Failed to read at {}: {}", offset, err.to_string()]
|
||||||
)))
|
)))
|
||||||
}))
|
}))
|
||||||
|
@ -174,7 +189,7 @@ impl Source for FileSrc {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn seek(&mut self, _: u64, _: Option<u64>) -> Result<(), ErrorMessage> {
|
fn seek(&mut self, _src: &RsSrcWrapper, _: u64, _: Option<u64>) -> Result<(), ErrorMessage> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,9 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate gst_plugin;
|
extern crate gst_plugin;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate slog;
|
extern crate gstreamer as gst;
|
||||||
extern crate url;
|
extern crate url;
|
||||||
|
|
||||||
use gst_plugin::plugin::*;
|
|
||||||
use gst_plugin::source::*;
|
use gst_plugin::source::*;
|
||||||
use gst_plugin::sink::*;
|
use gst_plugin::sink::*;
|
||||||
|
|
||||||
|
@ -24,7 +23,7 @@ mod filesink;
|
||||||
use filesrc::FileSrc;
|
use filesrc::FileSrc;
|
||||||
use filesink::FileSink;
|
use filesink::FileSink;
|
||||||
|
|
||||||
fn plugin_init(plugin: &Plugin) -> bool {
|
fn plugin_init(plugin: &gst::Plugin) -> bool {
|
||||||
source_register(
|
source_register(
|
||||||
plugin,
|
plugin,
|
||||||
SourceInfo {
|
SourceInfo {
|
||||||
|
|
|
@ -8,7 +8,8 @@ license = "MIT/Apache-2.0"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
url = "1.1"
|
url = "1.1"
|
||||||
gst-plugin = { path="../gst-plugin" }
|
gst-plugin = { path="../gst-plugin" }
|
||||||
slog = "2.0"
|
gstreamer = { git = "https://github.com/sdroege/gstreamer-rs", features = ["v1_10"] }
|
||||||
|
num-rational = { version = "0.1", default-features = false, features = [] }
|
||||||
nom = "3.0"
|
nom = "3.0"
|
||||||
flavors = {git = "https://github.com/rust-av/flavors.git"}
|
flavors = {git = "https://github.com/rust-av/flavors.git"}
|
||||||
muldiv = "0.1"
|
muldiv = "0.1"
|
||||||
|
|
|
@ -16,20 +16,16 @@ use flavors::parser as flavors;
|
||||||
|
|
||||||
use gst_plugin::error::*;
|
use gst_plugin::error::*;
|
||||||
use gst_plugin::demuxer::*;
|
use gst_plugin::demuxer::*;
|
||||||
use gst_plugin::buffer::*;
|
|
||||||
use gst_plugin::adapter::*;
|
use gst_plugin::adapter::*;
|
||||||
use gst_plugin::utils;
|
|
||||||
use gst_plugin::utils::Element;
|
|
||||||
use gst_plugin::log::*;
|
|
||||||
use gst_plugin::caps::Caps;
|
|
||||||
use gst_plugin::miniobject::*;
|
|
||||||
use gst_plugin::value::Rational32;
|
|
||||||
use gst_plugin::bytes::*;
|
use gst_plugin::bytes::*;
|
||||||
|
|
||||||
use slog::Logger;
|
|
||||||
|
|
||||||
use muldiv::*;
|
use muldiv::*;
|
||||||
|
|
||||||
|
use gst;
|
||||||
|
use gst::prelude::*;
|
||||||
|
|
||||||
|
use num_rational::Rational32;
|
||||||
|
|
||||||
const AUDIO_STREAM_ID: u32 = 0;
|
const AUDIO_STREAM_ID: u32 = 0;
|
||||||
const VIDEO_STREAM_ID: u32 = 1;
|
const VIDEO_STREAM_ID: u32 = 1;
|
||||||
|
|
||||||
|
@ -56,8 +52,8 @@ struct StreamingState {
|
||||||
|
|
||||||
metadata: Option<Metadata>,
|
metadata: Option<Metadata>,
|
||||||
|
|
||||||
aac_sequence_header: Option<GstRc<Buffer>>,
|
aac_sequence_header: Option<gst::Buffer>,
|
||||||
avc_sequence_header: Option<GstRc<Buffer>>,
|
avc_sequence_header: Option<gst::Buffer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StreamingState {
|
impl StreamingState {
|
||||||
|
@ -83,7 +79,7 @@ struct AudioFormat {
|
||||||
width: u8,
|
width: u8,
|
||||||
channels: u8,
|
channels: u8,
|
||||||
bitrate: Option<u32>,
|
bitrate: Option<u32>,
|
||||||
aac_sequence_header: Option<GstRc<Buffer>>,
|
aac_sequence_header: Option<gst::Buffer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignores bitrate
|
// Ignores bitrate
|
||||||
|
@ -99,7 +95,7 @@ impl AudioFormat {
|
||||||
fn new(
|
fn new(
|
||||||
data_header: &flavors::AudioDataHeader,
|
data_header: &flavors::AudioDataHeader,
|
||||||
metadata: &Option<Metadata>,
|
metadata: &Option<Metadata>,
|
||||||
aac_sequence_header: &Option<GstRc<Buffer>>,
|
aac_sequence_header: &Option<gst::Buffer>,
|
||||||
) -> AudioFormat {
|
) -> AudioFormat {
|
||||||
let numeric_rate = match (data_header.sound_format, data_header.sound_rate) {
|
let numeric_rate = match (data_header.sound_format, data_header.sound_rate) {
|
||||||
(flavors::SoundFormat::NELLYMOSER_16KHZ_MONO, _) => 16000,
|
(flavors::SoundFormat::NELLYMOSER_16KHZ_MONO, _) => 16000,
|
||||||
|
@ -143,55 +139,45 @@ impl AudioFormat {
|
||||||
changed
|
changed
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_string(&self) -> Option<String> {
|
fn to_caps(&self) -> Option<gst::Caps> {
|
||||||
self.to_caps().map(|c| c.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_caps(&self) -> Option<GstRc<Caps>> {
|
|
||||||
let mut caps = match self.format {
|
let mut caps = match self.format {
|
||||||
flavors::SoundFormat::MP3 | flavors::SoundFormat::MP3_8KHZ => Some(Caps::new_simple(
|
flavors::SoundFormat::MP3 | flavors::SoundFormat::MP3_8KHZ => Some(
|
||||||
"audio/mpeg",
|
gst::Caps::new_simple("audio/mpeg", &[("mpegversion", &1i32), ("layer", &3i32)]),
|
||||||
&[("mpegversion", 1i32.into()), ("layer", 3i32.into())],
|
),
|
||||||
)),
|
|
||||||
flavors::SoundFormat::PCM_NE | flavors::SoundFormat::PCM_LE => {
|
flavors::SoundFormat::PCM_NE | flavors::SoundFormat::PCM_LE => {
|
||||||
if self.rate != 0 && self.channels != 0 {
|
if self.rate != 0 && self.channels != 0 {
|
||||||
// Assume little-endian for "PCM_NE", it's probably more common and we have no
|
// Assume little-endian for "PCM_NE", it's probably more common and we have no
|
||||||
// way to know what the endianness of the system creating the stream was
|
// way to know what the endianness of the system creating the stream was
|
||||||
Some(Caps::new_simple(
|
Some(gst::Caps::new_simple(
|
||||||
"audio/x-raw",
|
"audio/x-raw",
|
||||||
&[
|
&[
|
||||||
("layout", "interleaved".into()),
|
("layout", &"interleaved"),
|
||||||
(
|
("format", &if self.width == 8 { "U8" } else { "S16LE" }),
|
||||||
"format",
|
|
||||||
if self.width == 8 {
|
|
||||||
"U8".into()
|
|
||||||
} else {
|
|
||||||
"S16LE".into()
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
flavors::SoundFormat::ADPCM => Some(Caps::new_simple(
|
flavors::SoundFormat::ADPCM => Some(gst::Caps::new_simple(
|
||||||
"audio/x-adpcm",
|
"audio/x-adpcm",
|
||||||
&[("layout", "swf".into())],
|
&[("layout", &"swf")],
|
||||||
)),
|
)),
|
||||||
flavors::SoundFormat::NELLYMOSER_16KHZ_MONO |
|
flavors::SoundFormat::NELLYMOSER_16KHZ_MONO |
|
||||||
flavors::SoundFormat::NELLYMOSER_8KHZ_MONO |
|
flavors::SoundFormat::NELLYMOSER_8KHZ_MONO |
|
||||||
flavors::SoundFormat::NELLYMOSER => Some(Caps::new_simple("audio/x-nellymoser", &[])),
|
flavors::SoundFormat::NELLYMOSER => {
|
||||||
flavors::SoundFormat::PCM_ALAW => Some(Caps::new_simple("audio/x-alaw", &[])),
|
Some(gst::Caps::new_simple("audio/x-nellymoser", &[]))
|
||||||
flavors::SoundFormat::PCM_ULAW => Some(Caps::new_simple("audio/x-mulaw", &[])),
|
}
|
||||||
|
flavors::SoundFormat::PCM_ALAW => Some(gst::Caps::new_simple("audio/x-alaw", &[])),
|
||||||
|
flavors::SoundFormat::PCM_ULAW => Some(gst::Caps::new_simple("audio/x-mulaw", &[])),
|
||||||
flavors::SoundFormat::AAC => self.aac_sequence_header.as_ref().map(|header| {
|
flavors::SoundFormat::AAC => self.aac_sequence_header.as_ref().map(|header| {
|
||||||
Caps::new_simple(
|
gst::Caps::new_simple(
|
||||||
"audio/mpeg",
|
"audio/mpeg",
|
||||||
&[
|
&[
|
||||||
("mpegversion", 4i32.into()),
|
("mpegversion", &4i32),
|
||||||
("framed", true.into()),
|
("framed", &true),
|
||||||
("stream-format", "raw".into()),
|
("stream-format", &"raw"),
|
||||||
("codec_data", header.as_ref().into()),
|
("codec_data", &header),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
@ -219,7 +205,7 @@ impl AudioFormat {
|
||||||
|
|
||||||
data.into_inner()
|
data.into_inner()
|
||||||
};
|
};
|
||||||
let header = Buffer::from_vec(header).unwrap();
|
let header = gst::Buffer::from_vec(header).unwrap();
|
||||||
|
|
||||||
let comment = {
|
let comment = {
|
||||||
let comment_size = 4 + 7 /* nothing */ + 4 + 1;
|
let comment_size = 4 + 7 /* nothing */ + 4 + 1;
|
||||||
|
@ -233,11 +219,11 @@ impl AudioFormat {
|
||||||
|
|
||||||
data.into_inner()
|
data.into_inner()
|
||||||
};
|
};
|
||||||
let comment = Buffer::from_vec(comment).unwrap();
|
let comment = gst::Buffer::from_vec(comment).unwrap();
|
||||||
|
|
||||||
Some(Caps::new_simple(
|
Some(gst::Caps::new_simple(
|
||||||
"audio/x-speex",
|
"audio/x-speex",
|
||||||
&[("streamheader", vec![header.into(), comment.into()].into())],
|
&[("streamheader", &gst::Array::new(&[&header, &comment]))],
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
flavors::SoundFormat::DEVICE_SPECIFIC => {
|
flavors::SoundFormat::DEVICE_SPECIFIC => {
|
||||||
|
@ -250,14 +236,14 @@ impl AudioFormat {
|
||||||
caps.as_mut().map(|c| {
|
caps.as_mut().map(|c| {
|
||||||
c.get_mut()
|
c.get_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set_simple(&[("rate", (self.rate as i32).into())])
|
.set_simple(&[("rate", &(self.rate as i32))])
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if self.channels != 0 {
|
if self.channels != 0 {
|
||||||
caps.as_mut().map(|c| {
|
caps.as_mut().map(|c| {
|
||||||
c.get_mut()
|
c.get_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set_simple(&[("channels", (self.channels as i32).into())])
|
.set_simple(&[("channels", &(self.channels as i32))])
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,14 +259,14 @@ struct VideoFormat {
|
||||||
pixel_aspect_ratio: Option<Rational32>,
|
pixel_aspect_ratio: Option<Rational32>,
|
||||||
framerate: Option<Rational32>,
|
framerate: Option<Rational32>,
|
||||||
bitrate: Option<u32>,
|
bitrate: Option<u32>,
|
||||||
avc_sequence_header: Option<GstRc<Buffer>>,
|
avc_sequence_header: Option<gst::Buffer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VideoFormat {
|
impl VideoFormat {
|
||||||
fn new(
|
fn new(
|
||||||
data_header: &flavors::VideoDataHeader,
|
data_header: &flavors::VideoDataHeader,
|
||||||
metadata: &Option<Metadata>,
|
metadata: &Option<Metadata>,
|
||||||
avc_sequence_header: &Option<GstRc<Buffer>>,
|
avc_sequence_header: &Option<gst::Buffer>,
|
||||||
) -> VideoFormat {
|
) -> VideoFormat {
|
||||||
VideoFormat {
|
VideoFormat {
|
||||||
format: data_header.codec_id,
|
format: data_header.codec_id,
|
||||||
|
@ -324,33 +310,26 @@ impl VideoFormat {
|
||||||
changed
|
changed
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_string(&self) -> Option<String> {
|
fn to_caps(&self) -> Option<gst::Caps> {
|
||||||
self.to_caps().map(|caps| caps.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_caps(&self) -> Option<GstRc<Caps>> {
|
|
||||||
let mut caps = match self.format {
|
let mut caps = match self.format {
|
||||||
flavors::CodecId::SORENSON_H263 => Some(Caps::new_simple(
|
flavors::CodecId::SORENSON_H263 => Some(gst::Caps::new_simple(
|
||||||
"video/x-flash-video",
|
"video/x-flash-video",
|
||||||
&[("flvversion", 1i32.into())],
|
&[("flvversion", &1i32)],
|
||||||
)),
|
)),
|
||||||
flavors::CodecId::SCREEN => Some(Caps::new_simple("video/x-flash-screen", &[])),
|
flavors::CodecId::SCREEN => Some(gst::Caps::new_simple("video/x-flash-screen", &[])),
|
||||||
flavors::CodecId::VP6 => Some(Caps::new_simple("video/x-vp6-flash", &[])),
|
flavors::CodecId::VP6 => Some(gst::Caps::new_simple("video/x-vp6-flash", &[])),
|
||||||
flavors::CodecId::VP6A => Some(Caps::new_simple("video/x-vp6-flash-alpha", &[])),
|
flavors::CodecId::VP6A => Some(gst::Caps::new_simple("video/x-vp6-flash-alpha", &[])),
|
||||||
flavors::CodecId::SCREEN2 => Some(Caps::new_simple("video/x-flash-screen2", &[])),
|
flavors::CodecId::SCREEN2 => Some(gst::Caps::new_simple("video/x-flash-screen2", &[])),
|
||||||
flavors::CodecId::H264 => self.avc_sequence_header.as_ref().map(|header| {
|
flavors::CodecId::H264 => self.avc_sequence_header.as_ref().map(|header| {
|
||||||
Caps::new_simple(
|
gst::Caps::new_simple(
|
||||||
"video/x-h264",
|
"video/x-h264",
|
||||||
&[
|
&[("stream-format", &"avc"), ("codec_data", &header)],
|
||||||
("stream-format", "avc".into()),
|
|
||||||
("codec_data", header.as_ref().into()),
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
flavors::CodecId::H263 => Some(Caps::new_simple("video/x-h263", &[])),
|
flavors::CodecId::H263 => Some(gst::Caps::new_simple("video/x-h263", &[])),
|
||||||
flavors::CodecId::MPEG4Part2 => Some(Caps::new_simple(
|
flavors::CodecId::MPEG4Part2 => Some(gst::Caps::new_simple(
|
||||||
"video/x-h263",
|
"video/x-h263",
|
||||||
&[("mpegversion", 4i32.into()), ("systemstream", false.into())],
|
&[("mpegversion", &4i32), ("systemstream", &false)],
|
||||||
)),
|
)),
|
||||||
flavors::CodecId::JPEG => {
|
flavors::CodecId::JPEG => {
|
||||||
// Unused according to spec
|
// Unused according to spec
|
||||||
|
@ -360,19 +339,21 @@ impl VideoFormat {
|
||||||
|
|
||||||
if let (Some(width), Some(height)) = (self.width, self.height) {
|
if let (Some(width), Some(height)) = (self.width, self.height) {
|
||||||
caps.as_mut().map(|c| {
|
caps.as_mut().map(|c| {
|
||||||
c.get_mut().unwrap().set_simple(&[
|
c.get_mut()
|
||||||
("width", (width as i32).into()),
|
.unwrap()
|
||||||
("height", (height as i32).into()),
|
.set_simple(&[("width", &(width as i32)), ("height", &(height as i32))])
|
||||||
])
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(par) = self.pixel_aspect_ratio {
|
if let Some(par) = self.pixel_aspect_ratio {
|
||||||
if *par.numer() != 0 && par.numer() != par.denom() {
|
if *par.numer() != 0 && par.numer() != par.denom() {
|
||||||
caps.as_mut().map(|c| {
|
caps.as_mut().map(|c| {
|
||||||
c.get_mut()
|
c.get_mut().unwrap().set_simple(&[
|
||||||
.unwrap()
|
(
|
||||||
.set_simple(&[("pixel-aspect-ratio", par.into())])
|
"pixel-aspect-ratio",
|
||||||
|
&gst::Fraction::new(*par.numer(), *par.denom()),
|
||||||
|
),
|
||||||
|
])
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -380,9 +361,9 @@ impl VideoFormat {
|
||||||
if let Some(fps) = self.framerate {
|
if let Some(fps) = self.framerate {
|
||||||
if *fps.numer() != 0 {
|
if *fps.numer() != 0 {
|
||||||
caps.as_mut().map(|c| {
|
caps.as_mut().map(|c| {
|
||||||
c.get_mut()
|
c.get_mut().unwrap().set_simple(&[
|
||||||
.unwrap()
|
("framerate", &gst::Fraction::new(*fps.numer(), *fps.denom())),
|
||||||
.set_simple(&[("framerate", fps.into())])
|
])
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -475,7 +456,7 @@ impl Metadata {
|
||||||
metadata.video_height = Some(height as u32);
|
metadata.video_height = Some(height as u32);
|
||||||
}
|
}
|
||||||
("framerate", &flavors::ScriptDataValue::Number(framerate)) if framerate >= 0.0 => {
|
("framerate", &flavors::ScriptDataValue::Number(framerate)) if framerate >= 0.0 => {
|
||||||
if let Some(framerate) = utils::f64_to_fraction(framerate) {
|
if let Some(framerate) = Rational32::approximate_float(framerate) {
|
||||||
metadata.video_framerate = Some(framerate);
|
metadata.video_framerate = Some(framerate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -500,9 +481,8 @@ impl Metadata {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct FlvDemux {
|
pub struct FlvDemux {
|
||||||
logger: Logger,
|
cat: gst::DebugCategory,
|
||||||
state: State,
|
state: State,
|
||||||
adapter: Adapter,
|
adapter: Adapter,
|
||||||
// Only in >= State::Streaming
|
// Only in >= State::Streaming
|
||||||
|
@ -510,11 +490,12 @@ pub struct FlvDemux {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FlvDemux {
|
impl FlvDemux {
|
||||||
pub fn new(element: Element) -> FlvDemux {
|
pub fn new(_element: &gst::Element) -> FlvDemux {
|
||||||
FlvDemux {
|
FlvDemux {
|
||||||
logger: Logger::root(
|
cat: gst::DebugCategory::new(
|
||||||
GstDebugDrain::new(Some(&element), "rsflvdemux", 0, "Rust FLV demuxer"),
|
"rsflvdemux",
|
||||||
o!(),
|
gst::DebugColorFlags::empty(),
|
||||||
|
"Rust FLV demuxer",
|
||||||
),
|
),
|
||||||
state: State::Stopped,
|
state: State::Stopped,
|
||||||
adapter: Adapter::new(),
|
adapter: Adapter::new(),
|
||||||
|
@ -522,7 +503,7 @@ impl FlvDemux {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_boxed(element: Element) -> Box<Demuxer> {
|
pub fn new_boxed(element: &gst::Element) -> Box<Demuxer> {
|
||||||
Box::new(FlvDemux::new(element))
|
Box::new(FlvDemux::new(element))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -539,15 +520,23 @@ impl FlvDemux {
|
||||||
let buffer = self.adapter
|
let buffer = self.adapter
|
||||||
.get_buffer(tag_header.data_size as usize)
|
.get_buffer(tag_header.data_size as usize)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let map = buffer.map_read().unwrap();
|
let map = buffer.map_readable().unwrap();
|
||||||
let data = map.as_slice();
|
let data = map.as_slice();
|
||||||
|
|
||||||
match flavors::script_data(data) {
|
match flavors::script_data(data) {
|
||||||
IResult::Done(_, ref script_data) if script_data.name == "onMetaData" => {
|
IResult::Done(_, ref script_data) if script_data.name == "onMetaData" => {
|
||||||
trace!(self.logger, "Got script tag: {:?}", script_data);
|
gst_trace!(
|
||||||
|
self.cat,
|
||||||
|
/* TODO: obj: demuxer, */ "Got script tag: {:?}",
|
||||||
|
script_data
|
||||||
|
);
|
||||||
|
|
||||||
let metadata = Metadata::new(script_data);
|
let metadata = Metadata::new(script_data);
|
||||||
debug!(self.logger, "Got metadata: {:?}", metadata);
|
gst_debug!(
|
||||||
|
self.cat,
|
||||||
|
/* TODO: obj: demuxer, */ "Got metadata: {:?}",
|
||||||
|
metadata
|
||||||
|
);
|
||||||
|
|
||||||
let streaming_state = self.streaming_state.as_mut().unwrap();
|
let streaming_state = self.streaming_state.as_mut().unwrap();
|
||||||
|
|
||||||
|
@ -583,7 +572,11 @@ impl FlvDemux {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IResult::Done(_, ref script_data) => {
|
IResult::Done(_, ref script_data) => {
|
||||||
trace!(self.logger, "Got script tag: {:?}", script_data);
|
gst_trace!(
|
||||||
|
self.cat,
|
||||||
|
/* TODO: obj: demuxer, */ "Got script tag: {:?}",
|
||||||
|
script_data
|
||||||
|
);
|
||||||
}
|
}
|
||||||
IResult::Error(_) | IResult::Incomplete(_) => {
|
IResult::Error(_) | IResult::Incomplete(_) => {
|
||||||
// ignore
|
// ignore
|
||||||
|
@ -597,8 +590,11 @@ impl FlvDemux {
|
||||||
&mut self,
|
&mut self,
|
||||||
data_header: &flavors::AudioDataHeader,
|
data_header: &flavors::AudioDataHeader,
|
||||||
) -> Result<HandleBufferResult, FlowError> {
|
) -> Result<HandleBufferResult, FlowError> {
|
||||||
let logger = self.logger.clone();
|
gst_trace!(
|
||||||
trace!(logger, "Got audio data header: {:?}", data_header);
|
self.cat,
|
||||||
|
/* TODO: obj: demuxer, */ "Got audio data header: {:?}",
|
||||||
|
data_header
|
||||||
|
);
|
||||||
|
|
||||||
let streaming_state = self.streaming_state.as_mut().unwrap();
|
let streaming_state = self.streaming_state.as_mut().unwrap();
|
||||||
|
|
||||||
|
@ -609,7 +605,11 @@ impl FlvDemux {
|
||||||
);
|
);
|
||||||
|
|
||||||
if streaming_state.audio.as_ref() != Some(&new_audio_format) {
|
if streaming_state.audio.as_ref() != Some(&new_audio_format) {
|
||||||
debug!(logger, "Got new audio format: {:?}", new_audio_format);
|
gst_debug!(
|
||||||
|
self.cat,
|
||||||
|
/* TODO: obj: demuxer, */ "Got new audio format: {:?}",
|
||||||
|
new_audio_format
|
||||||
|
);
|
||||||
let new_stream = streaming_state.audio == None;
|
let new_stream = streaming_state.audio == None;
|
||||||
|
|
||||||
let caps = new_audio_format.to_caps();
|
let caps = new_audio_format.to_caps();
|
||||||
|
@ -659,8 +659,8 @@ impl FlvDemux {
|
||||||
self.adapter
|
self.adapter
|
||||||
.flush(15 + tag_header.data_size as usize)
|
.flush(15 + tag_header.data_size as usize)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
warn!(
|
gst_warning!(
|
||||||
self.logger,
|
self.cat, /* TODO: obj: demuxer, */
|
||||||
"Too small packet for AAC packet header {}",
|
"Too small packet for AAC packet header {}",
|
||||||
15 + tag_header.data_size
|
15 + tag_header.data_size
|
||||||
);
|
);
|
||||||
|
@ -674,15 +674,19 @@ impl FlvDemux {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
IResult::Done(_, header) => {
|
IResult::Done(_, header) => {
|
||||||
trace!(self.logger, "Got AAC packet header {:?}", header);
|
gst_trace!(
|
||||||
|
self.cat,
|
||||||
|
/* TODO: obj: demuxer, */ "Got AAC packet header {:?}",
|
||||||
|
header
|
||||||
|
);
|
||||||
match header.packet_type {
|
match header.packet_type {
|
||||||
flavors::AACPacketType::SequenceHeader => {
|
flavors::AACPacketType::SequenceHeader => {
|
||||||
self.adapter.flush(15 + 1 + 1).unwrap();
|
self.adapter.flush(15 + 1 + 1).unwrap();
|
||||||
let buffer = self.adapter
|
let buffer = self.adapter
|
||||||
.get_buffer((tag_header.data_size - 1 - 1) as usize)
|
.get_buffer((tag_header.data_size - 1 - 1) as usize)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
debug!(
|
gst_debug!(
|
||||||
self.logger,
|
self.cat, /* TODO: obj: demuxer, */
|
||||||
"Got AAC sequence header {:?} of size {}",
|
"Got AAC sequence header {:?} of size {}",
|
||||||
buffer,
|
buffer,
|
||||||
tag_header.data_size - 1 - 1
|
tag_header.data_size - 1 - 1
|
||||||
|
@ -738,11 +742,15 @@ impl FlvDemux {
|
||||||
|
|
||||||
{
|
{
|
||||||
let buffer = buffer.get_mut().unwrap();
|
let buffer = buffer.get_mut().unwrap();
|
||||||
buffer.set_pts((tag_header.timestamp as u64).mul_div_floor(1000_000, 1));
|
buffer.set_pts(
|
||||||
|
(tag_header.timestamp as u64)
|
||||||
|
.mul_div_floor(1000_000, 1)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
trace!(
|
gst_trace!(
|
||||||
self.logger,
|
self.cat, /* TODO: obj: demuxer, */
|
||||||
"Outputting audio buffer {:?} for tag {:?} of size {}",
|
"Outputting audio buffer {:?} for tag {:?} of size {}",
|
||||||
buffer,
|
buffer,
|
||||||
tag_header,
|
tag_header,
|
||||||
|
@ -756,8 +764,11 @@ impl FlvDemux {
|
||||||
&mut self,
|
&mut self,
|
||||||
data_header: &flavors::VideoDataHeader,
|
data_header: &flavors::VideoDataHeader,
|
||||||
) -> Result<HandleBufferResult, FlowError> {
|
) -> Result<HandleBufferResult, FlowError> {
|
||||||
let logger = self.logger.clone();
|
gst_trace!(
|
||||||
trace!(logger, "Got video data header: {:?}", data_header);
|
self.cat,
|
||||||
|
/* TODO: obj: demuxer, */ "Got video data header: {:?}",
|
||||||
|
data_header
|
||||||
|
);
|
||||||
|
|
||||||
let streaming_state = self.streaming_state.as_mut().unwrap();
|
let streaming_state = self.streaming_state.as_mut().unwrap();
|
||||||
|
|
||||||
|
@ -768,7 +779,11 @@ impl FlvDemux {
|
||||||
);
|
);
|
||||||
|
|
||||||
if streaming_state.video.as_ref() != Some(&new_video_format) {
|
if streaming_state.video.as_ref() != Some(&new_video_format) {
|
||||||
debug!(logger, "Got new video format: {:?}", new_video_format);
|
gst_debug!(
|
||||||
|
self.cat,
|
||||||
|
/* TODO: obj: demuxer, */ "Got new video format: {:?}",
|
||||||
|
new_video_format
|
||||||
|
);
|
||||||
|
|
||||||
let new_stream = streaming_state.video == None;
|
let new_stream = streaming_state.video == None;
|
||||||
|
|
||||||
|
@ -821,8 +836,8 @@ impl FlvDemux {
|
||||||
self.adapter
|
self.adapter
|
||||||
.flush(15 + tag_header.data_size as usize)
|
.flush(15 + tag_header.data_size as usize)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
warn!(
|
gst_warning!(
|
||||||
self.logger,
|
self.cat, /* TODO: obj: demuxer, */
|
||||||
"Too small packet for AVC packet header {}",
|
"Too small packet for AVC packet header {}",
|
||||||
15 + tag_header.data_size
|
15 + tag_header.data_size
|
||||||
);
|
);
|
||||||
|
@ -836,15 +851,19 @@ impl FlvDemux {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
IResult::Done(_, header) => {
|
IResult::Done(_, header) => {
|
||||||
trace!(self.logger, "Got AVC packet header {:?}", header);
|
gst_trace!(
|
||||||
|
self.cat,
|
||||||
|
/* TODO: obj: demuxer, */ "Got AVC packet header {:?}",
|
||||||
|
header
|
||||||
|
);
|
||||||
match header.packet_type {
|
match header.packet_type {
|
||||||
flavors::AVCPacketType::SequenceHeader => {
|
flavors::AVCPacketType::SequenceHeader => {
|
||||||
self.adapter.flush(15 + 1 + 4).unwrap();
|
self.adapter.flush(15 + 1 + 4).unwrap();
|
||||||
let buffer = self.adapter
|
let buffer = self.adapter
|
||||||
.get_buffer((tag_header.data_size - 1 - 4) as usize)
|
.get_buffer((tag_header.data_size - 1 - 4) as usize)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
debug!(
|
gst_debug!(
|
||||||
self.logger,
|
self.cat, /* TODO: obj: demuxer, */
|
||||||
"Got AVC sequence header {:?} of size {}",
|
"Got AVC sequence header {:?} of size {}",
|
||||||
buffer,
|
buffer,
|
||||||
tag_header.data_size - 1 - 4
|
tag_header.data_size - 1 - 4
|
||||||
|
@ -911,9 +930,13 @@ impl FlvDemux {
|
||||||
{
|
{
|
||||||
let buffer = buffer.get_mut().unwrap();
|
let buffer = buffer.get_mut().unwrap();
|
||||||
if !is_keyframe {
|
if !is_keyframe {
|
||||||
buffer.set_flags(BUFFER_FLAG_DELTA_UNIT);
|
buffer.set_flags(gst::BUFFER_FLAG_DELTA_UNIT);
|
||||||
}
|
}
|
||||||
buffer.set_dts((tag_header.timestamp as u64).mul_div_floor(1000_000, 1));
|
buffer.set_dts(
|
||||||
|
(tag_header.timestamp as u64)
|
||||||
|
.mul_div_floor(1000_000, 1)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
// Prevent negative numbers
|
// Prevent negative numbers
|
||||||
let pts = if cts < 0 && tag_header.timestamp < (-cts) as u32 {
|
let pts = if cts < 0 && tag_header.timestamp < (-cts) as u32 {
|
||||||
|
@ -921,11 +944,11 @@ impl FlvDemux {
|
||||||
} else {
|
} else {
|
||||||
((tag_header.timestamp as i64) + (cts as i64)) as u64
|
((tag_header.timestamp as i64) + (cts as i64)) as u64
|
||||||
};
|
};
|
||||||
buffer.set_pts(pts.mul_div_floor(1000_000, 1));
|
buffer.set_pts(pts.mul_div_floor(1000_000, 1).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
trace!(
|
gst_trace!(
|
||||||
self.logger,
|
self.cat, /* TODO: obj: demuxer, */
|
||||||
"Outputting video buffer {:?} for tag {:?} of size {}, keyframe: {}",
|
"Outputting video buffer {:?} for tag {:?} of size {}, keyframe: {}",
|
||||||
buffer,
|
buffer,
|
||||||
tag_header,
|
tag_header,
|
||||||
|
@ -949,7 +972,11 @@ impl FlvDemux {
|
||||||
// fall through
|
// fall through
|
||||||
}
|
}
|
||||||
IResult::Done(_, ref header) => {
|
IResult::Done(_, ref header) => {
|
||||||
debug!(self.logger, "Found FLV header: {:?}", header);
|
gst_debug!(
|
||||||
|
self.cat,
|
||||||
|
/* TODO: obj: demuxer, */ "Found FLV header: {:?}",
|
||||||
|
header
|
||||||
|
);
|
||||||
|
|
||||||
let skip = if header.offset < 9 {
|
let skip = if header.offset < 9 {
|
||||||
0
|
0
|
||||||
|
@ -1006,7 +1033,11 @@ impl FlvDemux {
|
||||||
unimplemented!();
|
unimplemented!();
|
||||||
}
|
}
|
||||||
IResult::Done(_, previous_size) => {
|
IResult::Done(_, previous_size) => {
|
||||||
trace!(self.logger, "Previous tag size {}", previous_size);
|
gst_trace!(
|
||||||
|
self.cat,
|
||||||
|
/* TODO: obj: demuxer, */ "Previous tag size {}",
|
||||||
|
previous_size
|
||||||
|
);
|
||||||
// Nothing to do here, we just consume it for now
|
// Nothing to do here, we just consume it for now
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1020,12 +1051,12 @@ impl FlvDemux {
|
||||||
|
|
||||||
let res = match tag_header.tag_type {
|
let res = match tag_header.tag_type {
|
||||||
flavors::TagType::Script => {
|
flavors::TagType::Script => {
|
||||||
trace!(self.logger, "Found script tag");
|
gst_trace!(self.cat, /* TODO: obj: demuxer, */ "Found script tag");
|
||||||
|
|
||||||
self.handle_script_tag(&tag_header)
|
self.handle_script_tag(&tag_header)
|
||||||
}
|
}
|
||||||
flavors::TagType::Audio => {
|
flavors::TagType::Audio => {
|
||||||
trace!(self.logger, "Found audio tag");
|
gst_trace!(self.cat, /* TODO: obj: demuxer, */ "Found audio tag");
|
||||||
|
|
||||||
let data_header = match flavors::audio_data_header(&data[15..]) {
|
let data_header = match flavors::audio_data_header(&data[15..]) {
|
||||||
IResult::Error(_) | IResult::Incomplete(_) => {
|
IResult::Error(_) | IResult::Incomplete(_) => {
|
||||||
|
@ -1037,7 +1068,7 @@ impl FlvDemux {
|
||||||
self.handle_audio_tag(&tag_header, &data_header)
|
self.handle_audio_tag(&tag_header, &data_header)
|
||||||
}
|
}
|
||||||
flavors::TagType::Video => {
|
flavors::TagType::Video => {
|
||||||
trace!(self.logger, "Found video tag");
|
gst_trace!(self.cat, /* TODO: obj: demuxer, */ "Found video tag");
|
||||||
|
|
||||||
let data_header = match flavors::video_data_header(&data[15..]) {
|
let data_header = match flavors::video_data_header(&data[15..]) {
|
||||||
IResult::Error(_) | IResult::Incomplete(_) => {
|
IResult::Error(_) | IResult::Incomplete(_) => {
|
||||||
|
@ -1053,12 +1084,14 @@ impl FlvDemux {
|
||||||
if let Ok(HandleBufferResult::BufferForStream(_, ref buffer)) = res {
|
if let Ok(HandleBufferResult::BufferForStream(_, ref buffer)) = res {
|
||||||
let streaming_state = self.streaming_state.as_mut().unwrap();
|
let streaming_state = self.streaming_state.as_mut().unwrap();
|
||||||
|
|
||||||
if let Some(pts) = buffer.get_pts() {
|
if buffer.get_pts() != gst::CLOCK_TIME_NONE {
|
||||||
|
let pts = buffer.get_pts();
|
||||||
streaming_state.last_position = streaming_state
|
streaming_state.last_position = streaming_state
|
||||||
.last_position
|
.last_position
|
||||||
.map(|last| cmp::max(last, pts))
|
.map(|last| cmp::max(last, pts))
|
||||||
.or_else(|| Some(pts));
|
.or_else(|| Some(pts));
|
||||||
} else if let Some(dts) = buffer.get_dts() {
|
} else if buffer.get_dts() != gst::CLOCK_TIME_NONE {
|
||||||
|
let dts = buffer.get_dts();
|
||||||
streaming_state.last_position = streaming_state
|
streaming_state.last_position = streaming_state
|
||||||
.last_position
|
.last_position
|
||||||
.map(|last| cmp::max(last, dts))
|
.map(|last| cmp::max(last, dts))
|
||||||
|
@ -1097,7 +1130,7 @@ impl Demuxer for FlvDemux {
|
||||||
|
|
||||||
fn handle_buffer(
|
fn handle_buffer(
|
||||||
&mut self,
|
&mut self,
|
||||||
buffer: Option<GstRc<Buffer>>,
|
buffer: Option<gst::Buffer>,
|
||||||
) -> Result<HandleBufferResult, FlowError> {
|
) -> Result<HandleBufferResult, FlowError> {
|
||||||
if let Some(buffer) = buffer {
|
if let Some(buffer) = buffer {
|
||||||
self.adapter.push(buffer);
|
self.adapter.push(buffer);
|
||||||
|
|
|
@ -11,22 +11,21 @@
|
||||||
extern crate flavors;
|
extern crate flavors;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate gst_plugin;
|
extern crate gst_plugin;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate gstreamer as gst;
|
||||||
extern crate muldiv;
|
extern crate muldiv;
|
||||||
#[macro_use]
|
|
||||||
extern crate nom;
|
extern crate nom;
|
||||||
#[macro_use]
|
extern crate num_rational;
|
||||||
extern crate slog;
|
|
||||||
extern crate url;
|
extern crate url;
|
||||||
|
|
||||||
use gst_plugin::plugin::*;
|
|
||||||
use gst_plugin::demuxer::*;
|
use gst_plugin::demuxer::*;
|
||||||
use gst_plugin::caps::*;
|
use gst::prelude::*;
|
||||||
|
|
||||||
mod flvdemux;
|
mod flvdemux;
|
||||||
|
|
||||||
use flvdemux::FlvDemux;
|
use flvdemux::FlvDemux;
|
||||||
|
|
||||||
fn plugin_init(plugin: &Plugin) -> bool {
|
fn plugin_init(plugin: &gst::Plugin) -> bool {
|
||||||
demuxer_register(
|
demuxer_register(
|
||||||
plugin,
|
plugin,
|
||||||
&DemuxerInfo {
|
&DemuxerInfo {
|
||||||
|
@ -37,8 +36,8 @@ fn plugin_init(plugin: &Plugin) -> bool {
|
||||||
author: "Sebastian Dröge <sebastian@centricular.com>",
|
author: "Sebastian Dröge <sebastian@centricular.com>",
|
||||||
rank: 256 + 100,
|
rank: 256 + 100,
|
||||||
create_instance: FlvDemux::new_boxed,
|
create_instance: FlvDemux::new_boxed,
|
||||||
input_caps: &Caps::new_simple("video/x-flv", &[]),
|
input_caps: &gst::Caps::new_simple("video/x-flv", &[]),
|
||||||
output_caps: &Caps::new_any(),
|
output_caps: &gst::Caps::new_any(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ license = "MIT/Apache-2.0"
|
||||||
url = "1.1"
|
url = "1.1"
|
||||||
gst-plugin = { path="../gst-plugin" }
|
gst-plugin = { path="../gst-plugin" }
|
||||||
reqwest = "0.7"
|
reqwest = "0.7"
|
||||||
slog = "2.0"
|
gstreamer = { git = "https://github.com/sdroege/gstreamer-rs", features = ["v1_10"] }
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "gstrshttp"
|
name = "gstrshttp"
|
||||||
|
|
|
@ -15,11 +15,9 @@ use reqwest::header::{AcceptRanges, ByteRangeSpec, ContentLength, ContentRange,
|
||||||
|
|
||||||
use gst_plugin::error::*;
|
use gst_plugin::error::*;
|
||||||
use gst_plugin::source::*;
|
use gst_plugin::source::*;
|
||||||
use gst_plugin::buffer::*;
|
|
||||||
use gst_plugin::utils::*;
|
|
||||||
use gst_plugin::log::*;
|
|
||||||
|
|
||||||
use slog::Logger;
|
use gst;
|
||||||
|
use gst::prelude::*;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum StreamingState {
|
enum StreamingState {
|
||||||
|
@ -38,32 +36,35 @@ enum StreamingState {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct HttpSrc {
|
pub struct HttpSrc {
|
||||||
streaming_state: StreamingState,
|
streaming_state: StreamingState,
|
||||||
logger: Logger,
|
cat: gst::DebugCategory,
|
||||||
client: Client,
|
client: Client,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HttpSrc {
|
impl HttpSrc {
|
||||||
pub fn new(element: Element) -> HttpSrc {
|
pub fn new(_src: &RsSrcWrapper) -> HttpSrc {
|
||||||
HttpSrc {
|
HttpSrc {
|
||||||
streaming_state: StreamingState::Stopped,
|
streaming_state: StreamingState::Stopped,
|
||||||
logger: Logger::root(
|
cat: gst::DebugCategory::new(
|
||||||
GstDebugDrain::new(Some(&element), "rshttpsink", 0, "Rust http sink"),
|
"rshttpsrc",
|
||||||
o!(),
|
gst::DebugColorFlags::empty(),
|
||||||
|
"Rust HTTP source",
|
||||||
),
|
),
|
||||||
client: Client::new().unwrap(),
|
client: Client::new().unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_boxed(element: Element) -> Box<Source> {
|
pub fn new_boxed(src: &RsSrcWrapper) -> Box<Source> {
|
||||||
Box::new(HttpSrc::new(element))
|
Box::new(HttpSrc::new(src))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_request(
|
fn do_request(
|
||||||
&self,
|
&self,
|
||||||
|
src: &RsSrcWrapper,
|
||||||
uri: Url,
|
uri: Url,
|
||||||
start: u64,
|
start: u64,
|
||||||
stop: Option<u64>,
|
stop: Option<u64>,
|
||||||
) -> Result<StreamingState, ErrorMessage> {
|
) -> Result<StreamingState, ErrorMessage> {
|
||||||
|
let cat = self.cat;
|
||||||
let mut req = self.client.get(uri.clone()).unwrap();
|
let mut req = self.client.get(uri.clone()).unwrap();
|
||||||
|
|
||||||
match (start != 0, stop) {
|
match (start != 0, stop) {
|
||||||
|
@ -76,20 +77,20 @@ impl HttpSrc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!(self.logger, "Doing new request {:?}", req);
|
gst_debug!(cat, obj: src, "Doing new request {:?}", req);
|
||||||
|
|
||||||
let response = try!(req.send().or_else(|err| {
|
let response = try!(req.send().or_else(|err| {
|
||||||
error!(self.logger, "Request failed: {:?}", err);
|
gst_error!(cat, obj: src, "Request failed: {:?}", err);
|
||||||
Err(error_msg!(
|
Err(error_msg!(
|
||||||
SourceError::ReadFailed,
|
gst::ResourceError::Read,
|
||||||
["Failed to fetch {}: {}", uri, err.to_string()]
|
["Failed to fetch {}: {}", uri, err.to_string()]
|
||||||
))
|
))
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if !response.status().is_success() {
|
if !response.status().is_success() {
|
||||||
error!(self.logger, "Request status failed: {:?}", response);
|
gst_error!(cat, obj: src, "Request status failed: {:?}", response);
|
||||||
return Err(error_msg!(
|
return Err(error_msg!(
|
||||||
SourceError::ReadFailed,
|
gst::ResourceError::Read,
|
||||||
["Failed to fetch {}: {}", uri, response.status()]
|
["Failed to fetch {}: {}", uri, response.status()]
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -121,12 +122,12 @@ impl HttpSrc {
|
||||||
|
|
||||||
if position != start {
|
if position != start {
|
||||||
return Err(error_msg!(
|
return Err(error_msg!(
|
||||||
SourceError::SeekFailed,
|
gst::ResourceError::Seek,
|
||||||
["Failed to seek to {}: Got {}", start, position]
|
["Failed to seek to {}: Got {}", start, position]
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!(self.logger, "Request successful: {:?}", response);
|
gst_debug!(cat, obj: src, "Request successful: {:?}", response);
|
||||||
|
|
||||||
Ok(StreamingState::Started {
|
Ok(StreamingState::Started {
|
||||||
uri: uri,
|
uri: uri,
|
||||||
|
@ -143,8 +144,8 @@ impl HttpSrc {
|
||||||
fn validate_uri(uri: &Url) -> Result<(), UriError> {
|
fn validate_uri(uri: &Url) -> Result<(), UriError> {
|
||||||
if uri.scheme() != "http" && uri.scheme() != "https" {
|
if uri.scheme() != "http" && uri.scheme() != "https" {
|
||||||
return Err(UriError::new(
|
return Err(UriError::new(
|
||||||
UriErrorKind::UnsupportedProtocol,
|
gst::URIError::UnsupportedProtocol,
|
||||||
Some(format!("Unsupported URI '{}'", uri.as_str())),
|
format!("Unsupported URI '{}'", uri.as_str()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,34 +157,39 @@ impl Source for HttpSrc {
|
||||||
Box::new(validate_uri)
|
Box::new(validate_uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_seekable(&self) -> bool {
|
fn is_seekable(&self, _src: &RsSrcWrapper) -> bool {
|
||||||
match self.streaming_state {
|
match self.streaming_state {
|
||||||
StreamingState::Started { seekable, .. } => seekable,
|
StreamingState::Started { seekable, .. } => seekable,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_size(&self) -> Option<u64> {
|
fn get_size(&self, _src: &RsSrcWrapper) -> Option<u64> {
|
||||||
match self.streaming_state {
|
match self.streaming_state {
|
||||||
StreamingState::Started { size, .. } => size,
|
StreamingState::Started { size, .. } => size,
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start(&mut self, uri: Url) -> Result<(), ErrorMessage> {
|
fn start(&mut self, src: &RsSrcWrapper, uri: Url) -> Result<(), ErrorMessage> {
|
||||||
self.streaming_state = StreamingState::Stopped;
|
self.streaming_state = StreamingState::Stopped;
|
||||||
self.streaming_state = try!(self.do_request(uri, 0, None));
|
self.streaming_state = try!(self.do_request(src, uri, 0, None));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop(&mut self) -> Result<(), ErrorMessage> {
|
fn stop(&mut self, _src: &RsSrcWrapper) -> Result<(), ErrorMessage> {
|
||||||
self.streaming_state = StreamingState::Stopped;
|
self.streaming_state = StreamingState::Stopped;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn seek(&mut self, start: u64, stop: Option<u64>) -> Result<(), ErrorMessage> {
|
fn seek(
|
||||||
|
&mut self,
|
||||||
|
src: &RsSrcWrapper,
|
||||||
|
start: u64,
|
||||||
|
stop: Option<u64>,
|
||||||
|
) -> Result<(), ErrorMessage> {
|
||||||
let (position, old_stop, uri) = match self.streaming_state {
|
let (position, old_stop, uri) = match self.streaming_state {
|
||||||
StreamingState::Started {
|
StreamingState::Started {
|
||||||
position,
|
position,
|
||||||
|
@ -192,7 +198,7 @@ impl Source for HttpSrc {
|
||||||
..
|
..
|
||||||
} => (position, stop, uri.clone()),
|
} => (position, stop, uri.clone()),
|
||||||
StreamingState::Stopped => {
|
StreamingState::Stopped => {
|
||||||
return Err(error_msg!(SourceError::Failure, ["Not started yet"]));
|
return Err(error_msg!(gst::LibraryError::Failed, ["Not started yet"]));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -201,13 +207,19 @@ impl Source for HttpSrc {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.streaming_state = StreamingState::Stopped;
|
self.streaming_state = StreamingState::Stopped;
|
||||||
self.streaming_state = try!(self.do_request(uri, start, stop));
|
self.streaming_state = try!(self.do_request(src, uri, start, stop));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fill(&mut self, offset: u64, _: u32, buffer: &mut Buffer) -> Result<(), FlowError> {
|
fn fill(
|
||||||
let logger = self.logger.clone();
|
&mut self,
|
||||||
|
src: &RsSrcWrapper,
|
||||||
|
offset: u64,
|
||||||
|
_: u32,
|
||||||
|
buffer: &mut gst::BufferRef,
|
||||||
|
) -> Result<(), FlowError> {
|
||||||
|
let cat = self.cat;
|
||||||
|
|
||||||
let (response, position) = match self.streaming_state {
|
let (response, position) = match self.streaming_state {
|
||||||
StreamingState::Started {
|
StreamingState::Started {
|
||||||
|
@ -217,24 +229,25 @@ impl Source for HttpSrc {
|
||||||
} => (response, position),
|
} => (response, position),
|
||||||
StreamingState::Stopped => {
|
StreamingState::Stopped => {
|
||||||
return Err(FlowError::Error(
|
return Err(FlowError::Error(
|
||||||
error_msg!(SourceError::Failure, ["Not started yet"]),
|
error_msg!(gst::LibraryError::Failed, ["Not started yet"]),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if *position != offset {
|
if *position != offset {
|
||||||
return Err(FlowError::Error(error_msg!(
|
return Err(FlowError::Error(error_msg!(
|
||||||
SourceError::SeekFailed,
|
gst::ResourceError::Seek,
|
||||||
["Got unexpected offset {}, expected {}", offset, position]
|
["Got unexpected offset {}, expected {}", offset, position]
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let size = {
|
let size = {
|
||||||
let mut map = match buffer.map_readwrite() {
|
let mut map = match buffer.map_writable() {
|
||||||
None => {
|
None => {
|
||||||
return Err(FlowError::Error(
|
return Err(FlowError::Error(error_msg!(
|
||||||
error_msg!(SourceError::Failure, ["Failed to map buffer"]),
|
gst::LibraryError::Failed,
|
||||||
));
|
["Failed to map buffer"]
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
Some(map) => map,
|
Some(map) => map,
|
||||||
};
|
};
|
||||||
|
@ -242,9 +255,9 @@ impl Source for HttpSrc {
|
||||||
let data = map.as_mut_slice();
|
let data = map.as_mut_slice();
|
||||||
|
|
||||||
try!(response.read(data).or_else(|err| {
|
try!(response.read(data).or_else(|err| {
|
||||||
error!(logger, "Failed to read: {:?}", err);
|
gst_error!(cat, obj: src, "Failed to read: {:?}", err);
|
||||||
Err(FlowError::Error(error_msg!(
|
Err(FlowError::Error(error_msg!(
|
||||||
SourceError::ReadFailed,
|
gst::ResourceError::Read,
|
||||||
["Failed to read at {}: {}", offset, err.to_string()]
|
["Failed to read at {}: {}", offset, err.to_string()]
|
||||||
)))
|
)))
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -10,19 +10,18 @@
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate gst_plugin;
|
extern crate gst_plugin;
|
||||||
extern crate reqwest;
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate slog;
|
extern crate gstreamer as gst;
|
||||||
|
extern crate reqwest;
|
||||||
extern crate url;
|
extern crate url;
|
||||||
|
|
||||||
use gst_plugin::plugin::*;
|
|
||||||
use gst_plugin::source::*;
|
use gst_plugin::source::*;
|
||||||
|
|
||||||
mod httpsrc;
|
mod httpsrc;
|
||||||
|
|
||||||
use httpsrc::HttpSrc;
|
use httpsrc::HttpSrc;
|
||||||
|
|
||||||
fn plugin_init(plugin: &Plugin) -> bool {
|
fn plugin_init(plugin: &gst::Plugin) -> bool {
|
||||||
source_register(
|
source_register(
|
||||||
plugin,
|
plugin,
|
||||||
SourceInfo {
|
SourceInfo {
|
||||||
|
|
|
@ -9,18 +9,15 @@ license = "MIT/Apache-2.0"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
url = "1.1"
|
url = "1.1"
|
||||||
bitflags = "0.9"
|
|
||||||
slog = { version = "2.0", features = ["max_level_trace"] }
|
|
||||||
lazy_static = "0.2"
|
lazy_static = "0.2"
|
||||||
byteorder = "1.0"
|
byteorder = "1.0"
|
||||||
num-rational = { version = "0.1", default-features = false, features = [] }
|
|
||||||
derivative = "1.0"
|
|
||||||
glib-sys = { git = "https://github.com/gtk-rs/sys" }
|
glib-sys = { git = "https://github.com/gtk-rs/sys" }
|
||||||
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
|
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
|
||||||
gstreamer-sys = { git = "https://github.com/sdroege/gstreamer-sys", features = ["v1_10"] }
|
gstreamer-sys = { git = "https://github.com/sdroege/gstreamer-sys", features = ["v1_10"] }
|
||||||
gstreamer-base-sys = { git = "https://github.com/sdroege/gstreamer-sys", features = ["v1_10"] }
|
gstreamer-base-sys = { git = "https://github.com/sdroege/gstreamer-sys", features = ["v1_10"] }
|
||||||
glib = { git = "https://github.com/gtk-rs/glib" }
|
glib = { git = "https://github.com/gtk-rs/glib" }
|
||||||
gstreamer = { git = "https://github.com/sdroege/gstreamer-rs", features = ["v1_10"] }
|
gstreamer = { git = "https://github.com/sdroege/gstreamer-rs", features = ["v1_10"] }
|
||||||
|
gstreamer-base = { git = "https://github.com/sdroege/gstreamer-rs" }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
gcc = "0.3"
|
gcc = "0.3"
|
||||||
|
|
|
@ -6,26 +6,24 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use buffer::*;
|
|
||||||
use miniobject::*;
|
|
||||||
use log::*;
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use slog::Logger;
|
|
||||||
|
use gst;
|
||||||
|
use gst::prelude::*;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref LOGGER: Logger = {
|
static ref CAT: gst::DebugCategory = {
|
||||||
Logger::root(GstDebugDrain::new(None,
|
gst::DebugCategory::new(
|
||||||
"rsadapter",
|
"rsadapter",
|
||||||
0,
|
gst::DebugColorFlags::empty(),
|
||||||
"Rust buffer adapter"),
|
"Rust buffer adapter",
|
||||||
o!())
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Adapter {
|
pub struct Adapter {
|
||||||
deque: VecDeque<ReadMappedBuffer>,
|
deque: VecDeque<gst::MappedBuffer<gst::buffer::Readable>>,
|
||||||
size: usize,
|
size: usize,
|
||||||
skip: usize,
|
skip: usize,
|
||||||
scratch: Vec<u8>,
|
scratch: Vec<u8>,
|
||||||
|
@ -46,19 +44,19 @@ impl Adapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push(&mut self, buffer: GstRc<Buffer>) {
|
pub fn push(&mut self, buffer: gst::Buffer) {
|
||||||
let size = buffer.get_size();
|
let size = buffer.get_size();
|
||||||
|
|
||||||
self.size += size;
|
self.size += size;
|
||||||
trace!(
|
gst_trace!(
|
||||||
LOGGER,
|
CAT,
|
||||||
"Storing {:?} of size {}, now have size {}",
|
"Storing {:?} of size {}, now have size {}",
|
||||||
buffer,
|
buffer,
|
||||||
size,
|
size,
|
||||||
self.size
|
self.size
|
||||||
);
|
);
|
||||||
self.deque
|
self.deque
|
||||||
.push_back(Buffer::into_read_mapped_buffer(buffer).unwrap());
|
.push_back(buffer.into_mapped_buffer_readable().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
|
@ -66,29 +64,34 @@ impl Adapter {
|
||||||
self.size = 0;
|
self.size = 0;
|
||||||
self.skip = 0;
|
self.skip = 0;
|
||||||
self.scratch.clear();
|
self.scratch.clear();
|
||||||
trace!(LOGGER, "Cleared adapter");
|
gst_trace!(CAT, "Cleared adapter");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_available(&self) -> usize {
|
pub fn get_available(&self) -> usize {
|
||||||
self.size
|
self.size
|
||||||
}
|
}
|
||||||
|
|
||||||
fn copy_data(deque: &VecDeque<ReadMappedBuffer>, skip: usize, data: &mut [u8], size: usize) {
|
fn copy_data(
|
||||||
|
deque: &VecDeque<gst::MappedBuffer<gst::buffer::Readable>>,
|
||||||
|
skip: usize,
|
||||||
|
data: &mut [u8],
|
||||||
|
size: usize,
|
||||||
|
) {
|
||||||
let mut skip = skip;
|
let mut skip = skip;
|
||||||
let mut left = size;
|
let mut left = size;
|
||||||
let mut idx = 0;
|
let mut idx = 0;
|
||||||
|
|
||||||
trace!(LOGGER, "Copying {} bytes", size);
|
gst_trace!(CAT, "Copying {} bytes", size);
|
||||||
|
|
||||||
for item in deque {
|
for item in deque {
|
||||||
let data_item = item.as_slice();
|
let data_item = item.as_slice();
|
||||||
|
|
||||||
let to_copy = cmp::min(left, data_item.len() - skip);
|
let to_copy = cmp::min(left, data_item.len() - skip);
|
||||||
trace!(
|
gst_trace!(
|
||||||
LOGGER,
|
CAT,
|
||||||
"Copying {} bytes from {:?}, {} more to go",
|
"Copying {} bytes from {:?}, {} more to go",
|
||||||
to_copy,
|
to_copy,
|
||||||
item,
|
item.get_buffer(),
|
||||||
left - to_copy
|
left - to_copy
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -107,8 +110,8 @@ impl Adapter {
|
||||||
let size = data.len();
|
let size = data.len();
|
||||||
|
|
||||||
if self.size < size {
|
if self.size < size {
|
||||||
debug!(
|
gst_debug!(
|
||||||
LOGGER,
|
CAT,
|
||||||
"Peeking {} bytes into, not enough data: have {}",
|
"Peeking {} bytes into, not enough data: have {}",
|
||||||
size,
|
size,
|
||||||
self.size
|
self.size
|
||||||
|
@ -116,7 +119,7 @@ impl Adapter {
|
||||||
return Err(AdapterError::NotEnoughData);
|
return Err(AdapterError::NotEnoughData);
|
||||||
}
|
}
|
||||||
|
|
||||||
trace!(LOGGER, "Peeking {} bytes into", size);
|
gst_trace!(CAT, "Peeking {} bytes into", size);
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -127,8 +130,8 @@ impl Adapter {
|
||||||
|
|
||||||
pub fn peek(&mut self, size: usize) -> Result<&[u8], AdapterError> {
|
pub fn peek(&mut self, size: usize) -> Result<&[u8], AdapterError> {
|
||||||
if self.size < size {
|
if self.size < size {
|
||||||
debug!(
|
gst_debug!(
|
||||||
LOGGER,
|
CAT,
|
||||||
"Peeking {} bytes, not enough data: have {}",
|
"Peeking {} bytes, not enough data: have {}",
|
||||||
size,
|
size,
|
||||||
self.size
|
self.size
|
||||||
|
@ -141,13 +144,13 @@ impl Adapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(front) = self.deque.front() {
|
if let Some(front) = self.deque.front() {
|
||||||
trace!(LOGGER, "Peeking {} bytes, subbuffer of first", size);
|
gst_trace!(CAT, "Peeking {} bytes, subbuffer of first", size);
|
||||||
if front.get_size() - self.skip >= size {
|
if front.get_size() - self.skip >= size {
|
||||||
return Ok(&front.as_slice()[self.skip..self.skip + size]);
|
return Ok(&front.as_slice()[self.skip..self.skip + size]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trace!(LOGGER, "Peeking {} bytes, copy to scratch", size);
|
gst_trace!(CAT, "Peeking {} bytes, copy to scratch", size);
|
||||||
|
|
||||||
self.scratch.truncate(0);
|
self.scratch.truncate(0);
|
||||||
self.scratch.reserve(size);
|
self.scratch.reserve(size);
|
||||||
|
@ -159,10 +162,10 @@ impl Adapter {
|
||||||
Ok(self.scratch.as_slice())
|
Ok(self.scratch.as_slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_buffer(&mut self, size: usize) -> Result<GstRc<Buffer>, AdapterError> {
|
pub fn get_buffer(&mut self, size: usize) -> Result<gst::Buffer, AdapterError> {
|
||||||
if self.size < size {
|
if self.size < size {
|
||||||
debug!(
|
gst_debug!(
|
||||||
LOGGER,
|
CAT,
|
||||||
"Get buffer of {} bytes, not enough data: have {}",
|
"Get buffer of {} bytes, not enough data: have {}",
|
||||||
size,
|
size,
|
||||||
self.size
|
self.size
|
||||||
|
@ -171,13 +174,13 @@ impl Adapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return Ok(Buffer::new());
|
return Ok(gst::Buffer::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
let sub = self.deque
|
let sub = self.deque
|
||||||
.front()
|
.front()
|
||||||
.and_then(|front| if front.get_size() - self.skip >= size {
|
.and_then(|front| if front.get_size() - self.skip >= size {
|
||||||
trace!(LOGGER, "Get buffer of {} bytes, subbuffer of first", size);
|
gst_trace!(CAT, "Get buffer of {} bytes, subbuffer of first", size);
|
||||||
let new = front
|
let new = front
|
||||||
.get_buffer()
|
.get_buffer()
|
||||||
.copy_region(self.skip, Some(size))
|
.copy_region(self.skip, Some(size))
|
||||||
|
@ -192,10 +195,10 @@ impl Adapter {
|
||||||
return Ok(s);
|
return Ok(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
trace!(LOGGER, "Get buffer of {} bytes, copy into new buffer", size);
|
gst_trace!(CAT, "Get buffer of {} bytes, copy into new buffer", size);
|
||||||
let mut new = Buffer::new_with_size(size).unwrap();
|
let mut new = gst::Buffer::with_size(size).unwrap();
|
||||||
{
|
{
|
||||||
let mut map = new.get_mut().unwrap().map_readwrite().unwrap();
|
let mut map = new.get_mut().unwrap().map_writable().unwrap();
|
||||||
let data = map.as_mut_slice();
|
let data = map.as_mut_slice();
|
||||||
Self::copy_data(&self.deque, self.skip, data, size);
|
Self::copy_data(&self.deque, self.skip, data, size);
|
||||||
}
|
}
|
||||||
|
@ -205,8 +208,8 @@ impl Adapter {
|
||||||
|
|
||||||
pub fn flush(&mut self, size: usize) -> Result<(), AdapterError> {
|
pub fn flush(&mut self, size: usize) -> Result<(), AdapterError> {
|
||||||
if self.size < size {
|
if self.size < size {
|
||||||
debug!(
|
gst_debug!(
|
||||||
LOGGER,
|
CAT,
|
||||||
"Flush {} bytes, not enough data: have {}",
|
"Flush {} bytes, not enough data: have {}",
|
||||||
size,
|
size,
|
||||||
self.size
|
self.size
|
||||||
|
@ -218,17 +221,17 @@ impl Adapter {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
trace!(LOGGER, "Flushing {} bytes, have {}", size, self.size);
|
gst_trace!(CAT, "Flushing {} bytes, have {}", size, self.size);
|
||||||
|
|
||||||
let mut left = size;
|
let mut left = size;
|
||||||
while left > 0 {
|
while left > 0 {
|
||||||
let front_size = self.deque.front().unwrap().get_size() - self.skip;
|
let front_size = self.deque.front().unwrap().get_size() - self.skip;
|
||||||
|
|
||||||
if front_size <= left {
|
if front_size <= left {
|
||||||
trace!(
|
gst_trace!(
|
||||||
LOGGER,
|
CAT,
|
||||||
"Flushing whole {:?}, {} more to go",
|
"Flushing whole {:?}, {} more to go",
|
||||||
self.deque.front(),
|
self.deque.front().map(|b| b.get_buffer()),
|
||||||
left - front_size
|
left - front_size
|
||||||
);
|
);
|
||||||
self.deque.pop_front();
|
self.deque.pop_front();
|
||||||
|
@ -236,10 +239,10 @@ impl Adapter {
|
||||||
self.skip = 0;
|
self.skip = 0;
|
||||||
left -= front_size;
|
left -= front_size;
|
||||||
} else {
|
} else {
|
||||||
trace!(
|
gst_trace!(
|
||||||
LOGGER,
|
CAT,
|
||||||
"Flushing partial {:?}, {} more left",
|
"Flushing partial {:?}, {} more left",
|
||||||
self.deque.front(),
|
self.deque.front().map(|b| b.get_buffer()),
|
||||||
front_size - left
|
front_size - left
|
||||||
);
|
);
|
||||||
self.skip += left;
|
self.skip += left;
|
||||||
|
@ -255,24 +258,17 @@ impl Adapter {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::ptr;
|
use gst;
|
||||||
use gst_ffi;
|
|
||||||
|
|
||||||
fn init() {
|
|
||||||
unsafe {
|
|
||||||
gst_ffi::gst_init(ptr::null_mut(), ptr::null_mut());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_push_get() {
|
fn test_push_get() {
|
||||||
init();
|
gst::init().unwrap();
|
||||||
|
|
||||||
let mut a = Adapter::new();
|
let mut a = Adapter::new();
|
||||||
|
|
||||||
a.push(Buffer::new_with_size(100).unwrap());
|
a.push(gst::Buffer::with_size(100).unwrap());
|
||||||
assert_eq!(a.get_available(), 100);
|
assert_eq!(a.get_available(), 100);
|
||||||
a.push(Buffer::new_with_size(20).unwrap());
|
a.push(gst::Buffer::with_size(20).unwrap());
|
||||||
assert_eq!(a.get_available(), 120);
|
assert_eq!(a.get_available(), 120);
|
||||||
|
|
||||||
let b = a.get_buffer(20).unwrap();
|
let b = a.get_buffer(20).unwrap();
|
||||||
|
@ -282,7 +278,7 @@ mod tests {
|
||||||
assert_eq!(a.get_available(), 10);
|
assert_eq!(a.get_available(), 10);
|
||||||
assert_eq!(b.get_size(), 90);
|
assert_eq!(b.get_size(), 90);
|
||||||
|
|
||||||
a.push(Buffer::new_with_size(20).unwrap());
|
a.push(gst::Buffer::with_size(20).unwrap());
|
||||||
assert_eq!(a.get_available(), 30);
|
assert_eq!(a.get_available(), 30);
|
||||||
|
|
||||||
let b = a.get_buffer(20).unwrap();
|
let b = a.get_buffer(20).unwrap();
|
||||||
|
|
|
@ -1,583 +0,0 @@
|
||||||
// Copyright (C) 2016-2017 Sebastian Dröge <sebastian@centricular.com>
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
use std::ptr;
|
|
||||||
use std::mem;
|
|
||||||
use std::fmt;
|
|
||||||
use std::slice;
|
|
||||||
use std::u64;
|
|
||||||
use std::usize;
|
|
||||||
|
|
||||||
use miniobject::*;
|
|
||||||
|
|
||||||
use glib_ffi;
|
|
||||||
use gst_ffi;
|
|
||||||
|
|
||||||
pub struct Buffer(gst_ffi::GstBuffer);
|
|
||||||
|
|
||||||
#[derive(Derivative)]
|
|
||||||
#[derivative(Debug)]
|
|
||||||
pub struct ReadBufferMap<'a> {
|
|
||||||
buffer: &'a Buffer,
|
|
||||||
#[derivative(Debug = "ignore")] map_info: gst_ffi::GstMapInfo,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Derivative)]
|
|
||||||
#[derivative(Debug)]
|
|
||||||
pub struct ReadWriteBufferMap<'a> {
|
|
||||||
buffer: &'a Buffer,
|
|
||||||
#[derivative(Debug = "ignore")] map_info: gst_ffi::GstMapInfo,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Derivative)]
|
|
||||||
#[derivative(Debug)]
|
|
||||||
pub struct ReadMappedBuffer {
|
|
||||||
buffer: GstRc<Buffer>,
|
|
||||||
#[derivative(Debug = "ignore")] map_info: gst_ffi::GstMapInfo,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Derivative)]
|
|
||||||
#[derivative(Debug)]
|
|
||||||
pub struct ReadWriteMappedBuffer {
|
|
||||||
buffer: GstRc<Buffer>,
|
|
||||||
#[derivative(Debug = "ignore")] map_info: gst_ffi::GstMapInfo,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl MiniObject for Buffer {
|
|
||||||
type PtrType = gst_ffi::GstBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Buffer {
|
|
||||||
pub fn new() -> GstRc<Buffer> {
|
|
||||||
unsafe { GstRc::from_owned_ptr(gst_ffi::gst_buffer_new()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_with_size(size: usize) -> Option<GstRc<Buffer>> {
|
|
||||||
let raw =
|
|
||||||
unsafe { gst_ffi::gst_buffer_new_allocate(ptr::null_mut(), size, ptr::null_mut()) };
|
|
||||||
if raw.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(unsafe { GstRc::from_owned_ptr(raw) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "C" fn vec_drop(vec: glib_ffi::gpointer) {
|
|
||||||
let vec: Box<Vec<u8>> = Box::from_raw(vec as *mut Vec<u8>);
|
|
||||||
drop(vec);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_vec(vec: Vec<u8>) -> Option<GstRc<Buffer>> {
|
|
||||||
let raw = unsafe {
|
|
||||||
let mut vec = Box::new(vec);
|
|
||||||
let maxsize = vec.capacity();
|
|
||||||
let size = vec.len();
|
|
||||||
let data = vec.as_mut_ptr();
|
|
||||||
let user_data = Box::into_raw(vec);
|
|
||||||
gst_ffi::gst_buffer_new_wrapped_full(
|
|
||||||
gst_ffi::GstMemoryFlags::empty(),
|
|
||||||
data as glib_ffi::gpointer,
|
|
||||||
maxsize,
|
|
||||||
0,
|
|
||||||
size,
|
|
||||||
user_data as glib_ffi::gpointer,
|
|
||||||
Some(Buffer::vec_drop),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
if raw.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(unsafe { GstRc::from_owned_ptr(raw) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn map_read(&self) -> Option<ReadBufferMap> {
|
|
||||||
let mut map_info: gst_ffi::GstMapInfo = unsafe { mem::zeroed() };
|
|
||||||
let res = unsafe {
|
|
||||||
gst_ffi::gst_buffer_map(
|
|
||||||
self.as_mut_ptr() as *mut gst_ffi::GstBuffer,
|
|
||||||
&mut map_info,
|
|
||||||
gst_ffi::GST_MAP_READ,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if res == glib_ffi::GTRUE {
|
|
||||||
Some(ReadBufferMap {
|
|
||||||
buffer: self,
|
|
||||||
map_info: map_info,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn map_readwrite(&mut self) -> Option<ReadWriteBufferMap> {
|
|
||||||
let mut map_info: gst_ffi::GstMapInfo = unsafe { mem::zeroed() };
|
|
||||||
let res = unsafe {
|
|
||||||
gst_ffi::gst_buffer_map(self.as_mut_ptr(), &mut map_info, gst_ffi::GST_MAP_READWRITE)
|
|
||||||
};
|
|
||||||
if res == glib_ffi::GTRUE {
|
|
||||||
Some(ReadWriteBufferMap {
|
|
||||||
buffer: self,
|
|
||||||
map_info: map_info,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_read_mapped_buffer(buffer: GstRc<Buffer>) -> Option<ReadMappedBuffer> {
|
|
||||||
let mut map_info: gst_ffi::GstMapInfo = unsafe { mem::zeroed() };
|
|
||||||
let res = unsafe {
|
|
||||||
gst_ffi::gst_buffer_map(buffer.as_mut_ptr(), &mut map_info, gst_ffi::GST_MAP_READ)
|
|
||||||
};
|
|
||||||
if res == glib_ffi::GTRUE {
|
|
||||||
Some(ReadMappedBuffer {
|
|
||||||
buffer: buffer,
|
|
||||||
map_info: map_info,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_readwrite_mapped_buffer(buffer: GstRc<Buffer>) -> Option<ReadWriteMappedBuffer> {
|
|
||||||
let mut map_info: gst_ffi::GstMapInfo = unsafe { mem::zeroed() };
|
|
||||||
let res = unsafe {
|
|
||||||
gst_ffi::gst_buffer_map(
|
|
||||||
buffer.as_mut_ptr(),
|
|
||||||
&mut map_info,
|
|
||||||
gst_ffi::GST_MAP_READWRITE,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
if res == glib_ffi::GTRUE {
|
|
||||||
Some(ReadWriteMappedBuffer {
|
|
||||||
buffer: buffer,
|
|
||||||
map_info: map_info,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn append(buffer: GstRc<Buffer>, other: GstRc<Buffer>) -> GstRc<Buffer> {
|
|
||||||
unsafe {
|
|
||||||
GstRc::from_owned_ptr(gst_ffi::gst_buffer_append(
|
|
||||||
buffer.into_ptr() as *mut gst_ffi::GstBuffer,
|
|
||||||
other.into_ptr() as *mut gst_ffi::GstBuffer,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn copy_region(&self, offset: usize, size: Option<usize>) -> Option<GstRc<Buffer>> {
|
|
||||||
let size_real = size.unwrap_or(usize::MAX);
|
|
||||||
|
|
||||||
let raw = unsafe {
|
|
||||||
gst_ffi::gst_buffer_copy_region(
|
|
||||||
self.as_mut_ptr(),
|
|
||||||
gst_ffi::GST_BUFFER_COPY_ALL,
|
|
||||||
offset,
|
|
||||||
size_real,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
if raw.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(unsafe { GstRc::from_owned_ptr(raw) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn copy_from_slice(&mut self, offset: usize, slice: &[u8]) -> Result<(), usize> {
|
|
||||||
let maxsize = self.get_maxsize();
|
|
||||||
let size = slice.len();
|
|
||||||
|
|
||||||
assert!(maxsize >= offset && maxsize - offset >= size);
|
|
||||||
|
|
||||||
let copied = unsafe {
|
|
||||||
let src = slice.as_ptr();
|
|
||||||
gst_ffi::gst_buffer_fill(
|
|
||||||
self.as_mut_ptr(),
|
|
||||||
offset,
|
|
||||||
src as glib_ffi::gconstpointer,
|
|
||||||
size,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
if copied == size {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(copied)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn copy_to_slice(&self, offset: usize, slice: &mut [u8]) -> Result<(), usize> {
|
|
||||||
let maxsize = self.get_size();
|
|
||||||
let size = slice.len();
|
|
||||||
|
|
||||||
assert!(maxsize >= offset && maxsize - offset >= size);
|
|
||||||
|
|
||||||
let copied = unsafe {
|
|
||||||
let dest = slice.as_mut_ptr();
|
|
||||||
gst_ffi::gst_buffer_extract(self.as_mut_ptr(), offset, dest as glib_ffi::gpointer, size)
|
|
||||||
};
|
|
||||||
|
|
||||||
if copied == size {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(copied)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_size(&self) -> usize {
|
|
||||||
unsafe { gst_ffi::gst_buffer_get_size(self.as_mut_ptr()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_maxsize(&self) -> usize {
|
|
||||||
let mut maxsize: usize = 0;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
gst_ffi::gst_buffer_get_sizes_range(
|
|
||||||
self.as_mut_ptr(),
|
|
||||||
0,
|
|
||||||
-1,
|
|
||||||
ptr::null_mut(),
|
|
||||||
&mut maxsize as *mut usize,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
maxsize
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_size(&mut self, size: usize) {
|
|
||||||
assert!(self.get_maxsize() >= size);
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
gst_ffi::gst_buffer_set_size(self.as_mut_ptr(), size as isize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_offset(&self) -> Option<u64> {
|
|
||||||
let offset = self.0.offset;
|
|
||||||
|
|
||||||
if offset == u64::MAX {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(offset)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_offset(&mut self, offset: Option<u64>) {
|
|
||||||
let offset = offset.unwrap_or(u64::MAX);
|
|
||||||
self.0.offset = offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_offset_end(&self) -> Option<u64> {
|
|
||||||
let offset_end = self.0.offset_end;
|
|
||||||
|
|
||||||
if offset_end == u64::MAX {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(offset_end)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_offset_end(&mut self, offset_end: Option<u64>) {
|
|
||||||
let offset_end = offset_end.unwrap_or(u64::MAX);
|
|
||||||
self.0.offset_end = offset_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_pts(&self) -> Option<u64> {
|
|
||||||
let pts = self.0.pts;
|
|
||||||
|
|
||||||
if pts == u64::MAX {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(pts)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_pts(&mut self, pts: Option<u64>) {
|
|
||||||
let pts = pts.unwrap_or(u64::MAX);
|
|
||||||
self.0.pts = pts;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_dts(&self) -> Option<u64> {
|
|
||||||
let dts = self.0.dts;
|
|
||||||
|
|
||||||
if dts == u64::MAX {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(dts)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_dts(&mut self, dts: Option<u64>) {
|
|
||||||
let dts = dts.unwrap_or(u64::MAX);
|
|
||||||
self.0.dts = dts;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_duration(&self) -> Option<u64> {
|
|
||||||
let duration = self.0.duration;
|
|
||||||
|
|
||||||
if duration == u64::MAX {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(duration)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_duration(&mut self, duration: Option<u64>) {
|
|
||||||
let duration = duration.unwrap_or(u64::MAX);
|
|
||||||
self.0.duration = duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_flags(&self) -> BufferFlags {
|
|
||||||
BufferFlags::from_bits_truncate(self.0.mini_object.flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_flags(&mut self, flags: BufferFlags) {
|
|
||||||
self.0.mini_object.flags = flags.bits();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Sync for Buffer {}
|
|
||||||
unsafe impl Send for Buffer {}
|
|
||||||
|
|
||||||
impl fmt::Debug for Buffer {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(f, "{:?}", unsafe { self.as_ptr() })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for Buffer {
|
|
||||||
fn eq(&self, other: &Buffer) -> bool {
|
|
||||||
if self.get_size() != other.get_size() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let self_map = self.map_read();
|
|
||||||
let other_map = other.map_read();
|
|
||||||
|
|
||||||
match (self_map, other_map) {
|
|
||||||
(Some(self_map), Some(other_map)) => self_map.as_slice().eq(other_map.as_slice()),
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for Buffer {}
|
|
||||||
|
|
||||||
impl ToOwned for Buffer {
|
|
||||||
type Owned = GstRc<Buffer>;
|
|
||||||
|
|
||||||
fn to_owned(&self) -> GstRc<Buffer> {
|
|
||||||
unsafe { GstRc::from_unowned_ptr(self.as_ptr()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ReadBufferMap<'a> {
|
|
||||||
pub fn as_slice(&self) -> &[u8] {
|
|
||||||
unsafe { slice::from_raw_parts(self.map_info.data as *const u8, self.map_info.size) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_size(&self) -> usize {
|
|
||||||
self.map_info.size
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_buffer(&self) -> &Buffer {
|
|
||||||
self.buffer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Drop for ReadBufferMap<'a> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
gst_ffi::gst_buffer_unmap(self.buffer.as_mut_ptr(), &mut self.map_info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ReadWriteBufferMap<'a> {
|
|
||||||
pub fn as_mut_slice(&mut self) -> &mut [u8] {
|
|
||||||
unsafe { slice::from_raw_parts_mut(self.map_info.data as *mut u8, self.map_info.size) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_slice(&self) -> &[u8] {
|
|
||||||
unsafe { slice::from_raw_parts(self.map_info.data as *const u8, self.map_info.size) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_size(&self) -> usize {
|
|
||||||
self.map_info.size
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_buffer(&self) -> &Buffer {
|
|
||||||
self.buffer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Drop for ReadWriteBufferMap<'a> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
gst_ffi::gst_buffer_unmap(self.buffer.as_mut_ptr(), &mut self.map_info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ReadMappedBuffer {
|
|
||||||
pub fn as_slice(&self) -> &[u8] {
|
|
||||||
unsafe { slice::from_raw_parts(self.map_info.data as *const u8, self.map_info.size) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_size(&self) -> usize {
|
|
||||||
self.map_info.size
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_buffer(&self) -> &Buffer {
|
|
||||||
self.buffer.as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for ReadMappedBuffer {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
gst_ffi::gst_buffer_unmap(self.buffer.as_mut_ptr(), &mut self.map_info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Sync for ReadMappedBuffer {}
|
|
||||||
unsafe impl Send for ReadMappedBuffer {}
|
|
||||||
|
|
||||||
impl ReadWriteMappedBuffer {
|
|
||||||
pub fn as_mut_slice(&mut self) -> &mut [u8] {
|
|
||||||
unsafe { slice::from_raw_parts_mut(self.map_info.data as *mut u8, self.map_info.size) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_slice(&self) -> &[u8] {
|
|
||||||
unsafe { slice::from_raw_parts(self.map_info.data as *const u8, self.map_info.size) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_size(&self) -> usize {
|
|
||||||
self.map_info.size
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_buffer(&self) -> &Buffer {
|
|
||||||
self.buffer.as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for ReadWriteMappedBuffer {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
gst_ffi::gst_buffer_unmap(self.buffer.as_mut_ptr(), &mut self.map_info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Sync for ReadWriteMappedBuffer {}
|
|
||||||
unsafe impl Send for ReadWriteMappedBuffer {}
|
|
||||||
|
|
||||||
// FIXME: Duplicate of gst_ffi::GstBufferFlags with nicer naming
|
|
||||||
bitflags! {
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct BufferFlags: u32 {
|
|
||||||
const BUFFER_FLAG_LIVE = 0b0000000000010000;
|
|
||||||
const BUFFER_FLAG_DECODE_ONLY = 0b0000000000100000;
|
|
||||||
const BUFFER_FLAG_DISCONT = 0b0000000001000000;
|
|
||||||
const BUFFER_FLAG_RESYNC = 0b0000000010000000;
|
|
||||||
const BUFFER_FLAG_CORRUPTED = 0b0000000100000000;
|
|
||||||
const BUFFER_FLAG_MARKER = 0b0000001000000000;
|
|
||||||
const BUFFER_FLAG_HEADER = 0b0000010000000000;
|
|
||||||
const BUFFER_FLAG_GAP = 0b0000100000000000;
|
|
||||||
const BUFFER_FLAG_DROPPABLE = 0b0001000000000000;
|
|
||||||
const BUFFER_FLAG_DELTA_UNIT = 0b0010000000000000;
|
|
||||||
const BUFFER_FLAG_TAG_MEMORY = 0b0100000000000000;
|
|
||||||
const BUFFER_FLAG_SYNC_AFTER = 0b1000000000000000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use std::ptr;
|
|
||||||
|
|
||||||
fn init() {
|
|
||||||
unsafe {
|
|
||||||
gst_ffi::gst_init(ptr::null_mut(), ptr::null_mut());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_fields() {
|
|
||||||
init();
|
|
||||||
|
|
||||||
let mut buffer = Buffer::new();
|
|
||||||
|
|
||||||
{
|
|
||||||
let buffer = buffer.get_mut().unwrap();
|
|
||||||
buffer.set_pts(Some(1));
|
|
||||||
buffer.set_dts(Some(2));
|
|
||||||
buffer.set_offset(Some(3));
|
|
||||||
buffer.set_offset_end(Some(4));
|
|
||||||
buffer.set_duration(Some(5));
|
|
||||||
}
|
|
||||||
assert_eq!(buffer.get_pts(), Some(1));
|
|
||||||
assert_eq!(buffer.get_dts(), Some(2));
|
|
||||||
assert_eq!(buffer.get_offset(), Some(3));
|
|
||||||
assert_eq!(buffer.get_offset_end(), Some(4));
|
|
||||||
assert_eq!(buffer.get_duration(), Some(5));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_writability() {
|
|
||||||
init();
|
|
||||||
|
|
||||||
let mut buffer = Buffer::from_vec(vec![1, 2, 3, 4]).unwrap();
|
|
||||||
{
|
|
||||||
let data = buffer.map_read().unwrap();
|
|
||||||
assert_eq!(data.as_slice(), vec![1, 2, 3, 4].as_slice());
|
|
||||||
}
|
|
||||||
assert_ne!(buffer.get_mut(), None);
|
|
||||||
{
|
|
||||||
let buffer = buffer.get_mut().unwrap();
|
|
||||||
buffer.set_pts(Some(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut buffer2 = buffer.clone();
|
|
||||||
assert_eq!(buffer.get_mut(), None);
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
assert_eq!(buffer2.as_ptr(), buffer.as_ptr());
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
let buffer2 = buffer2.make_mut();
|
|
||||||
unsafe {
|
|
||||||
assert_ne!(buffer2.as_ptr(), buffer.as_ptr());
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer2.set_pts(Some(2));
|
|
||||||
|
|
||||||
let mut data = buffer2.map_readwrite().unwrap();
|
|
||||||
assert_eq!(data.as_slice(), vec![1, 2, 3, 4].as_slice());
|
|
||||||
data.as_mut_slice()[0] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(buffer.get_pts(), Some(1));
|
|
||||||
assert_eq!(buffer2.get_pts(), Some(2));
|
|
||||||
|
|
||||||
{
|
|
||||||
let data = buffer.map_read().unwrap();
|
|
||||||
assert_eq!(data.as_slice(), vec![1, 2, 3, 4].as_slice());
|
|
||||||
|
|
||||||
let data = buffer2.map_read().unwrap();
|
|
||||||
assert_eq!(data.as_slice(), vec![0, 2, 3, 4].as_slice());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,184 +0,0 @@
|
||||||
// Copyright (C) 2016-2017 Sebastian Dröge <sebastian@centricular.com>
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
use std::ffi::CString;
|
|
||||||
use std::ffi::CStr;
|
|
||||||
use std::fmt;
|
|
||||||
use value::*;
|
|
||||||
use miniobject::*;
|
|
||||||
use structure::*;
|
|
||||||
|
|
||||||
use glib_ffi;
|
|
||||||
use gst_ffi;
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Caps(gst_ffi::GstCaps);
|
|
||||||
|
|
||||||
unsafe impl MiniObject for Caps {
|
|
||||||
type PtrType = gst_ffi::GstCaps;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Caps {
|
|
||||||
pub fn new_empty() -> GstRc<Self> {
|
|
||||||
unsafe { GstRc::from_owned_ptr(gst_ffi::gst_caps_new_empty()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_any() -> GstRc<Self> {
|
|
||||||
unsafe { GstRc::from_owned_ptr(gst_ffi::gst_caps_new_any()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_simple(name: &str, values: &[(&str, Value)]) -> GstRc<Self> {
|
|
||||||
let mut caps = Caps::new_empty();
|
|
||||||
|
|
||||||
let name_cstr = CString::new(name).unwrap();
|
|
||||||
let structure = unsafe { gst_ffi::gst_structure_new_empty(name_cstr.as_ptr()) };
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
gst_ffi::gst_caps_append_structure(caps.as_mut_ptr(), structure);
|
|
||||||
}
|
|
||||||
|
|
||||||
caps.get_mut().unwrap().set_simple(values);
|
|
||||||
|
|
||||||
caps
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_string(value: &str) -> Option<GstRc<Self>> {
|
|
||||||
let value_cstr = CString::new(value).unwrap();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let caps_ptr = gst_ffi::gst_caps_from_string(value_cstr.as_ptr());
|
|
||||||
|
|
||||||
if caps_ptr.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(GstRc::from_owned_ptr(caps_ptr))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_simple(&mut self, values: &[(&str, Value)]) {
|
|
||||||
for value in values {
|
|
||||||
let name_cstr = CString::new(value.0).unwrap();
|
|
||||||
unsafe {
|
|
||||||
let gvalue = value.1.as_ptr();
|
|
||||||
gst_ffi::gst_caps_set_value(self.as_mut_ptr(), name_cstr.as_ptr(), gvalue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_string(&self) -> String {
|
|
||||||
unsafe {
|
|
||||||
let ptr = gst_ffi::gst_caps_to_string(self.as_ptr());
|
|
||||||
let s = CStr::from_ptr(ptr).to_str().unwrap().into();
|
|
||||||
glib_ffi::g_free(ptr as glib_ffi::gpointer);
|
|
||||||
|
|
||||||
s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_structure(&self, idx: u32) -> Option<&Structure> {
|
|
||||||
unsafe {
|
|
||||||
let structure = gst_ffi::gst_caps_get_structure(self.as_ptr(), idx);
|
|
||||||
if structure.is_null() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(Structure::from_borrowed_ptr(
|
|
||||||
structure as *const gst_ffi::GstStructure,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_mut_structure(&mut self, idx: u32) -> Option<&mut Structure> {
|
|
||||||
unsafe {
|
|
||||||
let structure = gst_ffi::gst_caps_get_structure(self.as_ptr(), idx);
|
|
||||||
if structure.is_null() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(Structure::from_borrowed_mut_ptr(
|
|
||||||
structure as *mut gst_ffi::GstStructure,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: All kinds of caps operations
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Caps {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
f.write_str(&self.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for Caps {
|
|
||||||
fn eq(&self, other: &Caps) -> bool {
|
|
||||||
(unsafe { gst_ffi::gst_caps_is_equal(self.as_ptr(), other.as_ptr()) } == glib_ffi::GTRUE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for Caps {}
|
|
||||||
|
|
||||||
impl ToOwned for Caps {
|
|
||||||
type Owned = GstRc<Caps>;
|
|
||||||
|
|
||||||
fn to_owned(&self) -> GstRc<Caps> {
|
|
||||||
unsafe { GstRc::from_unowned_ptr(self.as_ptr()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Sync for Caps {}
|
|
||||||
unsafe impl Send for Caps {}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use std::ptr;
|
|
||||||
|
|
||||||
fn init() {
|
|
||||||
unsafe {
|
|
||||||
gst_ffi::gst_init(ptr::null_mut(), ptr::null_mut());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_simple() {
|
|
||||||
init();
|
|
||||||
|
|
||||||
let caps = Caps::new_simple(
|
|
||||||
"foo/bar",
|
|
||||||
&[
|
|
||||||
("int", 12.into()),
|
|
||||||
("bool", true.into()),
|
|
||||||
("string", "bla".into()),
|
|
||||||
("fraction", (1, 2).into()),
|
|
||||||
("array", vec![1.into(), 2.into()].into()),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
caps.to_string(),
|
|
||||||
"foo/bar, int=(int)12, bool=(boolean)true, string=(string)bla, \
|
|
||||||
fraction=(fraction)1/2, array=(int)< 1, 2 >"
|
|
||||||
);
|
|
||||||
|
|
||||||
let s = caps.get_structure(0).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
s,
|
|
||||||
OwnedStructure::new(
|
|
||||||
"foo/bar",
|
|
||||||
&[
|
|
||||||
("int", 12.into()),
|
|
||||||
("bool", true.into()),
|
|
||||||
("string", "bla".into()),
|
|
||||||
("fraction", (1, 2).into()),
|
|
||||||
("array", vec![1.into(), 2.into()].into())
|
|
||||||
]
|
|
||||||
).as_ref()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -18,19 +18,15 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::u32;
|
use std::u32;
|
||||||
use std::u64;
|
use std::u64;
|
||||||
|
|
||||||
use slog::Logger;
|
|
||||||
|
|
||||||
use utils::*;
|
|
||||||
use error::*;
|
use error::*;
|
||||||
use buffer::*;
|
|
||||||
use miniobject::*;
|
|
||||||
use log::*;
|
|
||||||
use caps::Caps;
|
|
||||||
use plugin::Plugin;
|
|
||||||
|
|
||||||
use glib_ffi;
|
use glib_ffi;
|
||||||
use gst_ffi;
|
use gst_ffi;
|
||||||
|
|
||||||
|
use glib;
|
||||||
|
use glib::translate::*;
|
||||||
|
use gst;
|
||||||
|
|
||||||
pub type StreamIndex = u32;
|
pub type StreamIndex = u32;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -52,7 +48,7 @@ pub enum HandleBufferResult {
|
||||||
StreamsChanged(Vec<Stream>),
|
StreamsChanged(Vec<Stream>),
|
||||||
// TODO need something to replace/add new streams
|
// TODO need something to replace/add new streams
|
||||||
// TODO should probably directly implement the GstStreams new world order
|
// TODO should probably directly implement the GstStreams new world order
|
||||||
BufferForStream(StreamIndex, GstRc<Buffer>),
|
BufferForStream(StreamIndex, gst::Buffer),
|
||||||
Eos(Option<StreamIndex>),
|
Eos(Option<StreamIndex>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +63,7 @@ pub trait Demuxer {
|
||||||
fn seek(&mut self, start: u64, stop: Option<u64>) -> Result<SeekResult, ErrorMessage>;
|
fn seek(&mut self, start: u64, stop: Option<u64>) -> Result<SeekResult, ErrorMessage>;
|
||||||
fn handle_buffer(
|
fn handle_buffer(
|
||||||
&mut self,
|
&mut self,
|
||||||
buffer: Option<GstRc<Buffer>>,
|
buffer: Option<gst::Buffer>,
|
||||||
) -> Result<HandleBufferResult, FlowError>;
|
) -> Result<HandleBufferResult, FlowError>;
|
||||||
fn end_of_stream(&mut self) -> Result<(), ErrorMessage>;
|
fn end_of_stream(&mut self) -> Result<(), ErrorMessage>;
|
||||||
|
|
||||||
|
@ -79,12 +75,12 @@ pub trait Demuxer {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Stream {
|
pub struct Stream {
|
||||||
pub index: StreamIndex,
|
pub index: StreamIndex,
|
||||||
pub caps: GstRc<Caps>,
|
pub caps: gst::Caps,
|
||||||
pub stream_id: String,
|
pub stream_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stream {
|
impl Stream {
|
||||||
pub fn new(index: StreamIndex, caps: GstRc<Caps>, stream_id: String) -> Stream {
|
pub fn new(index: StreamIndex, caps: gst::Caps, stream_id: String) -> Stream {
|
||||||
Stream {
|
Stream {
|
||||||
index: index,
|
index: index,
|
||||||
caps: caps,
|
caps: caps,
|
||||||
|
@ -95,7 +91,7 @@ impl Stream {
|
||||||
|
|
||||||
pub struct DemuxerWrapper {
|
pub struct DemuxerWrapper {
|
||||||
raw: *mut gst_ffi::GstElement,
|
raw: *mut gst_ffi::GstElement,
|
||||||
logger: Logger,
|
cat: gst::DebugCategory,
|
||||||
demuxer: Mutex<Box<Demuxer>>,
|
demuxer: Mutex<Box<Demuxer>>,
|
||||||
panicked: AtomicBool,
|
panicked: AtomicBool,
|
||||||
}
|
}
|
||||||
|
@ -104,15 +100,11 @@ impl DemuxerWrapper {
|
||||||
fn new(raw: *mut gst_ffi::GstElement, demuxer: Box<Demuxer>) -> DemuxerWrapper {
|
fn new(raw: *mut gst_ffi::GstElement, demuxer: Box<Demuxer>) -> DemuxerWrapper {
|
||||||
DemuxerWrapper {
|
DemuxerWrapper {
|
||||||
raw: raw,
|
raw: raw,
|
||||||
logger: Logger::root(
|
cat: gst::DebugCategory::new(
|
||||||
GstDebugDrain::new(
|
|
||||||
Some(unsafe { &Element::new(raw) }),
|
|
||||||
"rsdemux",
|
"rsdemux",
|
||||||
0,
|
gst::DebugColorFlags::empty(),
|
||||||
"Rust demuxer base class",
|
"Rust demuxer base class",
|
||||||
),
|
),
|
||||||
o!(),
|
|
||||||
),
|
|
||||||
demuxer: Mutex::new(demuxer),
|
demuxer: Mutex::new(demuxer),
|
||||||
panicked: AtomicBool::new(false),
|
panicked: AtomicBool::new(false),
|
||||||
}
|
}
|
||||||
|
@ -121,8 +113,9 @@ impl DemuxerWrapper {
|
||||||
fn start(&self, upstream_size: u64, random_access: bool) -> bool {
|
fn start(&self, upstream_size: u64, random_access: bool) -> bool {
|
||||||
let demuxer = &mut self.demuxer.lock().unwrap();
|
let demuxer = &mut self.demuxer.lock().unwrap();
|
||||||
|
|
||||||
debug!(
|
gst_debug!(
|
||||||
self.logger,
|
self.cat,
|
||||||
|
// TODO obj: demuxer
|
||||||
"Starting with upstream size {} and random access {}",
|
"Starting with upstream size {} and random access {}",
|
||||||
upstream_size,
|
upstream_size,
|
||||||
random_access
|
random_access
|
||||||
|
@ -136,11 +129,18 @@ impl DemuxerWrapper {
|
||||||
|
|
||||||
match demuxer.start(upstream_size, random_access) {
|
match demuxer.start(upstream_size, random_access) {
|
||||||
Ok(..) => {
|
Ok(..) => {
|
||||||
trace!(self.logger, "Successfully started");
|
gst_trace!(
|
||||||
|
self.cat,
|
||||||
|
/* TODO obj: demuxer,*/ "Successfully started"
|
||||||
|
);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Err(ref msg) => {
|
Err(ref msg) => {
|
||||||
error!(self.logger, "Failed to start: {:?}", msg);
|
gst_error!(
|
||||||
|
self.cat,
|
||||||
|
/* TODO obj: demuxer,*/ "Failed to start: {:?}",
|
||||||
|
msg
|
||||||
|
);
|
||||||
self.post_message(msg);
|
self.post_message(msg);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -149,15 +149,19 @@ impl DemuxerWrapper {
|
||||||
fn stop(&self) -> bool {
|
fn stop(&self) -> bool {
|
||||||
let demuxer = &mut self.demuxer.lock().unwrap();
|
let demuxer = &mut self.demuxer.lock().unwrap();
|
||||||
|
|
||||||
debug!(self.logger, "Stopping");
|
gst_debug!(self.cat, /* TODO obj: demuxer,*/ "Stopping");
|
||||||
|
|
||||||
match demuxer.stop() {
|
match demuxer.stop() {
|
||||||
Ok(..) => {
|
Ok(..) => {
|
||||||
trace!(self.logger, "Successfully stop");
|
gst_trace!(self.cat, /* TODO obj: demuxer,*/ "Successfully stop");
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Err(ref msg) => {
|
Err(ref msg) => {
|
||||||
error!(self.logger, "Failed to stop: {:?}", msg);
|
gst_error!(
|
||||||
|
self.cat,
|
||||||
|
/* TODO obj: demuxer,*/ "Failed to stop: {:?}",
|
||||||
|
msg
|
||||||
|
);
|
||||||
self.post_message(msg);
|
self.post_message(msg);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -168,7 +172,11 @@ impl DemuxerWrapper {
|
||||||
let demuxer = &self.demuxer.lock().unwrap();
|
let demuxer = &self.demuxer.lock().unwrap();
|
||||||
|
|
||||||
let seekable = demuxer.is_seekable();
|
let seekable = demuxer.is_seekable();
|
||||||
debug!(self.logger, "Seekable {}", seekable);
|
gst_debug!(
|
||||||
|
self.cat,
|
||||||
|
/* TODO obj: demuxer,*/ "Seekable {}",
|
||||||
|
seekable
|
||||||
|
);
|
||||||
|
|
||||||
seekable
|
seekable
|
||||||
}
|
}
|
||||||
|
@ -179,12 +187,16 @@ impl DemuxerWrapper {
|
||||||
|
|
||||||
match demuxer.get_position() {
|
match demuxer.get_position() {
|
||||||
None => {
|
None => {
|
||||||
trace!(self.logger, "Got no position");
|
gst_trace!(self.cat, /* TODO obj: demuxer,*/ "Got no position");
|
||||||
*position = u64::MAX;
|
*position = u64::MAX;
|
||||||
glib_ffi::GFALSE
|
glib_ffi::GFALSE
|
||||||
}
|
}
|
||||||
Some(pos) => {
|
Some(pos) => {
|
||||||
trace!(self.logger, "Returning position {}", pos);
|
gst_trace!(
|
||||||
|
self.cat,
|
||||||
|
/* TODO obj: demuxer,*/ "Returning position {}",
|
||||||
|
pos
|
||||||
|
);
|
||||||
*position = pos;
|
*position = pos;
|
||||||
glib_ffi::GTRUE
|
glib_ffi::GTRUE
|
||||||
}
|
}
|
||||||
|
@ -196,12 +208,16 @@ impl DemuxerWrapper {
|
||||||
|
|
||||||
match demuxer.get_duration() {
|
match demuxer.get_duration() {
|
||||||
None => {
|
None => {
|
||||||
trace!(self.logger, "Got no duration");
|
gst_trace!(self.cat, /* TODO obj: demuxer,*/ "Got no duration");
|
||||||
*duration = u64::MAX;
|
*duration = u64::MAX;
|
||||||
glib_ffi::GFALSE
|
glib_ffi::GFALSE
|
||||||
}
|
}
|
||||||
Some(dur) => {
|
Some(dur) => {
|
||||||
trace!(self.logger, "Returning duration {}", dur);
|
gst_trace!(
|
||||||
|
self.cat,
|
||||||
|
/* TODO obj: demuxer,*/ "Returning duration {}",
|
||||||
|
dur
|
||||||
|
);
|
||||||
*duration = dur;
|
*duration = dur;
|
||||||
glib_ffi::GTRUE
|
glib_ffi::GTRUE
|
||||||
}
|
}
|
||||||
|
@ -215,7 +231,12 @@ impl DemuxerWrapper {
|
||||||
|
|
||||||
let stop = if stop == u64::MAX { None } else { Some(stop) };
|
let stop = if stop == u64::MAX { None } else { Some(stop) };
|
||||||
|
|
||||||
debug!(self.logger, "Seeking to {:?}-{:?}", start, stop);
|
gst_debug!(
|
||||||
|
self.cat,
|
||||||
|
/* TODO obj: demuxer,*/ "Seeking to {:?}-{:?}",
|
||||||
|
start,
|
||||||
|
stop
|
||||||
|
);
|
||||||
|
|
||||||
let res = {
|
let res = {
|
||||||
let mut demuxer = &mut self.demuxer.lock().unwrap();
|
let mut demuxer = &mut self.demuxer.lock().unwrap();
|
||||||
|
@ -223,7 +244,11 @@ impl DemuxerWrapper {
|
||||||
match demuxer.seek(start, stop) {
|
match demuxer.seek(start, stop) {
|
||||||
Ok(res) => res,
|
Ok(res) => res,
|
||||||
Err(ref msg) => {
|
Err(ref msg) => {
|
||||||
error!(self.logger, "Failed to seek: {:?}", msg);
|
gst_error!(
|
||||||
|
self.cat,
|
||||||
|
/* TODO obj: demuxer,*/ "Failed to seek: {:?}",
|
||||||
|
msg
|
||||||
|
);
|
||||||
self.post_message(msg);
|
self.post_message(msg);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -232,16 +257,16 @@ impl DemuxerWrapper {
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
SeekResult::TooEarly => {
|
SeekResult::TooEarly => {
|
||||||
debug!(self.logger, "Seeked too early");
|
gst_debug!(self.cat, /* TODO obj: demuxer,*/ "Seeked too early");
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
SeekResult::Ok(off) => {
|
SeekResult::Ok(off) => {
|
||||||
trace!(self.logger, "Seeked successfully");
|
gst_trace!(self.cat, /* TODO obj: demuxer,*/ "Seeked successfully");
|
||||||
*offset = off;
|
*offset = off;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
SeekResult::Eos => {
|
SeekResult::Eos => {
|
||||||
debug!(self.logger, "Seeked after EOS");
|
gst_debug!(self.cat, /* TODO obj: demuxer,*/ "Seeked after EOS");
|
||||||
*offset = u64::MAX;
|
*offset = u64::MAX;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -253,7 +278,7 @@ impl DemuxerWrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_buffer(&self, buffer: GstRc<Buffer>) -> gst_ffi::GstFlowReturn {
|
fn handle_buffer(&self, buffer: gst::Buffer) -> gst::FlowReturn {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn gst_rs_demuxer_stream_eos(raw: *mut gst_ffi::GstElement, index: u32);
|
fn gst_rs_demuxer_stream_eos(raw: *mut gst_ffi::GstElement, index: u32);
|
||||||
fn gst_rs_demuxer_add_stream(
|
fn gst_rs_demuxer_add_stream(
|
||||||
|
@ -279,12 +304,20 @@ impl DemuxerWrapper {
|
||||||
let mut res = {
|
let mut res = {
|
||||||
let mut demuxer = &mut self.demuxer.lock().unwrap();
|
let mut demuxer = &mut self.demuxer.lock().unwrap();
|
||||||
|
|
||||||
trace!(self.logger, "Handling buffer {:?}", buffer);
|
gst_trace!(
|
||||||
|
self.cat,
|
||||||
|
/* TODO obj: demuxer,*/ "Handling buffer {:?}",
|
||||||
|
buffer
|
||||||
|
);
|
||||||
|
|
||||||
match demuxer.handle_buffer(Some(buffer)) {
|
match demuxer.handle_buffer(Some(buffer)) {
|
||||||
Ok(res) => res,
|
Ok(res) => res,
|
||||||
Err(flow_error) => {
|
Err(flow_error) => {
|
||||||
error!(self.logger, "Failed handling buffer: {:?}", flow_error);
|
gst_error!(
|
||||||
|
self.cat,
|
||||||
|
/* TODO obj: demuxer,*/ "Failed handling buffer: {:?}",
|
||||||
|
flow_error
|
||||||
|
);
|
||||||
match flow_error {
|
match flow_error {
|
||||||
FlowError::NotNegotiated(ref msg) | FlowError::Error(ref msg) => {
|
FlowError::NotNegotiated(ref msg) | FlowError::Error(ref msg) => {
|
||||||
self.post_message(msg)
|
self.post_message(msg)
|
||||||
|
@ -298,24 +331,20 @@ impl DemuxerWrapper {
|
||||||
|
|
||||||
// Loop until AllEos, NeedMoreData or error when pushing downstream
|
// Loop until AllEos, NeedMoreData or error when pushing downstream
|
||||||
loop {
|
loop {
|
||||||
trace!(self.logger, "Handled {:?}", res);
|
gst_trace!(self.cat, /* TODO obj: demuxer,*/ "Handled {:?}", res);
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
HandleBufferResult::NeedMoreData => {
|
HandleBufferResult::NeedMoreData => {
|
||||||
return gst_ffi::GST_FLOW_OK;
|
return gst::FlowReturn::Ok;
|
||||||
}
|
}
|
||||||
HandleBufferResult::StreamAdded(stream) => {
|
HandleBufferResult::StreamAdded(stream) => unsafe {
|
||||||
let stream_id_cstr = CString::new(stream.stream_id.as_bytes()).unwrap();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
gst_rs_demuxer_add_stream(
|
gst_rs_demuxer_add_stream(
|
||||||
self.raw,
|
self.raw,
|
||||||
stream.index,
|
stream.index,
|
||||||
stream.caps.as_ptr(),
|
stream.caps.to_glib_none().0,
|
||||||
stream_id_cstr.as_ptr(),
|
stream.stream_id.to_glib_none().0,
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
}
|
|
||||||
HandleBufferResult::HaveAllStreams => unsafe {
|
HandleBufferResult::HaveAllStreams => unsafe {
|
||||||
gst_rs_demuxer_added_all_streams(self.raw);
|
gst_rs_demuxer_added_all_streams(self.raw);
|
||||||
},
|
},
|
||||||
|
@ -323,7 +352,7 @@ impl DemuxerWrapper {
|
||||||
gst_rs_demuxer_stream_format_changed(
|
gst_rs_demuxer_stream_format_changed(
|
||||||
self.raw,
|
self.raw,
|
||||||
stream.index,
|
stream.index,
|
||||||
stream.caps.as_ptr(),
|
stream.caps.to_glib_none().0,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
HandleBufferResult::StreamsChanged(streams) => for stream in streams {
|
HandleBufferResult::StreamsChanged(streams) => for stream in streams {
|
||||||
|
@ -331,20 +360,16 @@ impl DemuxerWrapper {
|
||||||
gst_rs_demuxer_stream_format_changed(
|
gst_rs_demuxer_stream_format_changed(
|
||||||
self.raw,
|
self.raw,
|
||||||
stream.index,
|
stream.index,
|
||||||
stream.caps.as_ptr(),
|
stream.caps.to_glib_none().0,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
HandleBufferResult::BufferForStream(index, buffer) => {
|
HandleBufferResult::BufferForStream(index, buffer) => {
|
||||||
let flow_ret = unsafe {
|
let flow_ret = unsafe {
|
||||||
gst_rs_demuxer_stream_push_buffer(
|
gst_rs_demuxer_stream_push_buffer(self.raw, index, buffer.into_ptr())
|
||||||
self.raw,
|
|
||||||
index,
|
|
||||||
buffer.into_ptr() as *mut gst_ffi::GstBuffer,
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
if flow_ret != gst_ffi::GST_FLOW_OK {
|
if flow_ret != gst_ffi::GST_FLOW_OK {
|
||||||
return flow_ret;
|
return from_glib(flow_ret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HandleBufferResult::Eos(index) => {
|
HandleBufferResult::Eos(index) => {
|
||||||
|
@ -354,21 +379,25 @@ impl DemuxerWrapper {
|
||||||
gst_rs_demuxer_stream_eos(self.raw, index);
|
gst_rs_demuxer_stream_eos(self.raw, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
return gst_ffi::GST_FLOW_EOS;
|
return gst::FlowReturn::Eos;
|
||||||
}
|
}
|
||||||
HandleBufferResult::Again => {
|
HandleBufferResult::Again => {
|
||||||
// nothing, just call again
|
// nothing, just call again
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
trace!(self.logger, "Calling again");
|
gst_trace!(self.cat, /* TODO obj: demuxer,*/ "Calling again");
|
||||||
|
|
||||||
res = {
|
res = {
|
||||||
let mut demuxer = &mut self.demuxer.lock().unwrap();
|
let mut demuxer = &mut self.demuxer.lock().unwrap();
|
||||||
match demuxer.handle_buffer(None) {
|
match demuxer.handle_buffer(None) {
|
||||||
Ok(res) => res,
|
Ok(res) => res,
|
||||||
Err(flow_error) => {
|
Err(flow_error) => {
|
||||||
error!(self.logger, "Failed calling again: {:?}", flow_error);
|
gst_error!(
|
||||||
|
self.cat,
|
||||||
|
/* TODO obj: demuxer,*/ "Failed calling again: {:?}",
|
||||||
|
flow_error
|
||||||
|
);
|
||||||
match flow_error {
|
match flow_error {
|
||||||
FlowError::NotNegotiated(ref msg) | FlowError::Error(ref msg) => {
|
FlowError::NotNegotiated(ref msg) | FlowError::Error(ref msg) => {
|
||||||
self.post_message(msg)
|
self.post_message(msg)
|
||||||
|
@ -385,11 +414,15 @@ impl DemuxerWrapper {
|
||||||
fn end_of_stream(&self) {
|
fn end_of_stream(&self) {
|
||||||
let mut demuxer = &mut self.demuxer.lock().unwrap();
|
let mut demuxer = &mut self.demuxer.lock().unwrap();
|
||||||
|
|
||||||
debug!(self.logger, "End of stream");
|
gst_debug!(self.cat, /* TODO obj: demuxer,*/ "End of stream");
|
||||||
match demuxer.end_of_stream() {
|
match demuxer.end_of_stream() {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(ref msg) => {
|
Err(ref msg) => {
|
||||||
error!(self.logger, "Failed end of stream: {:?}", msg);
|
gst_error!(
|
||||||
|
self.cat,
|
||||||
|
/* TODO obj: demuxer,*/ "Failed end of stream: {:?}",
|
||||||
|
msg
|
||||||
|
);
|
||||||
self.post_message(msg);
|
self.post_message(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -397,7 +430,7 @@ impl DemuxerWrapper {
|
||||||
|
|
||||||
fn post_message(&self, msg: &ErrorMessage) {
|
fn post_message(&self, msg: &ErrorMessage) {
|
||||||
unsafe {
|
unsafe {
|
||||||
msg.post(self.raw);
|
msg.post(&gst::Element::from_glib_borrow(self.raw));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -405,9 +438,9 @@ impl DemuxerWrapper {
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn demuxer_new(
|
pub unsafe extern "C" fn demuxer_new(
|
||||||
demuxer: *mut gst_ffi::GstElement,
|
demuxer: *mut gst_ffi::GstElement,
|
||||||
create_instance: fn(Element) -> Box<Demuxer>,
|
create_instance: fn(&gst::Element) -> Box<Demuxer>,
|
||||||
) -> *mut DemuxerWrapper {
|
) -> *mut DemuxerWrapper {
|
||||||
let instance = create_instance(Element::new(demuxer));
|
let instance = create_instance(&from_glib_borrow(demuxer));
|
||||||
Box::into_raw(Box::new(DemuxerWrapper::new(demuxer, instance)))
|
Box::into_raw(Box::new(DemuxerWrapper::new(demuxer, instance)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,39 +457,48 @@ pub unsafe extern "C" fn demuxer_start(
|
||||||
) -> glib_ffi::gboolean {
|
) -> glib_ffi::gboolean {
|
||||||
let wrap: &DemuxerWrapper = &*ptr;
|
let wrap: &DemuxerWrapper = &*ptr;
|
||||||
|
|
||||||
panic_to_error!(wrap, glib_ffi::GFALSE, {
|
panic_to_error!(
|
||||||
if wrap.start(upstream_size, random_access != glib_ffi::GFALSE) {
|
wrap,
|
||||||
glib_ffi::GTRUE
|
&gst::Element::from_glib_borrow(wrap.raw as *mut _),
|
||||||
} else {
|
false,
|
||||||
glib_ffi::GFALSE
|
{ wrap.start(upstream_size, random_access != glib_ffi::GFALSE) }
|
||||||
}
|
).to_glib()
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn demuxer_stop(ptr: *const DemuxerWrapper) -> glib_ffi::gboolean {
|
pub unsafe extern "C" fn demuxer_stop(ptr: *const DemuxerWrapper) -> glib_ffi::gboolean {
|
||||||
let wrap: &DemuxerWrapper = &*ptr;
|
let wrap: &DemuxerWrapper = &*ptr;
|
||||||
|
|
||||||
panic_to_error!(wrap, glib_ffi::GTRUE, {
|
panic_to_error!(
|
||||||
|
wrap,
|
||||||
|
&gst::Element::from_glib_borrow(wrap.raw as *mut _),
|
||||||
|
glib_ffi::GTRUE,
|
||||||
|
{
|
||||||
if wrap.stop() {
|
if wrap.stop() {
|
||||||
glib_ffi::GTRUE
|
glib_ffi::GTRUE
|
||||||
} else {
|
} else {
|
||||||
glib_ffi::GFALSE
|
glib_ffi::GFALSE
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn demuxer_is_seekable(ptr: *const DemuxerWrapper) -> glib_ffi::gboolean {
|
pub unsafe extern "C" fn demuxer_is_seekable(ptr: *const DemuxerWrapper) -> glib_ffi::gboolean {
|
||||||
let wrap: &DemuxerWrapper = &*ptr;
|
let wrap: &DemuxerWrapper = &*ptr;
|
||||||
|
|
||||||
panic_to_error!(wrap, glib_ffi::GFALSE, {
|
panic_to_error!(
|
||||||
|
wrap,
|
||||||
|
&gst::Element::from_glib_borrow(wrap.raw as *mut _),
|
||||||
|
glib_ffi::GFALSE,
|
||||||
|
{
|
||||||
if wrap.is_seekable() {
|
if wrap.is_seekable() {
|
||||||
glib_ffi::GTRUE
|
glib_ffi::GTRUE
|
||||||
} else {
|
} else {
|
||||||
glib_ffi::GFALSE
|
glib_ffi::GFALSE
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -466,10 +508,15 @@ pub unsafe extern "C" fn demuxer_get_position(
|
||||||
) -> glib_ffi::gboolean {
|
) -> glib_ffi::gboolean {
|
||||||
let wrap: &DemuxerWrapper = &*ptr;
|
let wrap: &DemuxerWrapper = &*ptr;
|
||||||
|
|
||||||
panic_to_error!(wrap, glib_ffi::GFALSE, {
|
panic_to_error!(
|
||||||
|
wrap,
|
||||||
|
&gst::Element::from_glib_borrow(wrap.raw as *mut _),
|
||||||
|
glib_ffi::GFALSE,
|
||||||
|
{
|
||||||
let position = &mut *position;
|
let position = &mut *position;
|
||||||
wrap.get_position(position)
|
wrap.get_position(position)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -479,10 +526,15 @@ pub unsafe extern "C" fn demuxer_get_duration(
|
||||||
) -> glib_ffi::gboolean {
|
) -> glib_ffi::gboolean {
|
||||||
let wrap: &DemuxerWrapper = &*ptr;
|
let wrap: &DemuxerWrapper = &*ptr;
|
||||||
|
|
||||||
panic_to_error!(wrap, glib_ffi::GFALSE, {
|
panic_to_error!(
|
||||||
|
wrap,
|
||||||
|
&gst::Element::from_glib_borrow(wrap.raw as *mut _),
|
||||||
|
glib_ffi::GFALSE,
|
||||||
|
{
|
||||||
let duration = &mut *duration;
|
let duration = &mut *duration;
|
||||||
wrap.get_duration(duration)
|
wrap.get_duration(duration)
|
||||||
})
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -494,7 +546,11 @@ pub unsafe extern "C" fn demuxer_seek(
|
||||||
) -> glib_ffi::gboolean {
|
) -> glib_ffi::gboolean {
|
||||||
let wrap: &mut DemuxerWrapper = &mut *ptr;
|
let wrap: &mut DemuxerWrapper = &mut *ptr;
|
||||||
|
|
||||||
panic_to_error!(wrap, glib_ffi::GFALSE, {
|
panic_to_error!(
|
||||||
|
wrap,
|
||||||
|
&gst::Element::from_glib_borrow(wrap.raw as *mut _),
|
||||||
|
glib_ffi::GFALSE,
|
||||||
|
{
|
||||||
let offset = &mut *offset;
|
let offset = &mut *offset;
|
||||||
|
|
||||||
if wrap.seek(start, stop, offset) {
|
if wrap.seek(start, stop, offset) {
|
||||||
|
@ -502,7 +558,8 @@ pub unsafe extern "C" fn demuxer_seek(
|
||||||
} else {
|
} else {
|
||||||
glib_ffi::GFALSE
|
glib_ffi::GFALSE
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -512,19 +569,26 @@ pub unsafe extern "C" fn demuxer_handle_buffer(
|
||||||
) -> gst_ffi::GstFlowReturn {
|
) -> gst_ffi::GstFlowReturn {
|
||||||
let wrap: &mut DemuxerWrapper = &mut *ptr;
|
let wrap: &mut DemuxerWrapper = &mut *ptr;
|
||||||
|
|
||||||
panic_to_error!(wrap, gst_ffi::GST_FLOW_ERROR, {
|
panic_to_error!(
|
||||||
let buffer = GstRc::from_owned_ptr(buffer);
|
wrap,
|
||||||
wrap.handle_buffer(buffer)
|
&gst::Element::from_glib_borrow(wrap.raw as *mut _),
|
||||||
})
|
gst::FlowReturn::Error,
|
||||||
|
{ wrap.handle_buffer(from_glib_full(buffer)) }
|
||||||
|
).to_glib()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn demuxer_end_of_stream(ptr: *mut DemuxerWrapper) {
|
pub unsafe extern "C" fn demuxer_end_of_stream(ptr: *mut DemuxerWrapper) {
|
||||||
let wrap: &mut DemuxerWrapper = &mut *ptr;
|
let wrap: &mut DemuxerWrapper = &mut *ptr;
|
||||||
|
|
||||||
panic_to_error!(wrap, (), {
|
panic_to_error!(
|
||||||
|
wrap,
|
||||||
|
&gst::Element::from_glib_borrow(wrap.raw as *mut _),
|
||||||
|
(),
|
||||||
|
{
|
||||||
wrap.end_of_stream();
|
wrap.end_of_stream();
|
||||||
})
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DemuxerInfo<'a> {
|
pub struct DemuxerInfo<'a> {
|
||||||
|
@ -534,12 +598,12 @@ pub struct DemuxerInfo<'a> {
|
||||||
pub classification: &'a str,
|
pub classification: &'a str,
|
||||||
pub author: &'a str,
|
pub author: &'a str,
|
||||||
pub rank: i32,
|
pub rank: i32,
|
||||||
pub create_instance: fn(Element) -> Box<Demuxer>,
|
pub create_instance: fn(&gst::Element) -> Box<Demuxer>,
|
||||||
pub input_caps: &'a Caps,
|
pub input_caps: &'a gst::Caps,
|
||||||
pub output_caps: &'a Caps,
|
pub output_caps: &'a gst::Caps,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn demuxer_register(plugin: &Plugin, demuxer_info: &DemuxerInfo) {
|
pub fn demuxer_register(plugin: &gst::Plugin, demuxer_info: &DemuxerInfo) {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn gst_rs_demuxer_register(
|
fn gst_rs_demuxer_register(
|
||||||
plugin: *const gst_ffi::GstPlugin,
|
plugin: *const gst_ffi::GstPlugin,
|
||||||
|
@ -563,7 +627,7 @@ pub fn demuxer_register(plugin: &Plugin, demuxer_info: &DemuxerInfo) {
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
gst_rs_demuxer_register(
|
gst_rs_demuxer_register(
|
||||||
plugin.as_ptr(),
|
plugin.to_glib_none().0,
|
||||||
cname.as_ptr(),
|
cname.as_ptr(),
|
||||||
clong_name.as_ptr(),
|
clong_name.as_ptr(),
|
||||||
cdescription.as_ptr(),
|
cdescription.as_ptr(),
|
||||||
|
@ -571,8 +635,8 @@ pub fn demuxer_register(plugin: &Plugin, demuxer_info: &DemuxerInfo) {
|
||||||
cauthor.as_ptr(),
|
cauthor.as_ptr(),
|
||||||
demuxer_info.rank,
|
demuxer_info.rank,
|
||||||
demuxer_info.create_instance as *const c_void,
|
demuxer_info.create_instance as *const c_void,
|
||||||
demuxer_info.input_caps.as_ptr(),
|
demuxer_info.input_caps.to_glib_none().0,
|
||||||
demuxer_info.output_caps.as_ptr(),
|
demuxer_info.output_caps.to_glib_none().0,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,6 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use std::ffi::CString;
|
|
||||||
use std::ptr;
|
|
||||||
use libc::c_char;
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::fmt::Error as FmtError;
|
use std::fmt::Error as FmtError;
|
||||||
|
@ -19,6 +16,11 @@ use url::Url;
|
||||||
use glib_ffi;
|
use glib_ffi;
|
||||||
use gst_ffi;
|
use gst_ffi;
|
||||||
|
|
||||||
|
use glib;
|
||||||
|
use glib::translate::ToGlibPtr;
|
||||||
|
use gst;
|
||||||
|
use gst::prelude::*;
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! error_msg(
|
macro_rules! error_msg(
|
||||||
// Plain strings
|
// Plain strings
|
||||||
|
@ -57,31 +59,19 @@ macro_rules! error_msg(
|
||||||
}};
|
}};
|
||||||
);
|
);
|
||||||
|
|
||||||
pub trait ToGError {
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
fn to_gerror(&self) -> (u32, i32);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn gst_library_error_domain() -> glib_ffi::GQuark {
|
|
||||||
unsafe { gst_ffi::gst_library_error_quark() }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn gst_resource_error_domain() -> glib_ffi::GQuark {
|
|
||||||
unsafe { gst_ffi::gst_resource_error_quark() }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ErrorMessage {
|
pub struct ErrorMessage {
|
||||||
pub error_domain: u32,
|
error_domain: glib_ffi::GQuark,
|
||||||
pub error_code: i32,
|
error_code: i32,
|
||||||
pub message: Option<String>,
|
message: Option<String>,
|
||||||
pub debug: Option<String>,
|
debug: Option<String>,
|
||||||
pub filename: &'static str,
|
filename: &'static str,
|
||||||
pub function: &'static str,
|
function: &'static str,
|
||||||
pub line: u32,
|
line: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ErrorMessage {
|
impl ErrorMessage {
|
||||||
pub fn new<T: ToGError>(
|
pub fn new<T: gst::MessageErrorDomain>(
|
||||||
error: &T,
|
error: &T,
|
||||||
message: Option<Cow<str>>,
|
message: Option<Cow<str>>,
|
||||||
debug: Option<Cow<str>>,
|
debug: Option<Cow<str>>,
|
||||||
|
@ -89,11 +79,12 @@ impl ErrorMessage {
|
||||||
function: &'static str,
|
function: &'static str,
|
||||||
line: u32,
|
line: u32,
|
||||||
) -> ErrorMessage {
|
) -> ErrorMessage {
|
||||||
let (gdomain, gcode) = error.to_gerror();
|
let domain = T::domain();
|
||||||
|
let code = error.code();
|
||||||
|
|
||||||
ErrorMessage {
|
ErrorMessage {
|
||||||
error_domain: gdomain,
|
error_domain: domain,
|
||||||
error_code: gcode,
|
error_code: code,
|
||||||
message: message.map(|m| m.into_owned()),
|
message: message.map(|m| m.into_owned()),
|
||||||
debug: debug.map(|d| d.into_owned()),
|
debug: debug.map(|d| d.into_owned()),
|
||||||
filename: filename,
|
filename: filename,
|
||||||
|
@ -102,8 +93,7 @@ impl ErrorMessage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn post<E: IsA<gst::Element>>(&self, element: &E) {
|
||||||
pub unsafe fn post(&self, element: *mut gst_ffi::GstElement) {
|
|
||||||
let ErrorMessage {
|
let ErrorMessage {
|
||||||
error_domain,
|
error_domain,
|
||||||
error_code,
|
error_code,
|
||||||
|
@ -114,33 +104,23 @@ impl ErrorMessage {
|
||||||
line,
|
line,
|
||||||
} = *self;
|
} = *self;
|
||||||
|
|
||||||
let message_ptr = message.as_ref().map_or(ptr::null(), |m| m.as_ptr()) as *const c_char;
|
unsafe {
|
||||||
let message_len = message.as_ref().map_or(0, |m| m.len());
|
|
||||||
|
|
||||||
let debug_ptr = debug.as_ref().map_or(ptr::null(), |m| m.as_ptr()) as *const c_char;
|
|
||||||
let debug_len = debug.as_ref().map_or(0, |m| m.len());
|
|
||||||
|
|
||||||
let file_cstr = CString::new(filename.as_bytes()).unwrap();
|
|
||||||
let file_ptr = file_cstr.as_ptr();
|
|
||||||
|
|
||||||
let function_cstr = CString::new(function.as_bytes()).unwrap();
|
|
||||||
let function_ptr = function_cstr.as_ptr();
|
|
||||||
|
|
||||||
gst_ffi::gst_element_message_full(
|
gst_ffi::gst_element_message_full(
|
||||||
element,
|
element.to_glib_none().0,
|
||||||
gst_ffi::GST_MESSAGE_ERROR,
|
gst_ffi::GST_MESSAGE_ERROR,
|
||||||
error_domain,
|
error_domain,
|
||||||
error_code,
|
error_code,
|
||||||
glib_ffi::g_strndup(message_ptr, message_len),
|
message.to_glib_none().0,
|
||||||
glib_ffi::g_strndup(debug_ptr, debug_len),
|
debug.to_glib_none().0,
|
||||||
file_ptr,
|
filename.to_glib_none().0,
|
||||||
function_ptr,
|
function.to_glib_none().0,
|
||||||
line as i32,
|
line as i32,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum FlowError {
|
pub enum FlowError {
|
||||||
Flushing,
|
Flushing,
|
||||||
Eos,
|
Eos,
|
||||||
|
@ -149,12 +129,12 @@ pub enum FlowError {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FlowError {
|
impl FlowError {
|
||||||
pub fn to_native(&self) -> gst_ffi::GstFlowReturn {
|
pub fn to_native(&self) -> gst::FlowReturn {
|
||||||
match *self {
|
match *self {
|
||||||
FlowError::Flushing => gst_ffi::GST_FLOW_FLUSHING,
|
FlowError::Flushing => gst::FlowReturn::Flushing,
|
||||||
FlowError::Eos => gst_ffi::GST_FLOW_EOS,
|
FlowError::Eos => gst::FlowReturn::Eos,
|
||||||
FlowError::NotNegotiated(..) => gst_ffi::GST_FLOW_NOT_NEGOTIATED,
|
FlowError::NotNegotiated(..) => gst::FlowReturn::NotNegotiated,
|
||||||
FlowError::Error(..) => gst_ffi::GST_FLOW_ERROR,
|
FlowError::Error(..) => gst::FlowReturn::Error,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,95 +170,60 @@ impl Error for FlowError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum UriErrorKind {
|
|
||||||
UnsupportedProtocol = 0,
|
|
||||||
BadUri,
|
|
||||||
BadState,
|
|
||||||
BadReference,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct UriError {
|
pub struct UriError {
|
||||||
error_kind: UriErrorKind,
|
error: gst::URIError,
|
||||||
message: Option<String>,
|
message: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UriError {
|
impl UriError {
|
||||||
pub fn new(error_kind: UriErrorKind, message: Option<String>) -> UriError {
|
pub fn new(error: gst::URIError, message: String) -> UriError {
|
||||||
UriError {
|
UriError {
|
||||||
error_kind: error_kind,
|
error: error,
|
||||||
message: message,
|
message: message,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn message(&self) -> &Option<String> {
|
pub fn message(&self) -> &str {
|
||||||
&self.message
|
&self.message
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn kind(&self) -> &UriErrorKind {
|
pub fn error(&self) -> gst::URIError {
|
||||||
&self.error_kind
|
self.error
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn into_gerror(self, err: *mut *mut glib_ffi::GError) {
|
pub fn into_error(self) -> glib::Error {
|
||||||
if let Some(msg) = self.message {
|
glib::Error::new(self.error, &self.message)
|
||||||
let cmsg = CString::new(msg.as_str()).unwrap();
|
|
||||||
glib_ffi::g_set_error_literal(
|
|
||||||
err,
|
|
||||||
gst_ffi::gst_uri_error_quark(),
|
|
||||||
self.error_kind as i32,
|
|
||||||
cmsg.as_ptr(),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
glib_ffi::g_set_error_literal(
|
|
||||||
err,
|
|
||||||
gst_ffi::gst_uri_error_quark(),
|
|
||||||
self.error_kind as i32,
|
|
||||||
ptr::null(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for UriError {
|
impl Display for UriError {
|
||||||
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
|
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
|
||||||
match self.message {
|
f.write_fmt(format_args!("{}: {}", self.description(), self.message))
|
||||||
None => f.write_str(self.description()),
|
|
||||||
Some(ref message) => f.write_fmt(format_args!("{}: {}", self.description(), message)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error for UriError {
|
impl Error for UriError {
|
||||||
fn description(&self) -> &str {
|
fn description(&self) -> &str {
|
||||||
match self.error_kind {
|
match self.error {
|
||||||
UriErrorKind::UnsupportedProtocol => "Unsupported protocol",
|
gst::URIError::UnsupportedProtocol => "Unsupported protocol",
|
||||||
UriErrorKind::BadUri => "Bad URI",
|
gst::URIError::BadUri => "Bad URI",
|
||||||
UriErrorKind::BadState => "Bad State",
|
gst::URIError::BadState => "Bad State",
|
||||||
UriErrorKind::BadReference => "Bad Reference",
|
gst::URIError::BadReference => "Bad Reference",
|
||||||
|
_ => "Unknown",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type UriValidator = Fn(&Url) -> Result<(), UriError>;
|
pub type UriValidator = Fn(&Url) -> Result<(), UriError>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct PanicError;
|
|
||||||
|
|
||||||
impl ToGError for PanicError {
|
|
||||||
fn to_gerror(&self) -> (u32, i32) {
|
|
||||||
(gst_library_error_domain(), 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! panic_to_error(
|
macro_rules! panic_to_error(
|
||||||
($wrap:expr, $ret:expr, $code:block) => {{
|
($wrap:expr, $element:expr, $ret:expr, $code:block) => {{
|
||||||
if $wrap.panicked.load(Ordering::Relaxed) {
|
if $wrap.panicked.load(Ordering::Relaxed) {
|
||||||
error_msg!(PanicError, ["Panicked"]).post($wrap.raw);
|
error_msg!(gst::LibraryError::Failed, ["Panicked"]).post($element);
|
||||||
return $ret;
|
$ret
|
||||||
}
|
} else {
|
||||||
|
|
||||||
let result = panic::catch_unwind(AssertUnwindSafe(|| $code));
|
let result = panic::catch_unwind(AssertUnwindSafe(|| $code));
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
|
@ -286,14 +231,15 @@ macro_rules! panic_to_error(
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
$wrap.panicked.store(true, Ordering::Relaxed);
|
$wrap.panicked.store(true, Ordering::Relaxed);
|
||||||
if let Some(cause) = err.downcast_ref::<&str>() {
|
if let Some(cause) = err.downcast_ref::<&str>() {
|
||||||
error_msg!(PanicError, ["Panicked: {}", cause]).post($wrap.raw);
|
error_msg!(gst::LibraryError::Failed, ["Panicked: {}", cause]).post($element);
|
||||||
} else if let Some(cause) = err.downcast_ref::<String>() {
|
} else if let Some(cause) = err.downcast_ref::<String>() {
|
||||||
error_msg!(PanicError, ["Panicked: {}", cause]).post($wrap.raw);
|
error_msg!(gst::LibraryError::Failed, ["Panicked: {}", cause]).post($element);
|
||||||
} else {
|
} else {
|
||||||
error_msg!(PanicError, ["Panicked"]).post($wrap.raw);
|
error_msg!(gst::LibraryError::Failed, ["Panicked"]).post($element);
|
||||||
}
|
}
|
||||||
$ret
|
$ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}
|
||||||
|
}};
|
||||||
);
|
);
|
||||||
|
|
|
@ -7,48 +7,30 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate bitflags;
|
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
#[macro_use]
|
extern crate gstreamer_base_sys as gst_base_ffi;
|
||||||
extern crate derivative;
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
extern crate num_rational;
|
|
||||||
#[macro_use]
|
|
||||||
extern crate slog;
|
|
||||||
extern crate url;
|
extern crate url;
|
||||||
pub extern crate glib_sys as glib_ffi;
|
pub extern crate glib_sys as glib_ffi;
|
||||||
pub extern crate gobject_sys as gobject_ffi;
|
pub extern crate gobject_sys as gobject_ffi;
|
||||||
pub extern crate gstreamer_base_sys as gst_base_ffi;
|
|
||||||
pub extern crate gstreamer_sys as gst_ffi;
|
pub extern crate gstreamer_sys as gst_ffi;
|
||||||
|
|
||||||
pub extern crate glib as glib;
|
extern crate gstreamer_base as gst_base;
|
||||||
|
#[macro_use]
|
||||||
|
pub extern crate glib;
|
||||||
|
#[macro_use]
|
||||||
pub extern crate gstreamer as gst;
|
pub extern crate gstreamer as gst;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod buffer;
|
|
||||||
pub mod adapter;
|
pub mod adapter;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod plugin;
|
pub mod plugin;
|
||||||
pub mod source;
|
pub mod source;
|
||||||
pub mod sink;
|
pub mod sink;
|
||||||
pub mod demuxer;
|
pub mod demuxer;
|
||||||
pub mod log;
|
|
||||||
pub mod value;
|
|
||||||
pub mod caps;
|
|
||||||
pub mod bytes;
|
pub mod bytes;
|
||||||
pub mod tags;
|
|
||||||
pub mod streams;
|
|
||||||
pub mod miniobject;
|
|
||||||
pub mod structure;
|
|
||||||
|
|
||||||
pub mod ffi {
|
|
||||||
pub use glib_ffi as glib;
|
|
||||||
pub use gobject_ffi as gobject;
|
|
||||||
pub use gst_ffi as gst;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,131 +0,0 @@
|
||||||
// Copyright (C) 2016-2017 Sebastian Dröge <sebastian@centricular.com>
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
use libc::c_char;
|
|
||||||
use std::ffi::CString;
|
|
||||||
use slog::{Drain, Level, Never, OwnedKVList, Record};
|
|
||||||
use std::fmt;
|
|
||||||
use std::ptr;
|
|
||||||
use std::mem;
|
|
||||||
|
|
||||||
use utils::Element;
|
|
||||||
|
|
||||||
use gobject_ffi;
|
|
||||||
use gst_ffi;
|
|
||||||
|
|
||||||
pub struct GstDebugDrain {
|
|
||||||
category: *mut gst_ffi::GstDebugCategory,
|
|
||||||
element: Box<gobject_ffi::GWeakRef>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GstDebugDrain {
|
|
||||||
pub fn new(
|
|
||||||
element: Option<&Element>,
|
|
||||||
name: &str,
|
|
||||||
color: u32,
|
|
||||||
description: &str,
|
|
||||||
) -> GstDebugDrain {
|
|
||||||
extern "C" {
|
|
||||||
fn _gst_debug_category_new(
|
|
||||||
name: *const c_char,
|
|
||||||
color: u32,
|
|
||||||
description: *const c_char,
|
|
||||||
) -> *mut gst_ffi::GstDebugCategory;
|
|
||||||
}
|
|
||||||
|
|
||||||
let name_cstr = CString::new(name.as_bytes()).unwrap();
|
|
||||||
let description_cstr = CString::new(description.as_bytes()).unwrap();
|
|
||||||
|
|
||||||
// Gets the category if it exists already
|
|
||||||
let category = unsafe {
|
|
||||||
_gst_debug_category_new(name_cstr.as_ptr(), color, description_cstr.as_ptr())
|
|
||||||
};
|
|
||||||
|
|
||||||
let element = match element {
|
|
||||||
Some(element) => unsafe { element.as_ptr() },
|
|
||||||
None => ptr::null(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut drain = GstDebugDrain {
|
|
||||||
category: category,
|
|
||||||
element: Box::new(unsafe { mem::zeroed() }),
|
|
||||||
};
|
|
||||||
|
|
||||||
if !element.is_null() {
|
|
||||||
unsafe {
|
|
||||||
gobject_ffi::g_weak_ref_set(
|
|
||||||
&mut *drain.element,
|
|
||||||
element as *mut gobject_ffi::GObject,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
drain
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for GstDebugDrain {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
gobject_ffi::g_weak_ref_clear(&mut *self.element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drain for GstDebugDrain {
|
|
||||||
type Ok = ();
|
|
||||||
type Err = Never;
|
|
||||||
|
|
||||||
fn log(&self, record: &Record, _: &OwnedKVList) -> Result<(), Never> {
|
|
||||||
let level = match record.level() {
|
|
||||||
Level::Critical | Level::Error => gst_ffi::GST_LEVEL_ERROR,
|
|
||||||
Level::Warning => gst_ffi::GST_LEVEL_WARNING,
|
|
||||||
Level::Info => gst_ffi::GST_LEVEL_INFO,
|
|
||||||
Level::Debug => gst_ffi::GST_LEVEL_DEBUG,
|
|
||||||
Level::Trace => gst_ffi::GST_LEVEL_TRACE,
|
|
||||||
};
|
|
||||||
|
|
||||||
let threshold = unsafe { gst_ffi::gst_debug_category_get_threshold(self.category) };
|
|
||||||
|
|
||||||
if level as u32 > threshold as u32 {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let file_cstr = CString::new(record.file().as_bytes()).unwrap();
|
|
||||||
|
|
||||||
// TODO: Probably want to include module?
|
|
||||||
let function_cstr = CString::new(record.function().as_bytes()).unwrap();
|
|
||||||
|
|
||||||
let message_cstr = CString::new(fmt::format(*record.msg()).as_bytes()).unwrap();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let element = gobject_ffi::g_weak_ref_get(
|
|
||||||
&*self.element as *const gobject_ffi::GWeakRef as *mut gobject_ffi::GWeakRef,
|
|
||||||
);
|
|
||||||
|
|
||||||
gst_ffi::gst_debug_log(
|
|
||||||
self.category,
|
|
||||||
level,
|
|
||||||
file_cstr.as_ptr(),
|
|
||||||
function_cstr.as_ptr(),
|
|
||||||
record.line() as i32,
|
|
||||||
element as *mut gobject_ffi::GObject,
|
|
||||||
message_cstr.as_ptr(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if !element.is_null() {
|
|
||||||
gst_ffi::gst_object_unref(element as *mut gst_ffi::GstObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Sync for GstDebugDrain {}
|
|
||||||
unsafe impl Send for GstDebugDrain {}
|
|
|
@ -1,158 +0,0 @@
|
||||||
// Copyright (C) 2016-2017 Sebastian Dröge <sebastian@centricular.com>
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
use std::{borrow, fmt, ops};
|
|
||||||
use std::mem;
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use glib_ffi;
|
|
||||||
use gst_ffi;
|
|
||||||
|
|
||||||
#[derive(Hash, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
pub struct GstRc<T: MiniObject>(*mut T, PhantomData<T>);
|
|
||||||
|
|
||||||
impl<T: MiniObject> GstRc<T> {
|
|
||||||
unsafe fn new(obj: *const T, owned: bool) -> Self {
|
|
||||||
assert!(!obj.is_null());
|
|
||||||
|
|
||||||
if !owned {
|
|
||||||
gst_ffi::gst_mini_object_ref((&*obj).as_ptr() as *mut gst_ffi::GstMiniObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
GstRc(obj as *mut T, PhantomData)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn from_owned_ptr(ptr: *const T::PtrType) -> Self {
|
|
||||||
Self::new(T::from_ptr(ptr), true)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn from_unowned_ptr(ptr: *const T::PtrType) -> Self {
|
|
||||||
Self::new(T::from_ptr(ptr), false)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn make_mut(&mut self) -> &mut T {
|
|
||||||
unsafe {
|
|
||||||
if self.is_writable() {
|
|
||||||
return &mut *self.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.0 = T::from_mut_ptr(gst_ffi::gst_mini_object_make_writable(
|
|
||||||
self.as_mut_ptr() as *mut gst_ffi::GstMiniObject,
|
|
||||||
) as *mut T::PtrType);
|
|
||||||
assert!(self.is_writable());
|
|
||||||
|
|
||||||
&mut *self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_mut(&mut self) -> Option<&mut T> {
|
|
||||||
if self.is_writable() {
|
|
||||||
Some(unsafe { &mut *self.0 })
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn copy(&self) -> Self {
|
|
||||||
unsafe {
|
|
||||||
GstRc::from_owned_ptr(
|
|
||||||
gst_ffi::gst_mini_object_copy(self.as_ptr() as *const gst_ffi::GstMiniObject) as
|
|
||||||
*const T::PtrType,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_writable(&self) -> bool {
|
|
||||||
(unsafe {
|
|
||||||
gst_ffi::gst_mini_object_is_writable(self.as_ptr() as *const gst_ffi::GstMiniObject)
|
|
||||||
} == glib_ffi::GTRUE)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn into_ptr(self) -> *mut T::PtrType {
|
|
||||||
let ptr = self.as_mut_ptr();
|
|
||||||
mem::forget(self);
|
|
||||||
|
|
||||||
ptr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: MiniObject> ops::Deref for GstRc<T> {
|
|
||||||
type Target = T;
|
|
||||||
fn deref(&self) -> &T {
|
|
||||||
self.as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: MiniObject> AsRef<T> for GstRc<T> {
|
|
||||||
fn as_ref(&self) -> &T {
|
|
||||||
unsafe { &*self.0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: MiniObject> borrow::Borrow<T> for GstRc<T> {
|
|
||||||
fn borrow(&self) -> &T {
|
|
||||||
self.as_ref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: Not generally possible because neither T nor ToOwned are defined here...
|
|
||||||
//impl<T: MiniObject> ToOwned for T {
|
|
||||||
// type Owned = GstRc<T>;
|
|
||||||
//
|
|
||||||
// fn to_owned(&self) -> GstRc<T> {
|
|
||||||
// unsafe { GstRc::from_unowned_ptr(self.as_ptr()) }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
impl<T: MiniObject> Clone for GstRc<T> {
|
|
||||||
fn clone(&self) -> GstRc<T> {
|
|
||||||
unsafe { GstRc::from_unowned_ptr(self.as_ptr()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: MiniObject> Drop for GstRc<T> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
gst_ffi::gst_mini_object_unref(self.as_ptr() as *mut gst_ffi::GstMiniObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<T: MiniObject + Sync> Sync for GstRc<T> {}
|
|
||||||
unsafe impl<T: MiniObject + Send> Send for GstRc<T> {}
|
|
||||||
|
|
||||||
impl<T: MiniObject + fmt::Display> fmt::Display for GstRc<T> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
(unsafe { &*self.0 }).fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe trait MiniObject
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
type PtrType;
|
|
||||||
|
|
||||||
unsafe fn as_ptr(&self) -> *const Self::PtrType {
|
|
||||||
self as *const Self as *const Self::PtrType
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn as_mut_ptr(&self) -> *mut Self::PtrType {
|
|
||||||
self as *const Self as *mut Self::PtrType
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn from_ptr<'a>(ptr: *const Self::PtrType) -> &'a Self {
|
|
||||||
assert!(!ptr.is_null());
|
|
||||||
&*(ptr as *const Self)
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn from_mut_ptr<'a>(ptr: *mut Self::PtrType) -> &'a mut Self {
|
|
||||||
assert!(!ptr.is_null());
|
|
||||||
&mut *(ptr as *mut Self)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,39 +6,25 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use gst_ffi;
|
|
||||||
|
|
||||||
pub struct Plugin(*mut gst_ffi::GstPlugin);
|
|
||||||
|
|
||||||
impl Plugin {
|
|
||||||
pub unsafe fn new(plugin: *mut gst_ffi::GstPlugin) -> Plugin {
|
|
||||||
Plugin(plugin)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn as_ptr(&self) -> *mut gst_ffi::GstPlugin {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! plugin_define(
|
macro_rules! plugin_define(
|
||||||
($name:expr, $description:expr, $plugin_init:ident,
|
($name:expr, $description:expr, $plugin_init:ident,
|
||||||
$version:expr, $license:expr, $source:expr,
|
$version:expr, $license:expr, $source:expr,
|
||||||
$package:expr, $origin:expr, $release_datetime:expr) => {
|
$package:expr, $origin:expr, $release_datetime:expr) => {
|
||||||
pub mod plugin_desc {
|
pub mod plugin_desc {
|
||||||
use $crate::plugin::Plugin;
|
use $crate::glib::translate::{from_glib_borrow, ToGlib};
|
||||||
|
|
||||||
// Not using c_char here because it requires the libc crate
|
// Not using c_char here because it requires the libc crate
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
type c_char = i8;
|
type c_char = i8;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct GstPluginDesc($crate::ffi::gst::GstPluginDesc);
|
pub struct GstPluginDesc($crate::gst_ffi::GstPluginDesc);
|
||||||
unsafe impl Sync for GstPluginDesc {}
|
unsafe impl Sync for GstPluginDesc {}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
pub static gst_plugin_desc: GstPluginDesc = GstPluginDesc($crate::ffi::gst::GstPluginDesc {
|
pub static gst_plugin_desc: GstPluginDesc = GstPluginDesc($crate::gst_ffi::GstPluginDesc {
|
||||||
major_version: 1,
|
major_version: 1,
|
||||||
minor_version: 10,
|
minor_version: 10,
|
||||||
name: $name as *const u8 as *const c_char,
|
name: $name as *const u8 as *const c_char,
|
||||||
|
@ -50,15 +36,11 @@ macro_rules! plugin_define(
|
||||||
package: $package as *const u8 as *const c_char,
|
package: $package as *const u8 as *const c_char,
|
||||||
origin: $origin as *const u8 as *const c_char,
|
origin: $origin as *const u8 as *const c_char,
|
||||||
release_datetime: $release_datetime as *const u8 as *const c_char,
|
release_datetime: $release_datetime as *const u8 as *const c_char,
|
||||||
_gst_reserved: [0 as $crate::ffi::glib::gpointer; 4],
|
_gst_reserved: [0 as $crate::glib_ffi::gpointer; 4],
|
||||||
});
|
});
|
||||||
|
|
||||||
unsafe extern "C" fn plugin_init_trampoline(plugin: *mut $crate::ffi::gst::GstPlugin) -> $crate::ffi::glib::gboolean {
|
unsafe extern "C" fn plugin_init_trampoline(plugin: *mut $crate::gst_ffi::GstPlugin) -> $crate::glib_ffi::gboolean {
|
||||||
if super::$plugin_init(&Plugin::new(plugin)) {
|
super::$plugin_init(&from_glib_borrow(plugin)).to_glib()
|
||||||
$crate::ffi::glib::GTRUE
|
|
||||||
} else {
|
|
||||||
$crate::ffi::glib::GFALSE
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,45 +19,21 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use slog::Logger;
|
|
||||||
|
|
||||||
use utils::*;
|
|
||||||
use error::*;
|
use error::*;
|
||||||
use buffer::*;
|
|
||||||
use miniobject::*;
|
|
||||||
use log::*;
|
|
||||||
use plugin::Plugin;
|
|
||||||
use caps::*;
|
|
||||||
|
|
||||||
use glib_ffi;
|
use glib_ffi;
|
||||||
use gobject_ffi;
|
use gobject_ffi;
|
||||||
use gst_ffi;
|
use gst_ffi;
|
||||||
use gst_base_ffi;
|
use gst_base_ffi;
|
||||||
|
|
||||||
#[derive(Debug)]
|
use glib::translate::*;
|
||||||
pub enum SinkError {
|
use gst;
|
||||||
Failure,
|
use gst::prelude::*;
|
||||||
OpenFailed,
|
use gst_base;
|
||||||
NotFound,
|
use gst_base::prelude::*;
|
||||||
WriteFailed,
|
|
||||||
SeekFailed,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToGError for SinkError {
|
|
||||||
fn to_gerror(&self) -> (u32, i32) {
|
|
||||||
match *self {
|
|
||||||
SinkError::Failure => (gst_library_error_domain(), 1),
|
|
||||||
SinkError::OpenFailed => (gst_resource_error_domain(), 6),
|
|
||||||
SinkError::NotFound => (gst_resource_error_domain(), 3),
|
|
||||||
SinkError::WriteFailed => (gst_resource_error_domain(), 10),
|
|
||||||
SinkError::SeekFailed => (gst_resource_error_domain(), 11),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SinkWrapper {
|
pub struct SinkWrapper {
|
||||||
raw: *mut gst_ffi::GstElement,
|
cat: gst::DebugCategory,
|
||||||
logger: Logger,
|
|
||||||
uri: Mutex<(Option<Url>, bool)>,
|
uri: Mutex<(Option<Url>, bool)>,
|
||||||
uri_validator: Box<UriValidator>,
|
uri_validator: Box<UriValidator>,
|
||||||
sink: Mutex<Box<Sink>>,
|
sink: Mutex<Box<Sink>>,
|
||||||
|
@ -67,25 +43,20 @@ pub struct SinkWrapper {
|
||||||
pub trait Sink {
|
pub trait Sink {
|
||||||
fn uri_validator(&self) -> Box<UriValidator>;
|
fn uri_validator(&self) -> Box<UriValidator>;
|
||||||
|
|
||||||
fn start(&mut self, uri: Url) -> Result<(), ErrorMessage>;
|
fn start(&mut self, sink: &RsSinkWrapper, uri: Url) -> Result<(), ErrorMessage>;
|
||||||
fn stop(&mut self) -> Result<(), ErrorMessage>;
|
fn stop(&mut self, sink: &RsSinkWrapper) -> Result<(), ErrorMessage>;
|
||||||
|
|
||||||
fn render(&mut self, buffer: &Buffer) -> Result<(), FlowError>;
|
fn render(&mut self, sink: &RsSinkWrapper, buffer: &gst::BufferRef) -> Result<(), FlowError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SinkWrapper {
|
impl SinkWrapper {
|
||||||
fn new(raw: *mut gst_ffi::GstElement, sink: Box<Sink>) -> SinkWrapper {
|
fn new(sink: Box<Sink>) -> SinkWrapper {
|
||||||
SinkWrapper {
|
SinkWrapper {
|
||||||
raw: raw,
|
cat: gst::DebugCategory::new(
|
||||||
logger: Logger::root(
|
|
||||||
GstDebugDrain::new(
|
|
||||||
Some(unsafe { &Element::new(raw) }),
|
|
||||||
"rssink",
|
"rssink",
|
||||||
0,
|
gst::DebugColorFlags::empty(),
|
||||||
"Rust sink base class",
|
"Rust sink base class",
|
||||||
),
|
),
|
||||||
o!(),
|
|
||||||
),
|
|
||||||
uri: Mutex::new((None, false)),
|
uri: Mutex::new((None, false)),
|
||||||
uri_validator: sink.uri_validator(),
|
uri_validator: sink.uri_validator(),
|
||||||
sink: Mutex::new(sink),
|
sink: Mutex::new(sink),
|
||||||
|
@ -93,15 +64,15 @@ impl SinkWrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_uri(&self, uri_str: Option<&str>) -> Result<(), UriError> {
|
fn set_uri(&self, sink: &RsSinkWrapper, uri_str: Option<&str>) -> Result<(), UriError> {
|
||||||
let uri_storage = &mut self.uri.lock().unwrap();
|
let uri_storage = &mut self.uri.lock().unwrap();
|
||||||
|
|
||||||
debug!(self.logger, "Setting URI {:?}", uri_str);
|
gst_debug!(self.cat, obj: sink, "Setting URI {:?}", uri_str);
|
||||||
|
|
||||||
if uri_storage.1 {
|
if uri_storage.1 {
|
||||||
return Err(UriError::new(
|
return Err(UriError::new(
|
||||||
UriErrorKind::BadState,
|
gst::URIError::BadState,
|
||||||
Some("Already started".to_string()),
|
"Already started".to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,8 +86,8 @@ impl SinkWrapper {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(err) => Err(UriError::new(
|
Err(err) => Err(UriError::new(
|
||||||
UriErrorKind::BadUri,
|
gst::URIError::BadUri,
|
||||||
Some(format!("Failed to parse URI '{}': {}", uri_str, err)),
|
format!("Failed to parse URI '{}': {}", uri_str, err),
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -124,13 +95,13 @@ impl SinkWrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_uri(&self) -> Option<String> {
|
fn get_uri(&self, _sink: &RsSinkWrapper) -> Option<String> {
|
||||||
let uri_storage = &self.uri.lock().unwrap();
|
let uri_storage = &self.uri.lock().unwrap();
|
||||||
uri_storage.0.as_ref().map(|uri| String::from(uri.as_str()))
|
uri_storage.0.as_ref().map(|uri| String::from(uri.as_str()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start(&self) -> bool {
|
fn start(&self, sink: &RsSinkWrapper) -> bool {
|
||||||
debug!(self.logger, "Starting");
|
gst_debug!(self.cat, obj: sink, "Starting");
|
||||||
|
|
||||||
// Don't keep the URI locked while we call start later
|
// Don't keep the URI locked while we call start later
|
||||||
let uri = match *self.uri.lock().unwrap() {
|
let uri = match *self.uri.lock().unwrap() {
|
||||||
|
@ -139,60 +110,63 @@ impl SinkWrapper {
|
||||||
uri.clone()
|
uri.clone()
|
||||||
}
|
}
|
||||||
(None, _) => {
|
(None, _) => {
|
||||||
error!(self.logger, "No URI given");
|
gst_error!(self.cat, obj: sink, "No URI given");
|
||||||
self.post_message(&error_msg!(SinkError::OpenFailed, ["No URI given"]));
|
self.post_message(
|
||||||
|
sink,
|
||||||
|
&error_msg!(gst::ResourceError::OpenWrite, ["No URI given"]),
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let sink = &mut self.sink.lock().unwrap();
|
let sink_impl = &mut self.sink.lock().unwrap();
|
||||||
match sink.start(uri) {
|
match sink_impl.start(sink, uri) {
|
||||||
Ok(..) => {
|
Ok(..) => {
|
||||||
trace!(self.logger, "Started successfully");
|
gst_trace!(self.cat, obj: sink, "Started successfully");
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Err(ref msg) => {
|
Err(ref msg) => {
|
||||||
error!(self.logger, "Failed to start: {:?}", msg);
|
gst_error!(self.cat, obj: sink, "Failed to start: {:?}", msg);
|
||||||
|
|
||||||
self.uri.lock().unwrap().1 = false;
|
self.uri.lock().unwrap().1 = false;
|
||||||
self.post_message(msg);
|
self.post_message(sink, msg);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop(&self) -> bool {
|
fn stop(&self, sink: &RsSinkWrapper) -> bool {
|
||||||
let sink = &mut self.sink.lock().unwrap();
|
let sink_impl = &mut self.sink.lock().unwrap();
|
||||||
|
|
||||||
debug!(self.logger, "Stopping");
|
gst_debug!(self.cat, obj: sink, "Stopping");
|
||||||
|
|
||||||
match sink.stop() {
|
match sink_impl.stop(sink) {
|
||||||
Ok(..) => {
|
Ok(..) => {
|
||||||
trace!(self.logger, "Stopped successfully");
|
gst_trace!(self.cat, obj: sink, "Stopped successfully");
|
||||||
self.uri.lock().unwrap().1 = false;
|
self.uri.lock().unwrap().1 = false;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Err(ref msg) => {
|
Err(ref msg) => {
|
||||||
error!(self.logger, "Failed to stop: {:?}", msg);
|
gst_error!(self.cat, obj: sink, "Failed to stop: {:?}", msg);
|
||||||
|
|
||||||
self.post_message(msg);
|
self.post_message(sink, msg);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&self, buffer: &Buffer) -> gst_ffi::GstFlowReturn {
|
fn render(&self, sink: &RsSinkWrapper, buffer: &gst::BufferRef) -> gst::FlowReturn {
|
||||||
let sink = &mut self.sink.lock().unwrap();
|
let sink_impl = &mut self.sink.lock().unwrap();
|
||||||
|
|
||||||
trace!(self.logger, "Rendering buffer {:?}", buffer);
|
gst_trace!(self.cat, obj: sink, "Rendering buffer {:?}", buffer);
|
||||||
|
|
||||||
match sink.render(buffer) {
|
match sink_impl.render(sink, buffer) {
|
||||||
Ok(..) => gst_ffi::GST_FLOW_OK,
|
Ok(..) => gst::FlowReturn::Ok,
|
||||||
Err(flow_error) => {
|
Err(flow_error) => {
|
||||||
error!(self.logger, "Failed to render: {:?}", flow_error);
|
gst_error!(self.cat, obj: sink, "Failed to render: {:?}", flow_error);
|
||||||
match flow_error {
|
match flow_error {
|
||||||
FlowError::NotNegotiated(ref msg) | FlowError::Error(ref msg) => {
|
FlowError::NotNegotiated(ref msg) | FlowError::Error(ref msg) => {
|
||||||
self.post_message(msg)
|
self.post_message(sink, msg)
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
@ -201,86 +175,75 @@ impl SinkWrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_message(&self, msg: &ErrorMessage) {
|
fn post_message(&self, sink: &RsSinkWrapper, msg: &ErrorMessage) {
|
||||||
unsafe {
|
msg.post(sink);
|
||||||
msg.post(self.raw);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn sink_set_uri(
|
unsafe fn sink_set_uri(
|
||||||
ptr: *const RsSink,
|
ptr: *mut RsSink,
|
||||||
uri_ptr: *const c_char,
|
uri_ptr: *const c_char,
|
||||||
cerr: *mut *mut glib_ffi::GError,
|
cerr: *mut *mut glib_ffi::GError,
|
||||||
) -> glib_ffi::gboolean {
|
) -> glib_ffi::gboolean {
|
||||||
let sink = &*(ptr as *const RsSink);
|
let sink: &RsSinkWrapper = &from_glib_borrow(ptr as *mut RsSink);
|
||||||
let wrap: &SinkWrapper = &*sink.wrap;
|
let wrap = sink.get_wrap();
|
||||||
|
|
||||||
panic_to_error!(wrap, glib_ffi::GFALSE, {
|
panic_to_error!(wrap, sink, false, {
|
||||||
let uri_str = if uri_ptr.is_null() {
|
let uri_str = if uri_ptr.is_null() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(CStr::from_ptr(uri_ptr).to_str().unwrap())
|
Some(CStr::from_ptr(uri_ptr).to_str().unwrap())
|
||||||
};
|
};
|
||||||
|
|
||||||
match wrap.set_uri(uri_str) {
|
match wrap.set_uri(sink, uri_str) {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!(wrap.logger, "Failed to set URI {:?}", err);
|
gst_error!(wrap.cat, obj: sink, "Failed to set URI {:?}", err);
|
||||||
err.into_gerror(cerr);
|
if !cerr.is_null() {
|
||||||
glib_ffi::GFALSE
|
let err = err.into_error();
|
||||||
|
*cerr = err.to_glib_full() as *mut _;
|
||||||
}
|
}
|
||||||
Ok(_) => glib_ffi::GTRUE,
|
false
|
||||||
}
|
}
|
||||||
})
|
Ok(_) => true,
|
||||||
|
}
|
||||||
|
}).to_glib()
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn sink_get_uri(ptr: *const RsSink) -> *mut c_char {
|
unsafe fn sink_get_uri(ptr: *mut RsSink) -> *mut c_char {
|
||||||
let sink = &*(ptr as *const RsSink);
|
let sink: &RsSinkWrapper = &from_glib_borrow(ptr as *mut RsSink);
|
||||||
let wrap: &SinkWrapper = &*sink.wrap;
|
let wrap = sink.get_wrap();
|
||||||
|
|
||||||
panic_to_error!(wrap, ptr::null_mut(), {
|
panic_to_error!(wrap, sink, None, { wrap.get_uri(sink) }).to_glib_full()
|
||||||
match wrap.get_uri() {
|
|
||||||
Some(uri_str) => glib_ffi::g_strndup(uri_str.as_ptr() as *const c_char, uri_str.len()),
|
|
||||||
None => ptr::null_mut(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn sink_start(ptr: *mut gst_base_ffi::GstBaseSink) -> glib_ffi::gboolean {
|
unsafe extern "C" fn sink_start(ptr: *mut gst_base_ffi::GstBaseSink) -> glib_ffi::gboolean {
|
||||||
let sink = &*(ptr as *const RsSink);
|
let sink: &RsSinkWrapper = &from_glib_borrow(ptr as *mut RsSink);
|
||||||
let wrap: &SinkWrapper = &*sink.wrap;
|
let wrap = sink.get_wrap();
|
||||||
|
|
||||||
panic_to_error!(wrap, glib_ffi::GFALSE, {
|
panic_to_error!(wrap, sink, false, { wrap.start(sink) }).to_glib()
|
||||||
if wrap.start() {
|
|
||||||
glib_ffi::GTRUE
|
|
||||||
} else {
|
|
||||||
glib_ffi::GFALSE
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn sink_stop(ptr: *mut gst_base_ffi::GstBaseSink) -> glib_ffi::gboolean {
|
unsafe extern "C" fn sink_stop(ptr: *mut gst_base_ffi::GstBaseSink) -> glib_ffi::gboolean {
|
||||||
let sink = &*(ptr as *const RsSink);
|
let sink: &RsSinkWrapper = &from_glib_borrow(ptr as *mut RsSink);
|
||||||
let wrap: &SinkWrapper = &*sink.wrap;
|
let wrap = sink.get_wrap();
|
||||||
|
|
||||||
panic_to_error!(wrap, glib_ffi::GTRUE, {
|
panic_to_error!(wrap, sink, true, { wrap.stop(sink) }).to_glib()
|
||||||
if wrap.stop() {
|
|
||||||
glib_ffi::GTRUE
|
|
||||||
} else {
|
|
||||||
glib_ffi::GFALSE
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn sink_render(
|
unsafe extern "C" fn sink_render(
|
||||||
ptr: *mut gst_base_ffi::GstBaseSink,
|
ptr: *mut gst_base_ffi::GstBaseSink,
|
||||||
buffer: *mut gst_ffi::GstBuffer,
|
buffer: *mut gst_ffi::GstBuffer,
|
||||||
) -> gst_ffi::GstFlowReturn {
|
) -> gst_ffi::GstFlowReturn {
|
||||||
let sink = &*(ptr as *const RsSink);
|
let sink: &RsSinkWrapper = &from_glib_borrow(ptr as *mut RsSink);
|
||||||
let wrap: &SinkWrapper = &*sink.wrap;
|
let wrap = sink.get_wrap();
|
||||||
let buffer: &Buffer = Buffer::from_ptr(buffer);
|
let buffer = gst::BufferRef::from_ptr(buffer);
|
||||||
|
|
||||||
panic_to_error!(wrap, gst_ffi::GST_FLOW_ERROR, { wrap.render(buffer) })
|
panic_to_error!(
|
||||||
|
wrap,
|
||||||
|
sink,
|
||||||
|
gst::FlowReturn::Error,
|
||||||
|
{ wrap.render(sink, buffer) }
|
||||||
|
).to_glib()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SinkInfo {
|
pub struct SinkInfo {
|
||||||
|
@ -290,31 +253,111 @@ pub struct SinkInfo {
|
||||||
pub classification: String,
|
pub classification: String,
|
||||||
pub author: String,
|
pub author: String,
|
||||||
pub rank: u32,
|
pub rank: u32,
|
||||||
pub create_instance: fn(Element) -> Box<Sink>,
|
pub create_instance: fn(&RsSinkWrapper) -> Box<Sink>,
|
||||||
pub protocols: Vec<String>,
|
pub protocols: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
glib_wrapper! {
|
||||||
struct RsSink {
|
pub struct RsSinkWrapper(Object<RsSink>): [gst_base::BaseSink => gst_base_ffi::GstBaseSink,
|
||||||
parent: gst_base_ffi::GstBaseSink,
|
gst::Element => gst_ffi::GstElement,
|
||||||
wrap: *mut SinkWrapper,
|
gst::Object => gst_ffi::GstObject,
|
||||||
sink_info: *const SinkInfo,
|
gst::URIHandler => gst_ffi::GstURIHandler,
|
||||||
|
];
|
||||||
|
|
||||||
|
match fn {
|
||||||
|
get_type => || rs_sink_get_type(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl RsSinkWrapper {
|
||||||
|
fn get_wrap(&self) -> &SinkWrapper {
|
||||||
|
let stash = self.to_glib_none();
|
||||||
|
let sink: *mut RsSink = stash.0;
|
||||||
|
|
||||||
|
unsafe { &*((*sink).wrap) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u32)]
|
||||||
|
enum Properties {
|
||||||
|
PropURI = 1u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct RsSinkClass {
|
pub struct RsSink {
|
||||||
|
parent: gst_base_ffi::GstBaseSink,
|
||||||
|
wrap: *mut SinkWrapper,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct RsSinkClass {
|
||||||
parent_class: gst_base_ffi::GstBaseSinkClass,
|
parent_class: gst_base_ffi::GstBaseSinkClass,
|
||||||
sink_info: *const SinkInfo,
|
sink_info: *const SinkInfo,
|
||||||
protocols: *const Vec<*const c_char>,
|
protocols: *const Vec<*const c_char>,
|
||||||
parent_vtable: glib_ffi::gconstpointer,
|
parent_vtable: glib_ffi::gconstpointer,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn rs_sink_get_type() -> glib_ffi::GType {
|
||||||
|
use std::sync::{Once, ONCE_INIT};
|
||||||
|
|
||||||
|
static mut TYPE: glib_ffi::GType = gobject_ffi::G_TYPE_INVALID;
|
||||||
|
static ONCE: Once = ONCE_INIT;
|
||||||
|
|
||||||
|
ONCE.call_once(|| {
|
||||||
|
let type_info = gobject_ffi::GTypeInfo {
|
||||||
|
class_size: mem::size_of::<RsSinkClass>() as u16,
|
||||||
|
base_init: None,
|
||||||
|
base_finalize: None,
|
||||||
|
class_init: Some(sink_class_init),
|
||||||
|
class_finalize: None,
|
||||||
|
class_data: ptr::null_mut(),
|
||||||
|
instance_size: mem::size_of::<RsSink>() as u16,
|
||||||
|
n_preallocs: 0,
|
||||||
|
instance_init: Some(sink_init),
|
||||||
|
value_table: ptr::null(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let type_name = {
|
||||||
|
let mut idx = 0;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let type_name = CString::new(format!("RsSink-{}", idx)).unwrap();
|
||||||
|
if gobject_ffi::g_type_from_name(type_name.as_ptr()) == gobject_ffi::G_TYPE_INVALID
|
||||||
|
{
|
||||||
|
break type_name;
|
||||||
|
}
|
||||||
|
idx += 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TYPE = gobject_ffi::g_type_register_static(
|
||||||
|
gst_base_ffi::gst_base_sink_get_type(),
|
||||||
|
type_name.as_ptr(),
|
||||||
|
&type_info,
|
||||||
|
gobject_ffi::GTypeFlags::empty(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let iface_info = gobject_ffi::GInterfaceInfo {
|
||||||
|
interface_init: Some(sink_uri_handler_init),
|
||||||
|
interface_finalize: None,
|
||||||
|
interface_data: ptr::null_mut(),
|
||||||
|
};
|
||||||
|
gobject_ffi::g_type_add_interface_static(
|
||||||
|
TYPE,
|
||||||
|
gst_ffi::gst_uri_handler_get_type(),
|
||||||
|
&iface_info,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
TYPE
|
||||||
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn sink_finalize(obj: *mut gobject_ffi::GObject) {
|
unsafe extern "C" fn sink_finalize(obj: *mut gobject_ffi::GObject) {
|
||||||
let sink = &mut *(obj as *mut RsSink);
|
let sink = &mut *(obj as *mut RsSink);
|
||||||
|
|
||||||
drop(Box::from_raw(sink.wrap));
|
drop(Box::from_raw(sink.wrap));
|
||||||
|
sink.wrap = ptr::null_mut();
|
||||||
|
|
||||||
let sink_klass = &**(obj as *const *const RsSinkClass);
|
let sink_klass = &**(obj as *const *mut RsSinkClass);
|
||||||
let parent_klass = &*(sink_klass.parent_vtable as *const gobject_ffi::GObjectClass);
|
let parent_klass = &*(sink_klass.parent_vtable as *const gobject_ffi::GObjectClass);
|
||||||
parent_klass.finalize.map(|f| f(obj));
|
parent_klass.finalize.map(|f| f(obj));
|
||||||
}
|
}
|
||||||
|
@ -325,10 +368,10 @@ unsafe extern "C" fn sink_set_property(
|
||||||
value: *mut gobject_ffi::GValue,
|
value: *mut gobject_ffi::GValue,
|
||||||
_pspec: *mut gobject_ffi::GParamSpec,
|
_pspec: *mut gobject_ffi::GParamSpec,
|
||||||
) {
|
) {
|
||||||
let sink = &*(obj as *const RsSink);
|
let sink = obj as *mut RsSink;
|
||||||
|
|
||||||
match id {
|
match mem::transmute(id) {
|
||||||
1 => {
|
Properties::PropURI => {
|
||||||
let uri_ptr = gobject_ffi::g_value_get_string(value);
|
let uri_ptr = gobject_ffi::g_value_get_string(value);
|
||||||
sink_set_uri(sink, uri_ptr, ptr::null_mut());
|
sink_set_uri(sink, uri_ptr, ptr::null_mut());
|
||||||
}
|
}
|
||||||
|
@ -342,10 +385,10 @@ unsafe extern "C" fn sink_get_property(
|
||||||
value: *mut gobject_ffi::GValue,
|
value: *mut gobject_ffi::GValue,
|
||||||
_pspec: *mut gobject_ffi::GParamSpec,
|
_pspec: *mut gobject_ffi::GParamSpec,
|
||||||
) {
|
) {
|
||||||
let sink = &*(obj as *const RsSink);
|
let sink = obj as *mut RsSink;
|
||||||
|
|
||||||
match id {
|
match mem::transmute(id) {
|
||||||
1 => {
|
Properties::PropURI => {
|
||||||
let uri_ptr = sink_get_uri(sink);
|
let uri_ptr = sink_get_uri(sink);
|
||||||
gobject_ffi::g_value_take_string(value, uri_ptr);
|
gobject_ffi::g_value_take_string(value, uri_ptr);
|
||||||
}
|
}
|
||||||
|
@ -353,70 +396,36 @@ unsafe extern "C" fn sink_get_property(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn sink_class_init(klass: glib_ffi::gpointer, klass_data: glib_ffi::gpointer) {
|
unsafe extern "C" fn sink_sub_class_init(
|
||||||
let sink_klass = &mut *(klass as *mut RsSinkClass);
|
klass: glib_ffi::gpointer,
|
||||||
|
klass_data: glib_ffi::gpointer,
|
||||||
|
) {
|
||||||
let sink_info = &*(klass_data as *const SinkInfo);
|
let sink_info = &*(klass_data as *const SinkInfo);
|
||||||
|
|
||||||
{
|
{
|
||||||
let gobject_klass = &mut sink_klass
|
let element_klass = &mut *(klass as *mut gst_ffi::GstElementClass);
|
||||||
.parent_class
|
|
||||||
.parent_class
|
|
||||||
.parent_class
|
|
||||||
.parent_class;
|
|
||||||
gobject_klass.set_property = Some(sink_set_property);
|
|
||||||
gobject_klass.get_property = Some(sink_get_property);
|
|
||||||
gobject_klass.finalize = Some(sink_finalize);
|
|
||||||
|
|
||||||
let name_cstr = CString::new("uri").unwrap();
|
gst_ffi::gst_element_class_set_metadata(
|
||||||
let nick_cstr = CString::new("URI").unwrap();
|
|
||||||
let blurb_cstr = CString::new("URI to read from").unwrap();
|
|
||||||
|
|
||||||
gobject_ffi::g_object_class_install_property(
|
|
||||||
klass as *mut gobject_ffi::GObjectClass,
|
|
||||||
1,
|
|
||||||
gobject_ffi::g_param_spec_string(
|
|
||||||
name_cstr.as_ptr(),
|
|
||||||
nick_cstr.as_ptr(),
|
|
||||||
blurb_cstr.as_ptr(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
gobject_ffi::G_PARAM_READWRITE,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
let element_klass = &mut sink_klass.parent_class.parent_class;
|
|
||||||
|
|
||||||
let longname_cstr = CString::new(sink_info.long_name.clone()).unwrap();
|
|
||||||
let classification_cstr = CString::new(sink_info.description.clone()).unwrap();
|
|
||||||
let description_cstr = CString::new(sink_info.classification.clone()).unwrap();
|
|
||||||
let author_cstr = CString::new(sink_info.author.clone()).unwrap();
|
|
||||||
|
|
||||||
gst_ffi::gst_element_class_set_static_metadata(
|
|
||||||
element_klass,
|
element_klass,
|
||||||
longname_cstr.into_raw(),
|
sink_info.long_name.to_glib_none().0,
|
||||||
classification_cstr.into_raw(),
|
sink_info.classification.to_glib_none().0,
|
||||||
description_cstr.into_raw(),
|
sink_info.description.to_glib_none().0,
|
||||||
author_cstr.into_raw(),
|
sink_info.author.to_glib_none().0,
|
||||||
);
|
);
|
||||||
|
|
||||||
let caps = Caps::new_any();
|
// TODO: Methods + sink_info.caps
|
||||||
let templ_name = CString::new("sink").unwrap();
|
let caps = gst::Caps::new_any();
|
||||||
let pad_template = gst_ffi::gst_pad_template_new(
|
let pad_template = gst::PadTemplate::new(
|
||||||
templ_name.into_raw(),
|
"sink",
|
||||||
gst_ffi::GST_PAD_SINK,
|
gst::PadDirection::Sink,
|
||||||
gst_ffi::GST_PAD_ALWAYS,
|
gst::PadPresence::Always,
|
||||||
caps.as_ptr() as *mut gst_ffi::GstCaps,
|
&caps,
|
||||||
);
|
);
|
||||||
gst_ffi::gst_element_class_add_pad_template(element_klass, pad_template);
|
gst_ffi::gst_element_class_add_pad_template(element_klass, pad_template.to_glib_full());
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let basesink_klass = &mut sink_klass.parent_class;
|
let sink_klass = &mut *(klass as *mut RsSinkClass);
|
||||||
basesink_klass.start = Some(sink_start);
|
|
||||||
basesink_klass.stop = Some(sink_stop);
|
|
||||||
basesink_klass.render = Some(sink_render);
|
|
||||||
}
|
|
||||||
|
|
||||||
sink_klass.sink_info = sink_info;
|
sink_klass.sink_info = sink_info;
|
||||||
let mut protocols = Box::new(Vec::with_capacity(sink_info.protocols.len()));
|
let mut protocols = Box::new(Vec::with_capacity(sink_info.protocols.len()));
|
||||||
|
@ -426,8 +435,43 @@ unsafe extern "C" fn sink_class_init(klass: glib_ffi::gpointer, klass_data: glib
|
||||||
}
|
}
|
||||||
protocols.push(ptr::null());
|
protocols.push(ptr::null());
|
||||||
sink_klass.protocols = Box::into_raw(protocols) as *const Vec<*const c_char>;
|
sink_klass.protocols = Box::into_raw(protocols) as *const Vec<*const c_char>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn sink_class_init(klass: glib_ffi::gpointer, _klass_data: glib_ffi::gpointer) {
|
||||||
|
{
|
||||||
|
let gobject_klass = &mut *(klass as *mut gobject_ffi::GObjectClass);
|
||||||
|
gobject_klass.set_property = Some(sink_set_property);
|
||||||
|
gobject_klass.get_property = Some(sink_get_property);
|
||||||
|
gobject_klass.finalize = Some(sink_finalize);
|
||||||
|
|
||||||
|
gobject_ffi::g_object_class_install_property(
|
||||||
|
klass as *mut gobject_ffi::GObjectClass,
|
||||||
|
1,
|
||||||
|
gobject_ffi::g_param_spec_string(
|
||||||
|
"uri".to_glib_none().0,
|
||||||
|
"URI".to_glib_none().0,
|
||||||
|
"URI to read from".to_glib_none().0,
|
||||||
|
ptr::null_mut(),
|
||||||
|
gobject_ffi::G_PARAM_READWRITE,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let basesink_klass = &mut *(klass as *mut gst_base_ffi::GstBaseSinkClass);
|
||||||
|
|
||||||
|
basesink_klass.start = Some(sink_start);
|
||||||
|
basesink_klass.stop = Some(sink_stop);
|
||||||
|
basesink_klass.render = Some(sink_render);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let sink_klass = &mut *(klass as *mut RsSinkClass);
|
||||||
|
|
||||||
sink_klass.parent_vtable = gobject_ffi::g_type_class_peek_parent(klass);
|
sink_klass.parent_vtable = gobject_ffi::g_type_class_peek_parent(klass);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn sink_init(
|
unsafe extern "C" fn sink_init(
|
||||||
instance: *mut gobject_ffi::GTypeInstance,
|
instance: *mut gobject_ffi::GTypeInstance,
|
||||||
|
@ -437,19 +481,17 @@ unsafe extern "C" fn sink_init(
|
||||||
let sink_klass = &*(klass as *const RsSinkClass);
|
let sink_klass = &*(klass as *const RsSinkClass);
|
||||||
let sink_info = &*sink_klass.sink_info;
|
let sink_info = &*sink_klass.sink_info;
|
||||||
|
|
||||||
sink.sink_info = sink_info;
|
let wrap = Box::new(SinkWrapper::new((sink_info.create_instance)(
|
||||||
|
&RsSinkWrapper::from_glib_borrow(instance as *mut _),
|
||||||
let wrap = Box::new(SinkWrapper::new(
|
)));
|
||||||
&mut sink.parent.element,
|
|
||||||
(sink_info.create_instance)(Element::new(&mut sink.parent.element)),
|
|
||||||
));
|
|
||||||
sink.wrap = Box::into_raw(wrap);
|
sink.wrap = Box::into_raw(wrap);
|
||||||
|
|
||||||
gst_base_ffi::gst_base_sink_set_sync(&mut sink.parent, glib_ffi::GFALSE);
|
let sink = &RsSinkWrapper::from_glib_borrow(sink as *mut _);
|
||||||
|
sink.set_sync(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn sink_uri_handler_get_type(_type: glib_ffi::GType) -> gst_ffi::GstURIType {
|
unsafe extern "C" fn sink_uri_handler_get_type(_type: glib_ffi::GType) -> gst_ffi::GstURIType {
|
||||||
gst_ffi::GST_URI_SINK
|
gst::URIType::Sink.to_glib()
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn sink_uri_handler_get_protocols(
|
unsafe extern "C" fn sink_uri_handler_get_protocols(
|
||||||
|
@ -463,7 +505,7 @@ unsafe extern "C" fn sink_uri_handler_get_protocols(
|
||||||
unsafe extern "C" fn sink_uri_handler_get_uri(
|
unsafe extern "C" fn sink_uri_handler_get_uri(
|
||||||
uri_handler: *mut gst_ffi::GstURIHandler,
|
uri_handler: *mut gst_ffi::GstURIHandler,
|
||||||
) -> *mut c_char {
|
) -> *mut c_char {
|
||||||
sink_get_uri(uri_handler as *const RsSink)
|
sink_get_uri(uri_handler as *mut RsSink)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn sink_uri_handler_set_uri(
|
unsafe extern "C" fn sink_uri_handler_set_uri(
|
||||||
|
@ -471,7 +513,7 @@ unsafe extern "C" fn sink_uri_handler_set_uri(
|
||||||
uri: *const c_char,
|
uri: *const c_char,
|
||||||
err: *mut *mut glib_ffi::GError,
|
err: *mut *mut glib_ffi::GError,
|
||||||
) -> glib_ffi::gboolean {
|
) -> glib_ffi::gboolean {
|
||||||
sink_set_uri(uri_handler as *const RsSink, uri, err)
|
sink_set_uri(uri_handler as *mut RsSink, uri, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn sink_uri_handler_init(
|
unsafe extern "C" fn sink_uri_handler_init(
|
||||||
|
@ -486,14 +528,12 @@ unsafe extern "C" fn sink_uri_handler_init(
|
||||||
uri_handler_iface.set_uri = Some(sink_uri_handler_set_uri);
|
uri_handler_iface.set_uri = Some(sink_uri_handler_set_uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sink_register(plugin: &Plugin, sink_info: SinkInfo) {
|
pub fn sink_register(plugin: &gst::Plugin, sink_info: SinkInfo) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let parent_type = gst_base_ffi::gst_base_sink_get_type();
|
let parent_type = rs_sink_get_type();
|
||||||
let mut type_name = String::from("RsSink-");
|
let type_name = format!("RsSink-{}", sink_info.name);
|
||||||
type_name.push_str(&sink_info.name);
|
|
||||||
let type_name_cstr = CString::new(type_name.into_bytes()).unwrap();
|
|
||||||
|
|
||||||
let name_cstr = CString::new(sink_info.name.clone().into_bytes()).unwrap();
|
let name = sink_info.name.clone();
|
||||||
let rank = sink_info.rank;
|
let rank = sink_info.rank;
|
||||||
|
|
||||||
let sink_info = Box::new(sink_info);
|
let sink_info = Box::new(sink_info);
|
||||||
|
@ -503,33 +543,22 @@ pub fn sink_register(plugin: &Plugin, sink_info: SinkInfo) {
|
||||||
class_size: mem::size_of::<RsSinkClass>() as u16,
|
class_size: mem::size_of::<RsSinkClass>() as u16,
|
||||||
base_init: None,
|
base_init: None,
|
||||||
base_finalize: None,
|
base_finalize: None,
|
||||||
class_init: Some(sink_class_init),
|
class_init: Some(sink_sub_class_init),
|
||||||
class_finalize: None,
|
class_finalize: None,
|
||||||
class_data: sink_info_ptr,
|
class_data: sink_info_ptr,
|
||||||
instance_size: mem::size_of::<RsSink>() as u16,
|
instance_size: mem::size_of::<RsSink>() as u16,
|
||||||
n_preallocs: 0,
|
n_preallocs: 0,
|
||||||
instance_init: Some(sink_init),
|
instance_init: None,
|
||||||
value_table: ptr::null(),
|
value_table: ptr::null(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let type_ = gobject_ffi::g_type_register_static(
|
let type_ = gobject_ffi::g_type_register_static(
|
||||||
parent_type,
|
parent_type,
|
||||||
type_name_cstr.as_ptr(),
|
type_name.to_glib_none().0,
|
||||||
&type_info,
|
&type_info,
|
||||||
gobject_ffi::GTypeFlags::empty(),
|
gobject_ffi::GTypeFlags::empty(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let iface_info = gobject_ffi::GInterfaceInfo {
|
gst::Element::register(plugin, &name, rank, from_glib(type_));
|
||||||
interface_init: Some(sink_uri_handler_init),
|
|
||||||
interface_finalize: None,
|
|
||||||
interface_data: ptr::null_mut(),
|
|
||||||
};
|
|
||||||
gobject_ffi::g_type_add_interface_static(
|
|
||||||
type_,
|
|
||||||
gst_ffi::gst_uri_handler_get_type(),
|
|
||||||
&iface_info,
|
|
||||||
);
|
|
||||||
|
|
||||||
gst_ffi::gst_element_register(plugin.as_ptr(), name_cstr.as_ptr(), rank, type_);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,45 +19,21 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use slog::Logger;
|
|
||||||
|
|
||||||
use plugin::Plugin;
|
|
||||||
use utils::*;
|
|
||||||
use error::*;
|
use error::*;
|
||||||
use buffer::*;
|
|
||||||
use miniobject::*;
|
|
||||||
use log::*;
|
|
||||||
use caps::*;
|
|
||||||
|
|
||||||
use glib_ffi;
|
use glib_ffi;
|
||||||
use gobject_ffi;
|
use gobject_ffi;
|
||||||
use gst_ffi;
|
use gst_ffi;
|
||||||
use gst_base_ffi;
|
use gst_base_ffi;
|
||||||
|
|
||||||
#[derive(Debug)]
|
use glib::translate::*;
|
||||||
pub enum SourceError {
|
use gst;
|
||||||
Failure,
|
use gst::prelude::*;
|
||||||
OpenFailed,
|
use gst_base;
|
||||||
NotFound,
|
use gst_base::prelude::*;
|
||||||
ReadFailed,
|
|
||||||
SeekFailed,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToGError for SourceError {
|
|
||||||
fn to_gerror(&self) -> (u32, i32) {
|
|
||||||
match *self {
|
|
||||||
SourceError::Failure => (gst_library_error_domain(), 1),
|
|
||||||
SourceError::OpenFailed => (gst_resource_error_domain(), 5),
|
|
||||||
SourceError::NotFound => (gst_resource_error_domain(), 3),
|
|
||||||
SourceError::ReadFailed => (gst_resource_error_domain(), 9),
|
|
||||||
SourceError::SeekFailed => (gst_resource_error_domain(), 11),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SourceWrapper {
|
pub struct SourceWrapper {
|
||||||
raw: *mut gst_ffi::GstElement,
|
cat: gst::DebugCategory,
|
||||||
logger: Logger,
|
|
||||||
uri: Mutex<(Option<Url>, bool)>,
|
uri: Mutex<(Option<Url>, bool)>,
|
||||||
uri_validator: Box<UriValidator>,
|
uri_validator: Box<UriValidator>,
|
||||||
source: Mutex<Box<Source>>,
|
source: Mutex<Box<Source>>,
|
||||||
|
@ -67,28 +43,34 @@ pub struct SourceWrapper {
|
||||||
pub trait Source {
|
pub trait Source {
|
||||||
fn uri_validator(&self) -> Box<UriValidator>;
|
fn uri_validator(&self) -> Box<UriValidator>;
|
||||||
|
|
||||||
fn is_seekable(&self) -> bool;
|
fn is_seekable(&self, src: &RsSrcWrapper) -> bool;
|
||||||
fn get_size(&self) -> Option<u64>;
|
fn get_size(&self, src: &RsSrcWrapper) -> Option<u64>;
|
||||||
|
|
||||||
fn start(&mut self, uri: Url) -> Result<(), ErrorMessage>;
|
fn start(&mut self, src: &RsSrcWrapper, uri: Url) -> Result<(), ErrorMessage>;
|
||||||
fn stop(&mut self) -> Result<(), ErrorMessage>;
|
fn stop(&mut self, src: &RsSrcWrapper) -> Result<(), ErrorMessage>;
|
||||||
fn fill(&mut self, offset: u64, length: u32, buffer: &mut Buffer) -> Result<(), FlowError>;
|
fn fill(
|
||||||
fn seek(&mut self, start: u64, stop: Option<u64>) -> Result<(), ErrorMessage>;
|
&mut self,
|
||||||
|
src: &RsSrcWrapper,
|
||||||
|
offset: u64,
|
||||||
|
length: u32,
|
||||||
|
buffer: &mut gst::BufferRef,
|
||||||
|
) -> Result<(), FlowError>;
|
||||||
|
fn seek(
|
||||||
|
&mut self,
|
||||||
|
src: &RsSrcWrapper,
|
||||||
|
start: u64,
|
||||||
|
stop: Option<u64>,
|
||||||
|
) -> Result<(), ErrorMessage>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SourceWrapper {
|
impl SourceWrapper {
|
||||||
fn new(raw: *mut gst_ffi::GstElement, source: Box<Source>) -> SourceWrapper {
|
fn new(source: Box<Source>) -> SourceWrapper {
|
||||||
SourceWrapper {
|
SourceWrapper {
|
||||||
raw: raw,
|
cat: gst::DebugCategory::new(
|
||||||
logger: Logger::root(
|
|
||||||
GstDebugDrain::new(
|
|
||||||
Some(unsafe { &Element::new(raw) }),
|
|
||||||
"rssrc",
|
"rssrc",
|
||||||
0,
|
gst::DebugColorFlags::empty(),
|
||||||
"Rust source base class",
|
"Rust source base class",
|
||||||
),
|
),
|
||||||
o!(),
|
|
||||||
),
|
|
||||||
uri: Mutex::new((None, false)),
|
uri: Mutex::new((None, false)),
|
||||||
uri_validator: source.uri_validator(),
|
uri_validator: source.uri_validator(),
|
||||||
source: Mutex::new(source),
|
source: Mutex::new(source),
|
||||||
|
@ -96,15 +78,15 @@ impl SourceWrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_uri(&self, uri_str: Option<&str>) -> Result<(), UriError> {
|
fn set_uri(&self, src: &RsSrcWrapper, uri_str: Option<&str>) -> Result<(), UriError> {
|
||||||
let uri_storage = &mut self.uri.lock().unwrap();
|
let uri_storage = &mut self.uri.lock().unwrap();
|
||||||
|
|
||||||
debug!(self.logger, "Setting URI {:?}", uri_str);
|
gst_debug!(self.cat, obj: src, "Setting URI {:?}", uri_str);
|
||||||
|
|
||||||
if uri_storage.1 {
|
if uri_storage.1 {
|
||||||
return Err(UriError::new(
|
return Err(UriError::new(
|
||||||
UriErrorKind::BadState,
|
gst::URIError::BadState,
|
||||||
Some("Already started".to_string()),
|
"Already started".to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,8 +100,8 @@ impl SourceWrapper {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(err) => Err(UriError::new(
|
Err(err) => Err(UriError::new(
|
||||||
UriErrorKind::BadUri,
|
gst::URIError::BadUri,
|
||||||
Some(format!("Failed to parse URI '{}': {}", uri_str, err)),
|
format!("Failed to parse URI '{}': {}", uri_str, err),
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -127,23 +109,23 @@ impl SourceWrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_uri(&self) -> Option<String> {
|
fn get_uri(&self, _src: &RsSrcWrapper) -> Option<String> {
|
||||||
let uri_storage = &self.uri.lock().unwrap();
|
let uri_storage = &self.uri.lock().unwrap();
|
||||||
uri_storage.0.as_ref().map(|uri| String::from(uri.as_str()))
|
uri_storage.0.as_ref().map(|uri| String::from(uri.as_str()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_seekable(&self) -> bool {
|
fn is_seekable(&self, src: &RsSrcWrapper) -> bool {
|
||||||
let source = &self.source.lock().unwrap();
|
let source_impl = &self.source.lock().unwrap();
|
||||||
source.is_seekable()
|
source_impl.is_seekable(src)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_size(&self) -> u64 {
|
fn get_size(&self, src: &RsSrcWrapper) -> u64 {
|
||||||
let source = &self.source.lock().unwrap();
|
let source_impl = &self.source.lock().unwrap();
|
||||||
source.get_size().unwrap_or(u64::MAX)
|
source_impl.get_size(src).unwrap_or(u64::MAX)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start(&self) -> bool {
|
fn start(&self, src: &RsSrcWrapper) -> bool {
|
||||||
debug!(self.logger, "Starting");
|
gst_debug!(self.cat, obj: src, "Starting");
|
||||||
|
|
||||||
// Don't keep the URI locked while we call start later
|
// Don't keep the URI locked while we call start later
|
||||||
let uri = match *self.uri.lock().unwrap() {
|
let uri = match *self.uri.lock().unwrap() {
|
||||||
|
@ -152,66 +134,76 @@ impl SourceWrapper {
|
||||||
uri.clone()
|
uri.clone()
|
||||||
}
|
}
|
||||||
(None, _) => {
|
(None, _) => {
|
||||||
error!(self.logger, "No URI given");
|
gst_error!(self.cat, obj: src, "No URI given");
|
||||||
self.post_message(&error_msg!(SourceError::OpenFailed, ["No URI given"]));
|
self.post_message(
|
||||||
|
src,
|
||||||
|
&error_msg!(gst::ResourceError::OpenRead, ["No URI given"]),
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let source = &mut self.source.lock().unwrap();
|
let source_impl = &mut self.source.lock().unwrap();
|
||||||
match source.start(uri) {
|
match source_impl.start(src, uri) {
|
||||||
Ok(..) => {
|
Ok(..) => {
|
||||||
trace!(self.logger, "Started successfully");
|
gst_trace!(self.cat, obj: src, "Started successfully");
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Err(ref msg) => {
|
Err(ref msg) => {
|
||||||
error!(self.logger, "Failed to start: {:?}", msg);
|
gst_error!(self.cat, obj: src, "Failed to start: {:?}", msg);
|
||||||
|
|
||||||
self.uri.lock().unwrap().1 = false;
|
self.uri.lock().unwrap().1 = false;
|
||||||
self.post_message(msg);
|
self.post_message(src, msg);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop(&self) -> bool {
|
fn stop(&self, src: &RsSrcWrapper) -> bool {
|
||||||
let source = &mut self.source.lock().unwrap();
|
let source_impl = &mut self.source.lock().unwrap();
|
||||||
|
|
||||||
debug!(self.logger, "Stopping");
|
gst_debug!(self.cat, obj: src, "Stopping");
|
||||||
|
|
||||||
match source.stop() {
|
match source_impl.stop(src) {
|
||||||
Ok(..) => {
|
Ok(..) => {
|
||||||
trace!(self.logger, "Stopped successfully");
|
gst_trace!(self.cat, obj: src, "Stopped successfully");
|
||||||
self.uri.lock().unwrap().1 = false;
|
self.uri.lock().unwrap().1 = false;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Err(ref msg) => {
|
Err(ref msg) => {
|
||||||
error!(self.logger, "Failed to stop: {:?}", msg);
|
gst_error!(self.cat, obj: src, "Failed to stop: {:?}", msg);
|
||||||
|
|
||||||
self.post_message(msg);
|
self.post_message(src, msg);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fill(&self, offset: u64, length: u32, buffer: &mut Buffer) -> gst_ffi::GstFlowReturn {
|
fn fill(
|
||||||
let source = &mut self.source.lock().unwrap();
|
&self,
|
||||||
|
src: &RsSrcWrapper,
|
||||||
|
offset: u64,
|
||||||
|
length: u32,
|
||||||
|
buffer: &mut gst::BufferRef,
|
||||||
|
) -> gst::FlowReturn {
|
||||||
|
let source_impl = &mut self.source.lock().unwrap();
|
||||||
|
|
||||||
trace!(
|
gst_trace!(
|
||||||
self.logger,
|
self.cat,
|
||||||
|
obj: src,
|
||||||
"Filling buffer {:?} with offset {} and length {}",
|
"Filling buffer {:?} with offset {} and length {}",
|
||||||
buffer,
|
buffer,
|
||||||
offset,
|
offset,
|
||||||
length
|
length
|
||||||
);
|
);
|
||||||
|
|
||||||
match source.fill(offset, length, buffer) {
|
match source_impl.fill(src, offset, length, buffer) {
|
||||||
Ok(()) => gst_ffi::GST_FLOW_OK,
|
Ok(()) => gst::FlowReturn::Ok,
|
||||||
Err(flow_error) => {
|
Err(flow_error) => {
|
||||||
error!(self.logger, "Failed to fill: {:?}", flow_error);
|
gst_error!(self.cat, obj: src, "Failed to fill: {:?}", flow_error);
|
||||||
match flow_error {
|
match flow_error {
|
||||||
FlowError::NotNegotiated(ref msg) | FlowError::Error(ref msg) => {
|
FlowError::NotNegotiated(ref msg) | FlowError::Error(ref msg) => {
|
||||||
self.post_message(msg)
|
self.post_message(src, msg)
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
@ -220,116 +212,94 @@ impl SourceWrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn seek(&self, start: u64, stop: Option<u64>) -> bool {
|
fn seek(&self, src: &RsSrcWrapper, start: u64, stop: Option<u64>) -> bool {
|
||||||
let source = &mut self.source.lock().unwrap();
|
let source_impl = &mut self.source.lock().unwrap();
|
||||||
|
|
||||||
debug!(self.logger, "Seeking to {:?}-{:?}", start, stop);
|
gst_debug!(self.cat, obj: src, "Seeking to {:?}-{:?}", start, stop);
|
||||||
|
|
||||||
match source.seek(start, stop) {
|
match source_impl.seek(src, start, stop) {
|
||||||
Ok(..) => true,
|
Ok(..) => true,
|
||||||
Err(ref msg) => {
|
Err(ref msg) => {
|
||||||
error!(self.logger, "Failed to seek {:?}", msg);
|
gst_error!(self.cat, obj: src, "Failed to seek {:?}", msg);
|
||||||
self.post_message(msg);
|
self.post_message(src, msg);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_message(&self, msg: &ErrorMessage) {
|
fn post_message(&self, src: &RsSrcWrapper, msg: &ErrorMessage) {
|
||||||
unsafe {
|
msg.post(src);
|
||||||
msg.post(self.raw);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn source_set_uri(
|
unsafe fn source_set_uri(
|
||||||
ptr: *const RsSrc,
|
ptr: *mut RsSrc,
|
||||||
uri_ptr: *const c_char,
|
uri_ptr: *const c_char,
|
||||||
cerr: *mut *mut glib_ffi::GError,
|
cerr: *mut *mut glib_ffi::GError,
|
||||||
) -> glib_ffi::gboolean {
|
) -> glib_ffi::gboolean {
|
||||||
let src = &*(ptr as *const RsSrc);
|
let src: &RsSrcWrapper = &from_glib_borrow(ptr as *mut RsSrc);
|
||||||
let wrap: &SourceWrapper = &*src.wrap;
|
let wrap = src.get_wrap();
|
||||||
|
|
||||||
panic_to_error!(wrap, glib_ffi::GFALSE, {
|
panic_to_error!(wrap, src, false, {
|
||||||
let uri_str = if uri_ptr.is_null() {
|
let uri_str = if uri_ptr.is_null() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(CStr::from_ptr(uri_ptr).to_str().unwrap())
|
Some(CStr::from_ptr(uri_ptr).to_str().unwrap())
|
||||||
};
|
};
|
||||||
|
|
||||||
match wrap.set_uri(uri_str) {
|
match wrap.set_uri(src, uri_str) {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!(wrap.logger, "Failed to set URI {:?}", err);
|
gst_error!(wrap.cat, obj: src, "Failed to set URI {:?}", err);
|
||||||
err.into_gerror(cerr);
|
if !cerr.is_null() {
|
||||||
glib_ffi::GFALSE
|
let err = err.into_error();
|
||||||
|
*cerr = err.to_glib_full() as *mut _;
|
||||||
}
|
}
|
||||||
Ok(_) => glib_ffi::GTRUE,
|
false
|
||||||
}
|
}
|
||||||
})
|
Ok(_) => true,
|
||||||
|
}
|
||||||
|
}).to_glib()
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn source_get_uri(ptr: *const RsSrc) -> *mut c_char {
|
unsafe fn source_get_uri(ptr: *mut RsSrc) -> *mut c_char {
|
||||||
let src = &*(ptr as *const RsSrc);
|
let src: &RsSrcWrapper = &from_glib_borrow(ptr as *mut RsSrc);
|
||||||
let wrap: &SourceWrapper = &*src.wrap;
|
let wrap = src.get_wrap();
|
||||||
|
|
||||||
panic_to_error!(wrap, ptr::null_mut(), {
|
panic_to_error!(wrap, src, None, { wrap.get_uri(src) }).to_glib_full()
|
||||||
match wrap.get_uri() {
|
|
||||||
Some(uri_str) => glib_ffi::g_strndup(uri_str.as_ptr() as *const c_char, uri_str.len()),
|
|
||||||
None => ptr::null_mut(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn source_is_seekable(ptr: *mut gst_base_ffi::GstBaseSrc) -> glib_ffi::gboolean {
|
unsafe extern "C" fn source_is_seekable(ptr: *mut gst_base_ffi::GstBaseSrc) -> glib_ffi::gboolean {
|
||||||
let src = &*(ptr as *const RsSrc);
|
let src: &RsSrcWrapper = &from_glib_borrow(ptr as *mut RsSrc);
|
||||||
let wrap: &SourceWrapper = &*src.wrap;
|
let wrap = src.get_wrap();
|
||||||
|
|
||||||
panic_to_error!(wrap, glib_ffi::GFALSE, {
|
panic_to_error!(wrap, src, false, { wrap.is_seekable(src) }).to_glib()
|
||||||
if wrap.is_seekable() {
|
|
||||||
glib_ffi::GTRUE
|
|
||||||
} else {
|
|
||||||
glib_ffi::GFALSE
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn source_get_size(
|
unsafe extern "C" fn source_get_size(
|
||||||
ptr: *mut gst_base_ffi::GstBaseSrc,
|
ptr: *mut gst_base_ffi::GstBaseSrc,
|
||||||
size: *mut u64,
|
size: *mut u64,
|
||||||
) -> glib_ffi::gboolean {
|
) -> glib_ffi::gboolean {
|
||||||
let src = &*(ptr as *const RsSrc);
|
let src: &RsSrcWrapper = &from_glib_borrow(ptr as *mut RsSrc);
|
||||||
let wrap: &SourceWrapper = &*src.wrap;
|
let wrap = src.get_wrap();
|
||||||
|
|
||||||
panic_to_error!(wrap, glib_ffi::GFALSE, {
|
panic_to_error!(wrap, src, false, {
|
||||||
*size = wrap.get_size();
|
*size = wrap.get_size(src);
|
||||||
glib_ffi::GTRUE
|
true
|
||||||
})
|
}).to_glib()
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn source_start(ptr: *mut gst_base_ffi::GstBaseSrc) -> glib_ffi::gboolean {
|
unsafe extern "C" fn source_start(ptr: *mut gst_base_ffi::GstBaseSrc) -> glib_ffi::gboolean {
|
||||||
let src = &*(ptr as *const RsSrc);
|
let src: &RsSrcWrapper = &from_glib_borrow(ptr as *mut RsSrc);
|
||||||
let wrap: &SourceWrapper = &*src.wrap;
|
let wrap = src.get_wrap();
|
||||||
|
|
||||||
panic_to_error!(wrap, glib_ffi::GFALSE, {
|
panic_to_error!(wrap, src, false, { wrap.start(src) }).to_glib()
|
||||||
if wrap.start() {
|
|
||||||
glib_ffi::GTRUE
|
|
||||||
} else {
|
|
||||||
glib_ffi::GFALSE
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn source_stop(ptr: *mut gst_base_ffi::GstBaseSrc) -> glib_ffi::gboolean {
|
unsafe extern "C" fn source_stop(ptr: *mut gst_base_ffi::GstBaseSrc) -> glib_ffi::gboolean {
|
||||||
let src = &*(ptr as *const RsSrc);
|
let src: &RsSrcWrapper = &from_glib_borrow(ptr as *mut RsSrc);
|
||||||
let wrap: &SourceWrapper = &*src.wrap;
|
let wrap = src.get_wrap();
|
||||||
|
|
||||||
panic_to_error!(wrap, glib_ffi::GTRUE, {
|
panic_to_error!(wrap, src, false, { wrap.stop(src) }).to_glib()
|
||||||
if wrap.stop() {
|
|
||||||
glib_ffi::GTRUE
|
|
||||||
} else {
|
|
||||||
glib_ffi::GFALSE
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn source_fill(
|
unsafe extern "C" fn source_fill(
|
||||||
|
@ -338,32 +308,58 @@ unsafe extern "C" fn source_fill(
|
||||||
length: u32,
|
length: u32,
|
||||||
buffer: *mut gst_ffi::GstBuffer,
|
buffer: *mut gst_ffi::GstBuffer,
|
||||||
) -> gst_ffi::GstFlowReturn {
|
) -> gst_ffi::GstFlowReturn {
|
||||||
let src = &*(ptr as *const RsSrc);
|
let src: &RsSrcWrapper = &from_glib_borrow(ptr as *mut RsSrc);
|
||||||
let wrap: &SourceWrapper = &*src.wrap;
|
let wrap = src.get_wrap();
|
||||||
let buffer: &mut Buffer = <Buffer as MiniObject>::from_mut_ptr(buffer);
|
let buffer = gst::BufferRef::from_mut_ptr(buffer);
|
||||||
|
|
||||||
panic_to_error!(wrap, gst_ffi::GST_FLOW_ERROR, {
|
panic_to_error!(wrap, src, gst::FlowReturn::Error, {
|
||||||
wrap.fill(offset, length, buffer)
|
wrap.fill(src, offset, length, buffer)
|
||||||
})
|
}).to_glib()
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn source_seek(
|
unsafe extern "C" fn source_seek(
|
||||||
ptr: *mut gst_base_ffi::GstBaseSrc,
|
ptr: *mut gst_base_ffi::GstBaseSrc,
|
||||||
segment: *mut gst_ffi::GstSegment,
|
segment: *mut gst_ffi::GstSegment,
|
||||||
) -> glib_ffi::gboolean {
|
) -> glib_ffi::gboolean {
|
||||||
let src = &*(ptr as *const RsSrc);
|
let src: &RsSrcWrapper = &from_glib_borrow(ptr as *mut RsSrc);
|
||||||
let wrap: &SourceWrapper = &*src.wrap;
|
let wrap = src.get_wrap();
|
||||||
|
|
||||||
let start = (*segment).start;
|
let start = (*segment).start;
|
||||||
let stop = (*segment).stop;
|
let stop = (*segment).stop;
|
||||||
|
|
||||||
panic_to_error!(wrap, glib_ffi::GFALSE, {
|
panic_to_error!(wrap, src, false, {
|
||||||
if wrap.seek(start, if stop == u64::MAX { None } else { Some(stop) }) {
|
wrap.seek(src, start, if stop == u64::MAX { None } else { Some(stop) })
|
||||||
glib_ffi::GTRUE
|
}).to_glib()
|
||||||
} else {
|
|
||||||
glib_ffi::GFALSE
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
unsafe extern "C" fn source_query(
|
||||||
|
ptr: *mut gst_base_ffi::GstBaseSrc,
|
||||||
|
query_ptr: *mut gst_ffi::GstQuery,
|
||||||
|
) -> glib_ffi::gboolean {
|
||||||
|
let src_klass = &**(ptr as *mut *mut RsSrcClass);
|
||||||
|
let source_info = &*src_klass.source_info;
|
||||||
|
let parent_klass = &*(src_klass.parent_vtable as *const gst_base_ffi::GstBaseSrcClass);
|
||||||
|
|
||||||
|
let src: &RsSrcWrapper = &from_glib_borrow(ptr as *mut RsSrc);
|
||||||
|
let wrap = src.get_wrap();
|
||||||
|
|
||||||
|
let query = gst::QueryRef::from_mut_ptr(query_ptr);
|
||||||
|
|
||||||
|
panic_to_error!(wrap, src, false, {
|
||||||
|
use gst::QueryView;
|
||||||
|
|
||||||
|
match query.view_mut() {
|
||||||
|
QueryView::Scheduling(ref mut q) if source_info.push_only => {
|
||||||
|
q.set(gst::SCHEDULING_FLAG_SEQUENTIAL, 1, -1, 0);
|
||||||
|
q.add_scheduling_modes(&[gst::PadMode::Push]);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => parent_klass
|
||||||
|
.query
|
||||||
|
.map(|f| from_glib(f(ptr, query_ptr)))
|
||||||
|
.unwrap_or(false),
|
||||||
|
}
|
||||||
|
}).to_glib()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SourceInfo {
|
pub struct SourceInfo {
|
||||||
|
@ -373,30 +369,111 @@ pub struct SourceInfo {
|
||||||
pub classification: String,
|
pub classification: String,
|
||||||
pub author: String,
|
pub author: String,
|
||||||
pub rank: u32,
|
pub rank: u32,
|
||||||
pub create_instance: fn(Element) -> Box<Source>,
|
pub create_instance: fn(&RsSrcWrapper) -> Box<Source>,
|
||||||
pub protocols: Vec<String>,
|
pub protocols: Vec<String>,
|
||||||
pub push_only: bool,
|
pub push_only: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
glib_wrapper! {
|
||||||
struct RsSrc {
|
pub struct RsSrcWrapper(Object<RsSrc>): [gst_base::BaseSrc => gst_base_ffi::GstBaseSrc,
|
||||||
parent: gst_base_ffi::GstPushSrc,
|
gst::Element => gst_ffi::GstElement,
|
||||||
wrap: *mut SourceWrapper,
|
gst::Object => gst_ffi::GstObject,
|
||||||
source_info: *const SourceInfo,
|
gst::URIHandler => gst_ffi::GstURIHandler,
|
||||||
|
];
|
||||||
|
|
||||||
|
match fn {
|
||||||
|
get_type => || rs_src_get_type(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RsSrcWrapper {
|
||||||
|
fn get_wrap(&self) -> &SourceWrapper {
|
||||||
|
let stash = self.to_glib_none();
|
||||||
|
let src: *mut RsSrc = stash.0;
|
||||||
|
|
||||||
|
unsafe { &*((*src).wrap) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u32)]
|
||||||
|
enum Properties {
|
||||||
|
PropURI = 1u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct RsSrcClass {
|
pub struct RsSrc {
|
||||||
|
parent: gst_base_ffi::GstPushSrc,
|
||||||
|
wrap: *mut SourceWrapper,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct RsSrcClass {
|
||||||
parent_class: gst_base_ffi::GstPushSrcClass,
|
parent_class: gst_base_ffi::GstPushSrcClass,
|
||||||
source_info: *const SourceInfo,
|
source_info: *const SourceInfo,
|
||||||
protocols: *const Vec<*const c_char>,
|
protocols: *const Vec<*const c_char>,
|
||||||
parent_vtable: glib_ffi::gconstpointer,
|
parent_vtable: glib_ffi::gconstpointer,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn rs_src_get_type() -> glib_ffi::GType {
|
||||||
|
use std::sync::{Once, ONCE_INIT};
|
||||||
|
|
||||||
|
static mut TYPE: glib_ffi::GType = gobject_ffi::G_TYPE_INVALID;
|
||||||
|
static ONCE: Once = ONCE_INIT;
|
||||||
|
|
||||||
|
ONCE.call_once(|| {
|
||||||
|
let type_info = gobject_ffi::GTypeInfo {
|
||||||
|
class_size: mem::size_of::<RsSrcClass>() as u16,
|
||||||
|
base_init: None,
|
||||||
|
base_finalize: None,
|
||||||
|
class_init: Some(source_class_init),
|
||||||
|
class_finalize: None,
|
||||||
|
class_data: ptr::null_mut(),
|
||||||
|
instance_size: mem::size_of::<RsSrc>() as u16,
|
||||||
|
n_preallocs: 0,
|
||||||
|
instance_init: Some(source_init),
|
||||||
|
value_table: ptr::null(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let type_name = {
|
||||||
|
let mut idx = 0;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let type_name = CString::new(format!("RsSrc-{}", idx)).unwrap();
|
||||||
|
if gobject_ffi::g_type_from_name(type_name.as_ptr()) == gobject_ffi::G_TYPE_INVALID
|
||||||
|
{
|
||||||
|
break type_name;
|
||||||
|
}
|
||||||
|
idx += 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TYPE = gobject_ffi::g_type_register_static(
|
||||||
|
gst_base_ffi::gst_base_src_get_type(),
|
||||||
|
type_name.as_ptr(),
|
||||||
|
&type_info,
|
||||||
|
gobject_ffi::GTypeFlags::empty(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let iface_info = gobject_ffi::GInterfaceInfo {
|
||||||
|
interface_init: Some(source_uri_handler_init),
|
||||||
|
interface_finalize: None,
|
||||||
|
interface_data: ptr::null_mut(),
|
||||||
|
};
|
||||||
|
gobject_ffi::g_type_add_interface_static(
|
||||||
|
TYPE,
|
||||||
|
gst_ffi::gst_uri_handler_get_type(),
|
||||||
|
&iface_info,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
TYPE
|
||||||
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn source_finalize(obj: *mut gobject_ffi::GObject) {
|
unsafe extern "C" fn source_finalize(obj: *mut gobject_ffi::GObject) {
|
||||||
let src = &mut *(obj as *mut RsSrc);
|
let src = &mut *(obj as *mut RsSrc);
|
||||||
|
|
||||||
drop(Box::from_raw(src.wrap));
|
drop(Box::from_raw(src.wrap));
|
||||||
|
src.wrap = ptr::null_mut();
|
||||||
|
|
||||||
let src_klass = &**(obj as *const *const RsSrcClass);
|
let src_klass = &**(obj as *const *const RsSrcClass);
|
||||||
let parent_klass = &*(src_klass.parent_vtable as *const gobject_ffi::GObjectClass);
|
let parent_klass = &*(src_klass.parent_vtable as *const gobject_ffi::GObjectClass);
|
||||||
|
@ -409,10 +486,10 @@ unsafe extern "C" fn source_set_property(
|
||||||
value: *mut gobject_ffi::GValue,
|
value: *mut gobject_ffi::GValue,
|
||||||
_pspec: *mut gobject_ffi::GParamSpec,
|
_pspec: *mut gobject_ffi::GParamSpec,
|
||||||
) {
|
) {
|
||||||
let src = &*(obj as *const RsSrc);
|
let src = obj as *mut RsSrc;
|
||||||
|
|
||||||
match id {
|
match mem::transmute(id) {
|
||||||
1 => {
|
Properties::PropURI => {
|
||||||
let uri_ptr = gobject_ffi::g_value_get_string(value);
|
let uri_ptr = gobject_ffi::g_value_get_string(value);
|
||||||
source_set_uri(src, uri_ptr, ptr::null_mut());
|
source_set_uri(src, uri_ptr, ptr::null_mut());
|
||||||
}
|
}
|
||||||
|
@ -426,10 +503,10 @@ unsafe extern "C" fn source_get_property(
|
||||||
value: *mut gobject_ffi::GValue,
|
value: *mut gobject_ffi::GValue,
|
||||||
_pspec: *mut gobject_ffi::GParamSpec,
|
_pspec: *mut gobject_ffi::GParamSpec,
|
||||||
) {
|
) {
|
||||||
let src = &*(obj as *const RsSrc);
|
let src = obj as *mut RsSrc;
|
||||||
|
|
||||||
match id {
|
match mem::transmute(id) {
|
||||||
1 => {
|
Properties::PropURI => {
|
||||||
let uri_ptr = source_get_uri(src);
|
let uri_ptr = source_get_uri(src);
|
||||||
gobject_ffi::g_value_take_string(value, uri_ptr);
|
gobject_ffi::g_value_take_string(value, uri_ptr);
|
||||||
}
|
}
|
||||||
|
@ -437,32 +514,62 @@ unsafe extern "C" fn source_get_property(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn source_class_init(klass: glib_ffi::gpointer, klass_data: glib_ffi::gpointer) {
|
unsafe extern "C" fn source_sub_class_init(
|
||||||
let src_klass = &mut *(klass as *mut RsSrcClass);
|
klass: glib_ffi::gpointer,
|
||||||
|
klass_data: glib_ffi::gpointer,
|
||||||
|
) {
|
||||||
let source_info = &*(klass_data as *const SourceInfo);
|
let source_info = &*(klass_data as *const SourceInfo);
|
||||||
|
|
||||||
{
|
{
|
||||||
let gobject_klass = &mut src_klass
|
let element_klass = &mut *(klass as *mut gst_ffi::GstElementClass);
|
||||||
.parent_class
|
|
||||||
.parent_class
|
gst_ffi::gst_element_class_set_metadata(
|
||||||
.parent_class
|
element_klass,
|
||||||
.parent_class
|
source_info.long_name.to_glib_none().0,
|
||||||
.parent_class;
|
source_info.classification.to_glib_none().0,
|
||||||
|
source_info.description.to_glib_none().0,
|
||||||
|
source_info.author.to_glib_none().0,
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: Methods + source_info.caps
|
||||||
|
let caps = gst::Caps::new_any();
|
||||||
|
let pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
);
|
||||||
|
gst_ffi::gst_element_class_add_pad_template(element_klass, pad_template.to_glib_full());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let src_klass = &mut *(klass as *mut RsSrcClass);
|
||||||
|
|
||||||
|
src_klass.source_info = source_info;
|
||||||
|
let mut protocols = Box::new(Vec::with_capacity(source_info.protocols.len()));
|
||||||
|
for p in &source_info.protocols {
|
||||||
|
protocols.push(p.to_glib_full());
|
||||||
|
}
|
||||||
|
protocols.push(ptr::null());
|
||||||
|
src_klass.protocols = Box::into_raw(protocols) as *const Vec<*const c_char>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn source_class_init(klass: glib_ffi::gpointer, _klass_data: glib_ffi::gpointer) {
|
||||||
|
{
|
||||||
|
let gobject_klass = &mut *(klass as *mut gobject_ffi::GObjectClass);
|
||||||
|
|
||||||
gobject_klass.set_property = Some(source_set_property);
|
gobject_klass.set_property = Some(source_set_property);
|
||||||
gobject_klass.get_property = Some(source_get_property);
|
gobject_klass.get_property = Some(source_get_property);
|
||||||
gobject_klass.finalize = Some(source_finalize);
|
gobject_klass.finalize = Some(source_finalize);
|
||||||
|
|
||||||
let name_cstr = CString::new("uri").unwrap();
|
|
||||||
let nick_cstr = CString::new("URI").unwrap();
|
|
||||||
let blurb_cstr = CString::new("URI to read from").unwrap();
|
|
||||||
|
|
||||||
gobject_ffi::g_object_class_install_property(
|
gobject_ffi::g_object_class_install_property(
|
||||||
klass as *mut gobject_ffi::GObjectClass,
|
klass as *mut gobject_ffi::GObjectClass,
|
||||||
1,
|
1,
|
||||||
gobject_ffi::g_param_spec_string(
|
gobject_ffi::g_param_spec_string(
|
||||||
name_cstr.as_ptr(),
|
"uri".to_glib_none().0,
|
||||||
nick_cstr.as_ptr(),
|
"URI".to_glib_none().0,
|
||||||
blurb_cstr.as_ptr(),
|
"URI to read from".to_glib_none().0,
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
gobject_ffi::G_PARAM_READWRITE,
|
gobject_ffi::G_PARAM_READWRITE,
|
||||||
),
|
),
|
||||||
|
@ -470,52 +577,23 @@ unsafe extern "C" fn source_class_init(klass: glib_ffi::gpointer, klass_data: gl
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let element_klass = &mut src_klass.parent_class.parent_class.parent_class;
|
let basesrc_klass = &mut *(klass as *mut gst_base_ffi::GstBaseSrcClass);
|
||||||
|
|
||||||
let longname_cstr = CString::new(source_info.long_name.clone()).unwrap();
|
|
||||||
let classification_cstr = CString::new(source_info.description.clone()).unwrap();
|
|
||||||
let description_cstr = CString::new(source_info.classification.clone()).unwrap();
|
|
||||||
let author_cstr = CString::new(source_info.author.clone()).unwrap();
|
|
||||||
|
|
||||||
gst_ffi::gst_element_class_set_static_metadata(
|
|
||||||
element_klass,
|
|
||||||
longname_cstr.into_raw(),
|
|
||||||
classification_cstr.into_raw(),
|
|
||||||
description_cstr.into_raw(),
|
|
||||||
author_cstr.into_raw(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let caps = Caps::new_any();
|
|
||||||
let templ_name = CString::new("src").unwrap();
|
|
||||||
let pad_template = gst_ffi::gst_pad_template_new(
|
|
||||||
templ_name.into_raw(),
|
|
||||||
gst_ffi::GST_PAD_SRC,
|
|
||||||
gst_ffi::GST_PAD_ALWAYS,
|
|
||||||
caps.as_ptr() as *mut gst_ffi::GstCaps,
|
|
||||||
);
|
|
||||||
gst_ffi::gst_element_class_add_pad_template(element_klass, pad_template);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
let basesrc_klass = &mut src_klass.parent_class.parent_class;
|
|
||||||
basesrc_klass.start = Some(source_start);
|
basesrc_klass.start = Some(source_start);
|
||||||
basesrc_klass.stop = Some(source_stop);
|
basesrc_klass.stop = Some(source_stop);
|
||||||
basesrc_klass.is_seekable = Some(source_is_seekable);
|
basesrc_klass.is_seekable = Some(source_is_seekable);
|
||||||
basesrc_klass.get_size = Some(source_get_size);
|
basesrc_klass.get_size = Some(source_get_size);
|
||||||
basesrc_klass.fill = Some(source_fill);
|
basesrc_klass.fill = Some(source_fill);
|
||||||
basesrc_klass.do_seek = Some(source_seek);
|
basesrc_klass.do_seek = Some(source_seek);
|
||||||
|
basesrc_klass.query = Some(source_query);
|
||||||
}
|
}
|
||||||
|
|
||||||
src_klass.source_info = source_info;
|
{
|
||||||
let mut protocols = Box::new(Vec::with_capacity(source_info.protocols.len()));
|
let src_klass = &mut *(klass as *mut RsSrcClass);
|
||||||
for p in &source_info.protocols {
|
|
||||||
let p_cstr = CString::new(p.clone().into_bytes()).unwrap();
|
|
||||||
protocols.push(p_cstr.into_raw() as *const c_char);
|
|
||||||
}
|
|
||||||
protocols.push(ptr::null());
|
|
||||||
src_klass.protocols = Box::into_raw(protocols) as *const Vec<*const c_char>;
|
|
||||||
src_klass.parent_vtable = gobject_ffi::g_type_class_peek_parent(klass);
|
src_klass.parent_vtable = gobject_ffi::g_type_class_peek_parent(klass);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn source_init(
|
unsafe extern "C" fn source_init(
|
||||||
instance: *mut gobject_ffi::GTypeInstance,
|
instance: *mut gobject_ffi::GTypeInstance,
|
||||||
|
@ -525,19 +603,17 @@ unsafe extern "C" fn source_init(
|
||||||
let src_klass = &*(klass as *const RsSrcClass);
|
let src_klass = &*(klass as *const RsSrcClass);
|
||||||
let source_info = &*src_klass.source_info;
|
let source_info = &*src_klass.source_info;
|
||||||
|
|
||||||
src.source_info = source_info;
|
let wrap = Box::new(SourceWrapper::new((source_info.create_instance)(
|
||||||
|
&RsSrcWrapper::from_glib_borrow(instance as *mut _),
|
||||||
let wrap = Box::new(SourceWrapper::new(
|
)));
|
||||||
&mut src.parent.parent.element,
|
|
||||||
(source_info.create_instance)(Element::new(&mut src.parent.parent.element)),
|
|
||||||
));
|
|
||||||
src.wrap = Box::into_raw(wrap);
|
src.wrap = Box::into_raw(wrap);
|
||||||
|
|
||||||
gst_base_ffi::gst_base_src_set_blocksize(&mut src.parent.parent, 4096);
|
let src = &RsSrcWrapper::from_glib_borrow(src as *mut _);
|
||||||
|
src.set_blocksize(4096);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn source_uri_handler_get_type(_type: glib_ffi::GType) -> gst_ffi::GstURIType {
|
unsafe extern "C" fn source_uri_handler_get_type(_type: glib_ffi::GType) -> gst_ffi::GstURIType {
|
||||||
gst_ffi::GST_URI_SRC
|
gst::URIType::Src.to_glib()
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn source_uri_handler_get_protocols(
|
unsafe extern "C" fn source_uri_handler_get_protocols(
|
||||||
|
@ -551,7 +627,7 @@ unsafe extern "C" fn source_uri_handler_get_protocols(
|
||||||
unsafe extern "C" fn source_uri_handler_get_uri(
|
unsafe extern "C" fn source_uri_handler_get_uri(
|
||||||
uri_handler: *mut gst_ffi::GstURIHandler,
|
uri_handler: *mut gst_ffi::GstURIHandler,
|
||||||
) -> *mut c_char {
|
) -> *mut c_char {
|
||||||
source_get_uri(uri_handler as *const RsSrc)
|
source_get_uri(uri_handler as *mut RsSrc)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn source_uri_handler_set_uri(
|
unsafe extern "C" fn source_uri_handler_set_uri(
|
||||||
|
@ -559,7 +635,7 @@ unsafe extern "C" fn source_uri_handler_set_uri(
|
||||||
uri: *const c_char,
|
uri: *const c_char,
|
||||||
err: *mut *mut glib_ffi::GError,
|
err: *mut *mut glib_ffi::GError,
|
||||||
) -> glib_ffi::gboolean {
|
) -> glib_ffi::gboolean {
|
||||||
source_set_uri(uri_handler as *const RsSrc, uri, err)
|
source_set_uri(uri_handler as *mut RsSrc, uri, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn source_uri_handler_init(
|
unsafe extern "C" fn source_uri_handler_init(
|
||||||
|
@ -574,18 +650,12 @@ unsafe extern "C" fn source_uri_handler_init(
|
||||||
uri_handler_iface.set_uri = Some(source_uri_handler_set_uri);
|
uri_handler_iface.set_uri = Some(source_uri_handler_set_uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn source_register(plugin: &Plugin, source_info: SourceInfo) {
|
pub fn source_register(plugin: &gst::Plugin, source_info: SourceInfo) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let parent_type = if source_info.push_only {
|
let parent_type = rs_src_get_type();
|
||||||
gst_base_ffi::gst_push_src_get_type()
|
let type_name = format!("RsSrc-{}", source_info.name);
|
||||||
} else {
|
|
||||||
gst_base_ffi::gst_base_src_get_type()
|
|
||||||
};
|
|
||||||
let mut type_name = String::from("RsSrc-");
|
|
||||||
type_name.push_str(&source_info.name);
|
|
||||||
let type_name_cstr = CString::new(type_name.into_bytes()).unwrap();
|
|
||||||
|
|
||||||
let name_cstr = CString::new(source_info.name.clone().into_bytes()).unwrap();
|
let name = source_info.name.clone();
|
||||||
let rank = source_info.rank;
|
let rank = source_info.rank;
|
||||||
|
|
||||||
let source_info = Box::new(source_info);
|
let source_info = Box::new(source_info);
|
||||||
|
@ -595,33 +665,22 @@ pub fn source_register(plugin: &Plugin, source_info: SourceInfo) {
|
||||||
class_size: mem::size_of::<RsSrcClass>() as u16,
|
class_size: mem::size_of::<RsSrcClass>() as u16,
|
||||||
base_init: None,
|
base_init: None,
|
||||||
base_finalize: None,
|
base_finalize: None,
|
||||||
class_init: Some(source_class_init),
|
class_init: Some(source_sub_class_init),
|
||||||
class_finalize: None,
|
class_finalize: None,
|
||||||
class_data: source_info_ptr,
|
class_data: source_info_ptr,
|
||||||
instance_size: mem::size_of::<RsSrc>() as u16,
|
instance_size: mem::size_of::<RsSrc>() as u16,
|
||||||
n_preallocs: 0,
|
n_preallocs: 0,
|
||||||
instance_init: Some(source_init),
|
instance_init: None,
|
||||||
value_table: ptr::null(),
|
value_table: ptr::null(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let type_ = gobject_ffi::g_type_register_static(
|
let type_ = gobject_ffi::g_type_register_static(
|
||||||
parent_type,
|
parent_type,
|
||||||
type_name_cstr.as_ptr(),
|
type_name.to_glib_none().0,
|
||||||
&type_info,
|
&type_info,
|
||||||
gobject_ffi::GTypeFlags::empty(),
|
gobject_ffi::GTypeFlags::empty(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let iface_info = gobject_ffi::GInterfaceInfo {
|
gst::Element::register(plugin, &name, rank, from_glib(type_));
|
||||||
interface_init: Some(source_uri_handler_init),
|
|
||||||
interface_finalize: None,
|
|
||||||
interface_data: ptr::null_mut(),
|
|
||||||
};
|
|
||||||
gobject_ffi::g_type_add_interface_static(
|
|
||||||
type_,
|
|
||||||
gst_ffi::gst_uri_handler_get_type(),
|
|
||||||
&iface_info,
|
|
||||||
);
|
|
||||||
|
|
||||||
gst_ffi::gst_element_register(plugin.as_ptr(), name_cstr.as_ptr(), rank, type_);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,272 +0,0 @@
|
||||||
// Copyright (C) 2016-2017 Sebastian Dröge <sebastian@centricular.com>
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
use std::ptr;
|
|
||||||
use std::mem;
|
|
||||||
use std::ffi::{CStr, CString};
|
|
||||||
use caps::Caps;
|
|
||||||
use miniobject::*;
|
|
||||||
use tags::TagList;
|
|
||||||
|
|
||||||
use gst_ffi;
|
|
||||||
|
|
||||||
pub struct Stream(*mut gst_ffi::GstStream);
|
|
||||||
pub struct StreamCollection(*mut gst_ffi::GstStreamCollection);
|
|
||||||
|
|
||||||
bitflags! {
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct StreamType: u32 {
|
|
||||||
const TYPE_UNKNOWN = 0b00000001;
|
|
||||||
const TYPE_AUDIO = 0b00000010;
|
|
||||||
const TYPE_VIDEO = 0b00000100;
|
|
||||||
const TYPE_CONTAINER = 0b00001000;
|
|
||||||
const TYPE_TEXT = 0b00010000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bitflags! {
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct StreamFlags: u32 {
|
|
||||||
const FLAG_SPARSE = 0b00000001;
|
|
||||||
const FLAG_SELECT = 0b00000010;
|
|
||||||
const FLAG_UNSELECT = 0b00000100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Stream {
|
|
||||||
pub fn new(
|
|
||||||
stream_id: &str,
|
|
||||||
caps: Option<GstRc<Caps>>,
|
|
||||||
t: StreamType,
|
|
||||||
flags: StreamFlags,
|
|
||||||
) -> Self {
|
|
||||||
let stream_id_cstr = CString::new(stream_id).unwrap();
|
|
||||||
let caps = caps.map(|caps| unsafe { caps.as_mut_ptr() })
|
|
||||||
.unwrap_or(ptr::null_mut());
|
|
||||||
|
|
||||||
Stream(unsafe {
|
|
||||||
gst_ffi::gst_stream_new(
|
|
||||||
stream_id_cstr.as_ptr(),
|
|
||||||
caps,
|
|
||||||
mem::transmute(t.bits()),
|
|
||||||
mem::transmute(flags.bits()),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn as_ptr(&self) -> *const gst_ffi::GstStream {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_caps(&self) -> Option<&Caps> {
|
|
||||||
let ptr = unsafe { gst_ffi::gst_stream_get_caps(self.0) };
|
|
||||||
|
|
||||||
if ptr.is_null() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(unsafe { <Caps as MiniObject>::from_ptr(ptr) })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_stream_flags(&self) -> StreamFlags {
|
|
||||||
StreamFlags::from_bits_truncate(unsafe {
|
|
||||||
gst_ffi::gst_stream_get_stream_flags(self.0).bits()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_stream_type(&self) -> StreamType {
|
|
||||||
StreamType::from_bits_truncate(unsafe {
|
|
||||||
gst_ffi::gst_stream_get_stream_type(self.0).bits()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_stream_id(&self) -> &str {
|
|
||||||
let cstr = unsafe { CStr::from_ptr(gst_ffi::gst_stream_get_stream_id(self.0)) };
|
|
||||||
cstr.to_str().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_tags(&self) -> Option<&TagList> {
|
|
||||||
let ptr = unsafe { gst_ffi::gst_stream_get_tags(self.0) };
|
|
||||||
|
|
||||||
if ptr.is_null() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(unsafe { <TagList as MiniObject>::from_ptr(ptr) })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_caps(&self, caps: Option<GstRc<Caps>>) {
|
|
||||||
let ptr = caps.map(|caps| unsafe { caps.as_mut_ptr() })
|
|
||||||
.unwrap_or(ptr::null_mut());
|
|
||||||
|
|
||||||
unsafe { gst_ffi::gst_stream_set_caps(self.0, ptr) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_stream_flags(&self, flags: StreamFlags) {
|
|
||||||
unsafe { gst_ffi::gst_stream_set_stream_flags(self.0, mem::transmute(flags.bits())) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_stream_type(&self, t: StreamType) {
|
|
||||||
unsafe { gst_ffi::gst_stream_set_stream_type(self.0, mem::transmute(t.bits())) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_tags(&self, tags: Option<TagList>) {
|
|
||||||
let ptr = tags.map(|tags| unsafe { tags.as_mut_ptr() })
|
|
||||||
.unwrap_or(ptr::null_mut());
|
|
||||||
|
|
||||||
unsafe { gst_ffi::gst_stream_set_tags(self.0, ptr) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for Stream {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
unsafe {
|
|
||||||
Stream(
|
|
||||||
gst_ffi::gst_object_ref(self.0 as *mut gst_ffi::GstObject) as
|
|
||||||
*mut gst_ffi::GstStream,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Stream {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe { gst_ffi::gst_object_unref(self.0 as *mut gst_ffi::GstObject) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StreamCollection {
|
|
||||||
pub fn new(upstream_id: &str, streams: &[Stream]) -> Self {
|
|
||||||
let upstream_id_cstr = CString::new(upstream_id).unwrap();
|
|
||||||
let collection = StreamCollection(unsafe {
|
|
||||||
gst_ffi::gst_stream_collection_new(upstream_id_cstr.as_ptr())
|
|
||||||
});
|
|
||||||
|
|
||||||
for stream in streams {
|
|
||||||
unsafe { gst_ffi::gst_stream_collection_add_stream(collection.0, stream.clone().0) };
|
|
||||||
}
|
|
||||||
|
|
||||||
collection
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn streams(&self) -> StreamCollectionIterator {
|
|
||||||
StreamCollectionIterator::new(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn len(&self) -> u32 {
|
|
||||||
unsafe { gst_ffi::gst_stream_collection_get_size(self.0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn empty(&self) -> bool {
|
|
||||||
self.len() == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_upstream_id(&self) -> &str {
|
|
||||||
let cstr =
|
|
||||||
unsafe { CStr::from_ptr(gst_ffi::gst_stream_collection_get_upstream_id(self.0)) };
|
|
||||||
cstr.to_str().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn as_ptr(&self) -> *const gst_ffi::GstStreamCollection {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct StreamCollectionIterator<'a> {
|
|
||||||
position: u32,
|
|
||||||
length: u32,
|
|
||||||
collection: &'a StreamCollection,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> StreamCollectionIterator<'a> {
|
|
||||||
fn new(collection: &'a StreamCollection) -> Self {
|
|
||||||
StreamCollectionIterator {
|
|
||||||
position: 0,
|
|
||||||
length: collection.len(),
|
|
||||||
collection: collection,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Iterator for StreamCollectionIterator<'a> {
|
|
||||||
type Item = Stream;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Stream> {
|
|
||||||
if self.position == self.length {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let stream =
|
|
||||||
unsafe { gst_ffi::gst_stream_collection_get_stream(self.collection.0, self.position) };
|
|
||||||
if stream.is_null() {
|
|
||||||
self.position = self.length;
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
self.position += 1;
|
|
||||||
|
|
||||||
Some(unsafe {
|
|
||||||
Stream(
|
|
||||||
gst_ffi::gst_object_ref(stream as *mut gst_ffi::GstObject) as
|
|
||||||
*mut gst_ffi::GstStream,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
||||||
if self.position == self.length {
|
|
||||||
return (0, Some(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
let remaining = (self.length - self.position) as usize;
|
|
||||||
|
|
||||||
(remaining, Some(remaining))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> DoubleEndedIterator for StreamCollectionIterator<'a> {
|
|
||||||
fn next_back(&mut self) -> Option<Self::Item> {
|
|
||||||
if self.position == self.length {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.length -= 1;
|
|
||||||
|
|
||||||
let stream =
|
|
||||||
unsafe { gst_ffi::gst_stream_collection_get_stream(self.collection.0, self.length) };
|
|
||||||
if stream.is_null() {
|
|
||||||
self.position = self.length;
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(unsafe {
|
|
||||||
Stream(
|
|
||||||
gst_ffi::gst_object_ref(stream as *mut gst_ffi::GstObject) as
|
|
||||||
*mut gst_ffi::GstStream,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ExactSizeIterator for StreamCollectionIterator<'a> {}
|
|
||||||
|
|
||||||
impl Clone for StreamCollection {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
unsafe {
|
|
||||||
StreamCollection(
|
|
||||||
gst_ffi::gst_object_ref(self.0 as *mut gst_ffi::GstObject) as
|
|
||||||
*mut gst_ffi::GstStreamCollection,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for StreamCollection {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe { gst_ffi::gst_object_unref(self.0 as *mut gst_ffi::GstObject) }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,414 +0,0 @@
|
||||||
// Copyright (C) 2016-2017 Sebastian Dröge <sebastian@centricular.com>
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
use std::fmt;
|
|
||||||
use std::ptr;
|
|
||||||
use std::mem;
|
|
||||||
use std::ffi::{CStr, CString};
|
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
use std::borrow::{Borrow, BorrowMut, ToOwned};
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use value::*;
|
|
||||||
|
|
||||||
use glib_ffi;
|
|
||||||
use gst_ffi;
|
|
||||||
|
|
||||||
pub struct OwnedStructure(*mut Structure, PhantomData<Structure>);
|
|
||||||
|
|
||||||
impl OwnedStructure {
|
|
||||||
pub fn new_empty(name: &str) -> OwnedStructure {
|
|
||||||
let name_cstr = CString::new(name).unwrap();
|
|
||||||
OwnedStructure(
|
|
||||||
unsafe { gst_ffi::gst_structure_new_empty(name_cstr.as_ptr()) as *mut Structure },
|
|
||||||
PhantomData,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new(name: &str, values: &[(&str, Value)]) -> OwnedStructure {
|
|
||||||
let mut structure = OwnedStructure::new_empty(name);
|
|
||||||
|
|
||||||
for &(f, ref v) in values {
|
|
||||||
structure.set(f, v.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
structure
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_string(s: &str) -> Option<OwnedStructure> {
|
|
||||||
unsafe {
|
|
||||||
let cstr = CString::new(s).unwrap();
|
|
||||||
let structure = gst_ffi::gst_structure_from_string(cstr.as_ptr(), ptr::null_mut());
|
|
||||||
if structure.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(OwnedStructure(structure as *mut Structure, PhantomData))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn into_ptr(self) -> *mut gst_ffi::GstStructure {
|
|
||||||
let ptr = self.0 as *mut Structure as *mut gst_ffi::GstStructure;
|
|
||||||
mem::forget(self);
|
|
||||||
|
|
||||||
ptr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for OwnedStructure {
|
|
||||||
type Target = Structure;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Structure {
|
|
||||||
unsafe { &*self.0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for OwnedStructure {
|
|
||||||
fn deref_mut(&mut self) -> &mut Structure {
|
|
||||||
unsafe { &mut *self.0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsRef<Structure> for OwnedStructure {
|
|
||||||
fn as_ref(&self) -> &Structure {
|
|
||||||
self.deref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsMut<Structure> for OwnedStructure {
|
|
||||||
fn as_mut(&mut self) -> &mut Structure {
|
|
||||||
self.deref_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for OwnedStructure {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
OwnedStructure(
|
|
||||||
unsafe { gst_ffi::gst_structure_copy(&(*self.0).0) as *mut Structure },
|
|
||||||
PhantomData,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for OwnedStructure {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe { gst_ffi::gst_structure_free(&mut (*self.0).0) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for OwnedStructure {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
f.write_str(&self.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for OwnedStructure {
|
|
||||||
fn eq(&self, other: &OwnedStructure) -> bool {
|
|
||||||
self.as_ref().eq(other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq<Structure> for OwnedStructure {
|
|
||||||
fn eq(&self, other: &Structure) -> bool {
|
|
||||||
self.as_ref().eq(other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for OwnedStructure {}
|
|
||||||
|
|
||||||
impl Borrow<Structure> for OwnedStructure {
|
|
||||||
fn borrow(&self) -> &Structure {
|
|
||||||
unsafe { &*self.0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BorrowMut<Structure> for OwnedStructure {
|
|
||||||
fn borrow_mut(&mut self) -> &mut Structure {
|
|
||||||
unsafe { &mut *self.0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToOwned for Structure {
|
|
||||||
type Owned = OwnedStructure;
|
|
||||||
|
|
||||||
fn to_owned(&self) -> OwnedStructure {
|
|
||||||
OwnedStructure(
|
|
||||||
unsafe { gst_ffi::gst_structure_copy(&self.0) as *mut Structure },
|
|
||||||
PhantomData,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Structure(gst_ffi::GstStructure);
|
|
||||||
|
|
||||||
impl Structure {
|
|
||||||
pub unsafe fn from_borrowed_ptr<'a>(ptr: *const gst_ffi::GstStructure) -> &'a Structure {
|
|
||||||
assert!(!ptr.is_null());
|
|
||||||
|
|
||||||
&*(ptr as *mut Structure)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn from_borrowed_mut_ptr<'a>(ptr: *mut gst_ffi::GstStructure) -> &'a mut Structure {
|
|
||||||
assert!(!ptr.is_null());
|
|
||||||
|
|
||||||
&mut *(ptr as *mut Structure)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_string(&self) -> String {
|
|
||||||
unsafe {
|
|
||||||
let ptr = gst_ffi::gst_structure_to_string(&self.0);
|
|
||||||
let s = CStr::from_ptr(ptr).to_str().unwrap().into();
|
|
||||||
glib_ffi::g_free(ptr as glib_ffi::gpointer);
|
|
||||||
|
|
||||||
s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get<'a, T: ValueType<'a>>(&'a self, name: &str) -> Option<TypedValueRef<'a, T>> {
|
|
||||||
self.get_value(name).and_then(TypedValueRef::from_value_ref)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_value<'a>(&'a self, name: &str) -> Option<ValueRef<'a>> {
|
|
||||||
unsafe {
|
|
||||||
let name_cstr = CString::new(name).unwrap();
|
|
||||||
|
|
||||||
let value = gst_ffi::gst_structure_get_value(&self.0, name_cstr.as_ptr());
|
|
||||||
|
|
||||||
if value.is_null() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
ValueRef::from_ptr(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set<T: Into<Value>>(&mut self, name: &str, value: T) {
|
|
||||||
unsafe {
|
|
||||||
let name_cstr = CString::new(name).unwrap();
|
|
||||||
let mut gvalue = value.into().into_raw();
|
|
||||||
|
|
||||||
gst_ffi::gst_structure_take_value(&mut self.0, name_cstr.as_ptr(), &mut gvalue);
|
|
||||||
mem::forget(gvalue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_name(&self) -> &str {
|
|
||||||
unsafe {
|
|
||||||
let cstr = CStr::from_ptr(gst_ffi::gst_structure_get_name(&self.0));
|
|
||||||
cstr.to_str().unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn has_field(&self, field: &str) -> bool {
|
|
||||||
unsafe {
|
|
||||||
let cstr = CString::new(field).unwrap();
|
|
||||||
gst_ffi::gst_structure_has_field(&self.0, cstr.as_ptr()) == glib_ffi::GTRUE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_field(&mut self, field: &str) {
|
|
||||||
unsafe {
|
|
||||||
let cstr = CString::new(field).unwrap();
|
|
||||||
gst_ffi::gst_structure_remove_field(&mut self.0, cstr.as_ptr());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_all_fields(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
gst_ffi::gst_structure_remove_all_fields(&mut self.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fields(&self) -> FieldIterator {
|
|
||||||
FieldIterator::new(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter(&self) -> Iter {
|
|
||||||
Iter::new(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_nth_field_name(&self, idx: u32) -> Option<&str> {
|
|
||||||
unsafe {
|
|
||||||
let field_name = gst_ffi::gst_structure_nth_field_name(&self.0, idx);
|
|
||||||
if field_name.is_null() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let cstr = CStr::from_ptr(field_name);
|
|
||||||
Some(cstr.to_str().unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn n_fields(&self) -> u32 {
|
|
||||||
unsafe { gst_ffi::gst_structure_n_fields(&self.0) as u32 }
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Various operations
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Structure {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
f.write_str(&self.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for Structure {
|
|
||||||
fn eq(&self, other: &Structure) -> bool {
|
|
||||||
(unsafe { gst_ffi::gst_structure_is_equal(&self.0, &other.0) } == glib_ffi::GTRUE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for Structure {}
|
|
||||||
|
|
||||||
pub struct FieldIterator<'a> {
|
|
||||||
structure: &'a Structure,
|
|
||||||
idx: u32,
|
|
||||||
n_fields: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> FieldIterator<'a> {
|
|
||||||
pub fn new(structure: &'a Structure) -> FieldIterator<'a> {
|
|
||||||
let n_fields = structure.n_fields();
|
|
||||||
|
|
||||||
FieldIterator {
|
|
||||||
structure: structure,
|
|
||||||
idx: 0,
|
|
||||||
n_fields: n_fields,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Iterator for FieldIterator<'a> {
|
|
||||||
type Item = &'a str;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<&'a str> {
|
|
||||||
if self.idx >= self.n_fields {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(field_name) = self.structure.get_nth_field_name(self.idx) {
|
|
||||||
self.idx += 1;
|
|
||||||
Some(field_name)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
||||||
if self.idx == self.n_fields {
|
|
||||||
return (0, Some(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
let remaining = (self.n_fields - self.idx) as usize;
|
|
||||||
|
|
||||||
(remaining, Some(remaining))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> DoubleEndedIterator for FieldIterator<'a> {
|
|
||||||
fn next_back(&mut self) -> Option<Self::Item> {
|
|
||||||
if self.idx == self.n_fields {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.n_fields -= 1;
|
|
||||||
if let Some(field_name) = self.structure.get_nth_field_name(self.n_fields) {
|
|
||||||
Some(field_name)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ExactSizeIterator for FieldIterator<'a> {}
|
|
||||||
|
|
||||||
pub struct Iter<'a> {
|
|
||||||
iter: FieldIterator<'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Iter<'a> {
|
|
||||||
pub fn new(structure: &'a Structure) -> Iter<'a> {
|
|
||||||
Iter {
|
|
||||||
iter: FieldIterator::new(structure),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Iterator for Iter<'a> {
|
|
||||||
type Item = (&'a str, ValueRef<'a>);
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<(&'a str, ValueRef<'a>)> {
|
|
||||||
if let Some(f) = self.iter.next() {
|
|
||||||
let v = self.iter.structure.get_value(f);
|
|
||||||
Some((f, v.unwrap()))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
||||||
self.iter.size_hint()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> DoubleEndedIterator for Iter<'a> {
|
|
||||||
fn next_back(&mut self) -> Option<Self::Item> {
|
|
||||||
if let Some(f) = self.iter.next_back() {
|
|
||||||
let v = self.iter.structure.get_value(f);
|
|
||||||
Some((f, v.unwrap()))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ExactSizeIterator for Iter<'a> {}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use std::ptr;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn new_set_get() {
|
|
||||||
unsafe { gst_ffi::gst_init(ptr::null_mut(), ptr::null_mut()) };
|
|
||||||
|
|
||||||
let mut s = OwnedStructure::new_empty("test");
|
|
||||||
assert_eq!(s.get_name(), "test");
|
|
||||||
|
|
||||||
s.set("f1", "abc");
|
|
||||||
s.set("f2", String::from("bcd"));
|
|
||||||
s.set("f3", 123i32);
|
|
||||||
|
|
||||||
assert_eq!(s.get::<&str>("f1").unwrap().get(), "abc");
|
|
||||||
assert_eq!(s.get::<&str>("f2").unwrap().get(), "bcd");
|
|
||||||
assert_eq!(s.get::<i32>("f3").unwrap().get(), 123i32);
|
|
||||||
assert_eq!(s.fields().collect::<Vec<_>>(), vec!["f1", "f2", "f3"]);
|
|
||||||
assert_eq!(
|
|
||||||
s.iter()
|
|
||||||
.map(|(f, v)| (f, Value::from_value_ref(&v)))
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
vec![
|
|
||||||
("f1", Value::new("abc")),
|
|
||||||
("f2", Value::new("bcd")),
|
|
||||||
("f3", Value::new(123i32)),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
let s2 = OwnedStructure::new(
|
|
||||||
"test",
|
|
||||||
&[
|
|
||||||
("f1", "abc".into()),
|
|
||||||
("f2", "bcd".into()),
|
|
||||||
("f3", 123i32.into()),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
assert_eq!(s, s2);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,290 +0,0 @@
|
||||||
// Copyright (C) 2016-2017 Sebastian Dröge <sebastian@centricular.com>
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
use std::fmt;
|
|
||||||
use std::mem;
|
|
||||||
use std::ffi::{CStr, CString};
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
use value::*;
|
|
||||||
use miniobject::*;
|
|
||||||
|
|
||||||
use glib_ffi;
|
|
||||||
use gobject_ffi;
|
|
||||||
use gst_ffi;
|
|
||||||
|
|
||||||
pub trait Tag<'a> {
|
|
||||||
type TagType: ValueType<'a>;
|
|
||||||
fn tag_name() -> &'static str;
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_tag(
|
|
||||||
($name:ident, $t:ty, $tag:expr) => {
|
|
||||||
pub struct $name;
|
|
||||||
impl<'a> Tag<'a> for $name {
|
|
||||||
type TagType = $t;
|
|
||||||
fn tag_name() -> &'static str {
|
|
||||||
$tag
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
);
|
|
||||||
|
|
||||||
impl_tag!(Title, &'a str, "title");
|
|
||||||
impl_tag!(Album, &'a str, "album");
|
|
||||||
impl_tag!(Artist, &'a str, "artist");
|
|
||||||
impl_tag!(Encoder, &'a str, "encoder");
|
|
||||||
impl_tag!(AudioCodec, &'a str, "audio-codec");
|
|
||||||
impl_tag!(VideoCodec, &'a str, "video-codec");
|
|
||||||
impl_tag!(SubtitleCodec, &'a str, "subtitle-codec");
|
|
||||||
impl_tag!(ContainerFormat, &'a str, "container-format");
|
|
||||||
// TODO: Should ideally enforce this to be ISO-639
|
|
||||||
impl_tag!(LanguageCode, &'a str, "language-code");
|
|
||||||
impl_tag!(Duration, u64, "duration");
|
|
||||||
impl_tag!(NominalBitrate, u32, "nominal-bitrate");
|
|
||||||
|
|
||||||
pub enum MergeMode {
|
|
||||||
ReplaceAll,
|
|
||||||
Replace,
|
|
||||||
Append,
|
|
||||||
Prepend,
|
|
||||||
Keep,
|
|
||||||
KeepAll,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MergeMode {
|
|
||||||
fn to_ffi(&self) -> gst_ffi::GstTagMergeMode {
|
|
||||||
match *self {
|
|
||||||
MergeMode::ReplaceAll => gst_ffi::GST_TAG_MERGE_REPLACE_ALL,
|
|
||||||
MergeMode::Replace => gst_ffi::GST_TAG_MERGE_REPLACE,
|
|
||||||
MergeMode::Append => gst_ffi::GST_TAG_MERGE_APPEND,
|
|
||||||
MergeMode::Prepend => gst_ffi::GST_TAG_MERGE_PREPEND,
|
|
||||||
MergeMode::Keep => gst_ffi::GST_TAG_MERGE_KEEP,
|
|
||||||
MergeMode::KeepAll => gst_ffi::GST_TAG_MERGE_KEEP_ALL,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TagList(gst_ffi::GstTagList);
|
|
||||||
|
|
||||||
unsafe impl MiniObject for TagList {
|
|
||||||
type PtrType = gst_ffi::GstTagList;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TagList {
|
|
||||||
pub fn new() -> GstRc<Self> {
|
|
||||||
unsafe { GstRc::from_owned_ptr(gst_ffi::gst_tag_list_new_empty()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add<'a, T: Tag<'a>>(&mut self, value: T::TagType, mode: MergeMode)
|
|
||||||
where
|
|
||||||
T::TagType: Into<Value>,
|
|
||||||
{
|
|
||||||
unsafe {
|
|
||||||
let v = value.into();
|
|
||||||
let mut gvalue = v.into_raw();
|
|
||||||
let tag_name = CString::new(T::tag_name()).unwrap();
|
|
||||||
|
|
||||||
gst_ffi::gst_tag_list_add_value(
|
|
||||||
self.as_mut_ptr(),
|
|
||||||
mode.to_ffi(),
|
|
||||||
tag_name.as_ptr(),
|
|
||||||
&gvalue,
|
|
||||||
);
|
|
||||||
|
|
||||||
gobject_ffi::g_value_unset(&mut gvalue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get<'a, T: Tag<'a>>(&self) -> Option<TypedValue<T::TagType>> {
|
|
||||||
unsafe {
|
|
||||||
let mut gvalue = mem::zeroed();
|
|
||||||
let tag_name = CString::new(T::tag_name()).unwrap();
|
|
||||||
|
|
||||||
let found =
|
|
||||||
gst_ffi::gst_tag_list_copy_value(&mut gvalue, self.as_ptr(), tag_name.as_ptr());
|
|
||||||
|
|
||||||
if found == glib_ffi::GFALSE {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Value::from_raw(gvalue).and_then(TypedValue::from_value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_index<'a, T: Tag<'a>>(&'a self, idx: u32) -> Option<TypedValueRef<'a, T::TagType>> {
|
|
||||||
unsafe {
|
|
||||||
let tag_name = CString::new(T::tag_name()).unwrap();
|
|
||||||
|
|
||||||
let value =
|
|
||||||
gst_ffi::gst_tag_list_get_value_index(self.as_ptr(), tag_name.as_ptr(), idx);
|
|
||||||
|
|
||||||
if value.is_null() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
ValueRef::from_ptr(value).and_then(TypedValueRef::from_value_ref)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_size<'a, T: Tag<'a>>(&'a self) -> u32 {
|
|
||||||
unsafe {
|
|
||||||
let tag_name = CString::new(T::tag_name()).unwrap();
|
|
||||||
|
|
||||||
gst_ffi::gst_tag_list_get_tag_size(self.as_ptr(), tag_name.as_ptr())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter_tag<'a, T: Tag<'a>>(&'a self) -> TagIterator<'a, T> {
|
|
||||||
TagIterator::new(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_string(&self) -> String {
|
|
||||||
unsafe {
|
|
||||||
let ptr = gst_ffi::gst_tag_list_to_string(self.as_ptr());
|
|
||||||
let s = CStr::from_ptr(ptr).to_str().unwrap().into();
|
|
||||||
glib_ffi::g_free(ptr as glib_ffi::gpointer);
|
|
||||||
|
|
||||||
s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for TagList {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
f.write_str(&self.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for TagList {
|
|
||||||
fn eq(&self, other: &TagList) -> bool {
|
|
||||||
(unsafe { gst_ffi::gst_tag_list_is_equal(self.as_ptr(), other.as_ptr()) } ==
|
|
||||||
glib_ffi::GTRUE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for TagList {}
|
|
||||||
|
|
||||||
impl ToOwned for TagList {
|
|
||||||
type Owned = GstRc<TagList>;
|
|
||||||
|
|
||||||
fn to_owned(&self) -> GstRc<TagList> {
|
|
||||||
unsafe { GstRc::from_unowned_ptr(self.as_ptr()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Sync for TagList {}
|
|
||||||
unsafe impl Send for TagList {}
|
|
||||||
|
|
||||||
pub struct TagIterator<'a, T: Tag<'a>> {
|
|
||||||
taglist: &'a TagList,
|
|
||||||
idx: u32,
|
|
||||||
size: u32,
|
|
||||||
phantom: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: Tag<'a>> TagIterator<'a, T> {
|
|
||||||
fn new(taglist: &'a TagList) -> TagIterator<'a, T> {
|
|
||||||
TagIterator {
|
|
||||||
taglist: taglist,
|
|
||||||
idx: 0,
|
|
||||||
size: taglist.get_size::<T>(),
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: Tag<'a>> Iterator for TagIterator<'a, T> {
|
|
||||||
type Item = TypedValueRef<'a, T::TagType>;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
if self.idx >= self.size {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let item = self.taglist.get_index::<T>(self.idx);
|
|
||||||
self.idx += 1;
|
|
||||||
|
|
||||||
item
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
||||||
if self.idx == self.size {
|
|
||||||
return (0, Some(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
let remaining = (self.size - self.idx) as usize;
|
|
||||||
|
|
||||||
(remaining, Some(remaining))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: Tag<'a>> DoubleEndedIterator for TagIterator<'a, T> {
|
|
||||||
fn next_back(&mut self) -> Option<Self::Item> {
|
|
||||||
if self.idx == self.size {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.size -= 1;
|
|
||||||
self.taglist.get_index::<T>(self.size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: Tag<'a>> ExactSizeIterator for TagIterator<'a, T> {}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use std::ptr;
|
|
||||||
|
|
||||||
fn init() {
|
|
||||||
unsafe {
|
|
||||||
gst_ffi::gst_init(ptr::null_mut(), ptr::null_mut());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_add() {
|
|
||||||
init();
|
|
||||||
|
|
||||||
let mut tags = TagList::new();
|
|
||||||
assert_eq!(tags.to_string(), "taglist;");
|
|
||||||
{
|
|
||||||
let tags = tags.get_mut().unwrap();
|
|
||||||
tags.add::<Title>("some title".into(), MergeMode::Append);
|
|
||||||
tags.add::<Duration>((1000u64 * 1000 * 1000 * 120).into(), MergeMode::Append);
|
|
||||||
}
|
|
||||||
assert_eq!(
|
|
||||||
tags.to_string(),
|
|
||||||
"taglist, title=(string)\"some\\ title\", duration=(guint64)120000000000;"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_get() {
|
|
||||||
init();
|
|
||||||
|
|
||||||
let mut tags = TagList::new();
|
|
||||||
assert_eq!(tags.to_string(), "taglist;");
|
|
||||||
{
|
|
||||||
let tags = tags.get_mut().unwrap();
|
|
||||||
tags.add::<Title>("some title".into(), MergeMode::Append);
|
|
||||||
tags.add::<Duration>((1000u64 * 1000 * 1000 * 120).into(), MergeMode::Append);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(tags.get::<Title>().unwrap().get(), "some title");
|
|
||||||
assert_eq!(
|
|
||||||
tags.get::<Duration>().unwrap().get(),
|
|
||||||
(1000u64 * 1000 * 1000 * 120)
|
|
||||||
);
|
|
||||||
assert_eq!(tags.get_index::<Title>(0).unwrap().get(), "some title");
|
|
||||||
assert_eq!(
|
|
||||||
tags.get_index::<Duration>(0).unwrap().get(),
|
|
||||||
(1000u64 * 1000 * 1000 * 120)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,159 +8,8 @@
|
||||||
|
|
||||||
use libc::c_char;
|
use libc::c_char;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::i32;
|
|
||||||
use num_rational::Rational32;
|
|
||||||
|
|
||||||
use gst_ffi;
|
|
||||||
|
|
||||||
pub struct Element(*mut gst_ffi::GstElement);
|
|
||||||
|
|
||||||
impl Element {
|
|
||||||
pub unsafe fn new(element: *mut gst_ffi::GstElement) -> Element {
|
|
||||||
if element.is_null() {
|
|
||||||
panic!("NULL not allowed");
|
|
||||||
}
|
|
||||||
|
|
||||||
gst_ffi::gst_object_ref(element as *mut gst_ffi::GstObject);
|
|
||||||
|
|
||||||
Element(element)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn as_ptr(&self) -> *mut gst_ffi::GstElement {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Element {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
gst_ffi::gst_object_unref(self.0 as *mut gst_ffi::GstObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for Element {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
unsafe { Element::new(self.0) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn cstring_drop(ptr: *mut c_char) {
|
pub unsafe extern "C" fn cstring_drop(ptr: *mut c_char) {
|
||||||
let _ = CString::from_raw(ptr);
|
let _ = CString::from_raw(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn f64_to_fraction(val: f64) -> Option<Rational32> {
|
|
||||||
// Continued fractions algorithm
|
|
||||||
// http://mathforum.org/dr.math/faq/faq.fractions.html#decfrac
|
|
||||||
|
|
||||||
let negative = val < 0.0;
|
|
||||||
|
|
||||||
let mut q = val.abs();
|
|
||||||
let mut n0 = 0;
|
|
||||||
let mut d0 = 1;
|
|
||||||
let mut n1 = 1;
|
|
||||||
let mut d1 = 0;
|
|
||||||
|
|
||||||
const MAX_ITERATIONS: usize = 30;
|
|
||||||
const MAX_ERROR: f64 = 1.0e-20;
|
|
||||||
// 1/EPSILON > i32::MAX
|
|
||||||
const EPSILON: f64 = 1.0e-10;
|
|
||||||
|
|
||||||
// Overflow
|
|
||||||
if q > i32::MAX as f64 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
for _ in 0..MAX_ITERATIONS {
|
|
||||||
let a = q as u32;
|
|
||||||
let f = q - (a as f64);
|
|
||||||
|
|
||||||
// Prevent overflow
|
|
||||||
if a != 0 &&
|
|
||||||
(n1 > (i32::MAX as u32) / a || d1 > (i32::MAX as u32) / a ||
|
|
||||||
a * n1 > (i32::MAX as u32) - n0 || a * d1 > (i32::MAX as u32) - d0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let n = a * n1 + n0;
|
|
||||||
let d = a * d1 + d0;
|
|
||||||
|
|
||||||
n0 = n1;
|
|
||||||
d0 = d1;
|
|
||||||
n1 = n;
|
|
||||||
d1 = d;
|
|
||||||
|
|
||||||
// Prevent division by ~0
|
|
||||||
if f < EPSILON {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let r = 1.0 / f;
|
|
||||||
|
|
||||||
// Simplify fraction. Doing so here instead of at the end
|
|
||||||
// allows us to get closer to the target value without overflows
|
|
||||||
let g = gcd(n1, d1);
|
|
||||||
if g != 0 {
|
|
||||||
n1 /= g;
|
|
||||||
d1 /= g;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close enough?
|
|
||||||
if ((n as f64) / (d as f64) - val).abs() < MAX_ERROR {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
q = r;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Guaranteed by the overflow check
|
|
||||||
assert!(n1 <= i32::MAX as u32);
|
|
||||||
assert!(d1 <= i32::MAX as u32);
|
|
||||||
|
|
||||||
// Overflow
|
|
||||||
if d1 == 0 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make negative again if needed
|
|
||||||
if negative {
|
|
||||||
Some(Rational32::new(-(n1 as i32), d1 as i32))
|
|
||||||
} else {
|
|
||||||
Some(Rational32::new(n1 as i32, d1 as i32))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn gcd(mut a: u32, mut b: u32) -> u32 {
|
|
||||||
// Euclid's algorithm
|
|
||||||
while b != 0 {
|
|
||||||
let tmp = a;
|
|
||||||
a = b;
|
|
||||||
b = tmp % b;
|
|
||||||
}
|
|
||||||
|
|
||||||
a
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use num_rational::Rational32;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_gcd() {
|
|
||||||
assert_eq!(gcd(2 * 2 * 2 * 2, 2 * 2 * 2 * 3), 2 * 2 * 2);
|
|
||||||
assert_eq!(gcd(2 * 3 * 5 * 5 * 7, 2 * 5 * 7), 2 * 5 * 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_f64_to_fraction() {
|
|
||||||
assert_eq!(f64_to_fraction(2.0), Some(Rational32::new(2, 1)));
|
|
||||||
assert_eq!(f64_to_fraction(2.5), Some(Rational32::new(5, 2)));
|
|
||||||
assert_eq!(
|
|
||||||
f64_to_fraction(0.127659574),
|
|
||||||
Some(Rational32::new(29013539, 227272723))
|
|
||||||
);
|
|
||||||
assert_eq!(f64_to_fraction(29.97), Some(Rational32::new(2997, 100)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,960 +0,0 @@
|
||||||
// Copyright (C) 2016-2017 Sebastian Dröge <sebastian@centricular.com>
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
use std::ffi::CStr;
|
|
||||||
use std::mem;
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::fmt;
|
|
||||||
use std::slice;
|
|
||||||
use libc::c_char;
|
|
||||||
|
|
||||||
pub use num_rational::Rational32;
|
|
||||||
|
|
||||||
use buffer::*;
|
|
||||||
use miniobject::*;
|
|
||||||
|
|
||||||
use glib_ffi;
|
|
||||||
use gobject_ffi;
|
|
||||||
use gst_ffi;
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Value(gobject_ffi::GValue);
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
||||||
pub enum ValueView<'a> {
|
|
||||||
Bool(bool),
|
|
||||||
Int(i32),
|
|
||||||
UInt(u32),
|
|
||||||
Int64(i64),
|
|
||||||
UInt64(u64),
|
|
||||||
String(Cow<'a, str>),
|
|
||||||
Fraction(Rational32),
|
|
||||||
Buffer(GstRc<Buffer>),
|
|
||||||
Array(Cow<'a, [Value]>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ValueView<'a> {
|
|
||||||
pub fn try_get<T: ValueType<'a>>(&'a self) -> Option<T> {
|
|
||||||
T::from_value_view(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ValueType<'a>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
fn g_type() -> glib_ffi::GType;
|
|
||||||
|
|
||||||
fn from_value(v: &'a gobject_ffi::GValue) -> Option<Self>;
|
|
||||||
fn from_value_view(v: &'a ValueView<'a>) -> Option<Self>;
|
|
||||||
}
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref TYPE_BUFFER: glib_ffi::GType = unsafe { gst_ffi::gst_buffer_get_type() };
|
|
||||||
static ref TYPE_FRACTION: glib_ffi::GType = unsafe { gst_ffi::gst_fraction_get_type() };
|
|
||||||
static ref TYPE_GST_VALUE_ARRAY: glib_ffi::GType = unsafe { gst_ffi::gst_value_array_get_type() };
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Value {
|
|
||||||
pub unsafe fn as_ptr(&self) -> *const gobject_ffi::GValue {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn from_ptr(ptr: *const gobject_ffi::GValue) -> Option<Value> {
|
|
||||||
if ptr.is_null() || !Value::is_supported_type((*ptr).g_type) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut value = Value(mem::zeroed());
|
|
||||||
gobject_ffi::g_value_init(&mut value.0, (*ptr).g_type);
|
|
||||||
gobject_ffi::g_value_copy(ptr, &mut value.0);
|
|
||||||
|
|
||||||
Some(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_value_ref<'a>(v: &ValueRef<'a>) -> Value {
|
|
||||||
unsafe { Value::from_ptr(v.0) }.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn from_raw(value: gobject_ffi::GValue) -> Option<Value> {
|
|
||||||
if !Value::is_supported_type(value.g_type) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(Value(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn into_raw(mut self) -> gobject_ffi::GValue {
|
|
||||||
let v = mem::replace(&mut self.0, mem::zeroed());
|
|
||||||
mem::forget(self);
|
|
||||||
|
|
||||||
v
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_supported_type(typ: glib_ffi::GType) -> bool {
|
|
||||||
match typ {
|
|
||||||
gobject_ffi::G_TYPE_BOOLEAN |
|
|
||||||
gobject_ffi::G_TYPE_INT |
|
|
||||||
gobject_ffi::G_TYPE_UINT |
|
|
||||||
gobject_ffi::G_TYPE_INT64 |
|
|
||||||
gobject_ffi::G_TYPE_UINT64 |
|
|
||||||
gobject_ffi::G_TYPE_STRING => true,
|
|
||||||
typ if typ == *TYPE_FRACTION => true,
|
|
||||||
//typ if typ == *TYPE_BUFFER => true
|
|
||||||
typ if typ == *TYPE_GST_VALUE_ARRAY => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new<T: Into<Value>>(v: T) -> Value {
|
|
||||||
v.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_value_view(v: ValueView) -> Value {
|
|
||||||
match v {
|
|
||||||
ValueView::Bool(v) => Value::from(v),
|
|
||||||
ValueView::Int(v) => Value::from(v),
|
|
||||||
ValueView::UInt(v) => Value::from(v),
|
|
||||||
ValueView::Int64(v) => Value::from(v),
|
|
||||||
ValueView::UInt64(v) => Value::from(v),
|
|
||||||
ValueView::Fraction(v) => Value::from(v),
|
|
||||||
ValueView::String(v) => Value::from(v),
|
|
||||||
ValueView::Array(v) => Value::from(v),
|
|
||||||
ValueView::Buffer(v) => Value::from(v),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&self) -> ValueView {
|
|
||||||
match self.0.g_type {
|
|
||||||
gobject_ffi::G_TYPE_BOOLEAN => ValueView::Bool(bool::from_value(&self.0).unwrap()),
|
|
||||||
gobject_ffi::G_TYPE_INT => ValueView::Int(i32::from_value(&self.0).unwrap()),
|
|
||||||
gobject_ffi::G_TYPE_UINT => ValueView::UInt(u32::from_value(&self.0).unwrap()),
|
|
||||||
gobject_ffi::G_TYPE_INT64 => ValueView::Int64(i64::from_value(&self.0).unwrap()),
|
|
||||||
gobject_ffi::G_TYPE_UINT64 => ValueView::UInt64(u64::from_value(&self.0).unwrap()),
|
|
||||||
typ if typ == *TYPE_FRACTION => {
|
|
||||||
ValueView::Fraction(Rational32::from_value(&self.0).unwrap())
|
|
||||||
}
|
|
||||||
gobject_ffi::G_TYPE_STRING => ValueView::String(Cow::Borrowed(
|
|
||||||
<&str as ValueType>::from_value(&self.0).unwrap(),
|
|
||||||
)),
|
|
||||||
typ if typ == *TYPE_GST_VALUE_ARRAY => ValueView::Array(Cow::Borrowed(
|
|
||||||
<&[Value] as ValueType>::from_value(&self.0).unwrap(),
|
|
||||||
)),
|
|
||||||
typ if typ == *TYPE_BUFFER => {
|
|
||||||
ValueView::Buffer(<GstRc<Buffer> as ValueType>::from_value(&self.0).unwrap())
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn try_get<'a, T: ValueType<'a>>(&'a self) -> Option<T> {
|
|
||||||
T::from_value(&self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for Value {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
unsafe {
|
|
||||||
let mut new_value = Value(mem::zeroed());
|
|
||||||
gobject_ffi::g_value_init(&mut new_value.0, self.0.g_type);
|
|
||||||
gobject_ffi::g_value_copy(&self.0, &mut new_value.0);
|
|
||||||
|
|
||||||
new_value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for Value {
|
|
||||||
fn eq(&self, other: &Value) -> bool {
|
|
||||||
self.get().eq(&other.get())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Eq for Value {}
|
|
||||||
|
|
||||||
impl<'a> PartialEq<ValueRef<'a>> for Value {
|
|
||||||
fn eq(&self, other: &ValueRef<'a>) -> bool {
|
|
||||||
self.get().eq(&other.get())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Value {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
self.get().fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for Value {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
if self.0.g_type != gobject_ffi::G_TYPE_NONE {
|
|
||||||
gobject_ffi::g_value_unset(&mut self.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ValueRef<'a>(&'a gobject_ffi::GValue);
|
|
||||||
|
|
||||||
impl<'a> ValueRef<'a> {
|
|
||||||
pub unsafe fn as_ptr(&self) -> *const gobject_ffi::GValue {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_value(v: &'a Value) -> ValueRef<'a> {
|
|
||||||
ValueRef(&v.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn from_ptr(ptr: *const gobject_ffi::GValue) -> Option<ValueRef<'a>> {
|
|
||||||
if ptr.is_null() || !Value::is_supported_type((*ptr).g_type) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(ValueRef(&*ptr))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&self) -> ValueView {
|
|
||||||
match self.0.g_type {
|
|
||||||
gobject_ffi::G_TYPE_BOOLEAN => ValueView::Bool(bool::from_value(self.0).unwrap()),
|
|
||||||
gobject_ffi::G_TYPE_INT => ValueView::Int(i32::from_value(self.0).unwrap()),
|
|
||||||
gobject_ffi::G_TYPE_UINT => ValueView::UInt(u32::from_value(self.0).unwrap()),
|
|
||||||
gobject_ffi::G_TYPE_INT64 => ValueView::Int64(i64::from_value(self.0).unwrap()),
|
|
||||||
gobject_ffi::G_TYPE_UINT64 => ValueView::UInt64(u64::from_value(self.0).unwrap()),
|
|
||||||
typ if typ == *TYPE_FRACTION => {
|
|
||||||
ValueView::Fraction(Rational32::from_value(self.0).unwrap())
|
|
||||||
}
|
|
||||||
gobject_ffi::G_TYPE_STRING => ValueView::String(Cow::Borrowed(
|
|
||||||
<&str as ValueType>::from_value(self.0).unwrap(),
|
|
||||||
)),
|
|
||||||
typ if typ == *TYPE_GST_VALUE_ARRAY => ValueView::Array(Cow::Borrowed(
|
|
||||||
<&[Value] as ValueType>::from_value(self.0).unwrap(),
|
|
||||||
)),
|
|
||||||
typ if typ == *TYPE_BUFFER => {
|
|
||||||
ValueView::Buffer(<GstRc<Buffer> as ValueType>::from_value(self.0).unwrap())
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn try_get<T: ValueType<'a>>(&self) -> Option<T> {
|
|
||||||
T::from_value(self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> PartialEq for ValueRef<'a> {
|
|
||||||
fn eq(&self, other: &ValueRef<'a>) -> bool {
|
|
||||||
self.get().eq(&other.get())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'a> Eq for ValueRef<'a> {}
|
|
||||||
|
|
||||||
impl<'a> PartialEq<Value> for ValueRef<'a> {
|
|
||||||
fn eq(&self, other: &Value) -> bool {
|
|
||||||
self.get().eq(&other.get())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> fmt::Debug for ValueRef<'a> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
self.get().fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_value_type_simple(
|
|
||||||
($typ:ty, $variant:ident, $g_type:expr, $getter:expr, $setter:expr) => {
|
|
||||||
impl<'a> ValueType<'a> for $typ {
|
|
||||||
fn g_type() -> glib_ffi::GType {
|
|
||||||
$g_type
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_value(value: &'a gobject_ffi::GValue) -> Option<Self> {
|
|
||||||
if value.g_type != Self::g_type() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
Some($getter(&value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_value_view(value_view: &'a ValueView<'a>) -> Option<Self> {
|
|
||||||
if let ValueView::$variant(ref v) = *value_view {
|
|
||||||
Some(*v)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<$typ> for Value {
|
|
||||||
fn from(v: $typ) -> Value {
|
|
||||||
unsafe {
|
|
||||||
let mut value = Value(mem::zeroed());
|
|
||||||
|
|
||||||
gobject_ffi::g_value_init(&mut value.0, <$typ as ValueType>::g_type());
|
|
||||||
$setter(&mut value.0, v);
|
|
||||||
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
);
|
|
||||||
|
|
||||||
impl_value_type_simple!(
|
|
||||||
bool,
|
|
||||||
Bool,
|
|
||||||
gobject_ffi::G_TYPE_BOOLEAN,
|
|
||||||
|value: &gobject_ffi::GValue| !(gobject_ffi::g_value_get_boolean(value) == 0),
|
|
||||||
|value: &mut gobject_ffi::GValue, v| {
|
|
||||||
gobject_ffi::g_value_set_boolean(value, if v { glib_ffi::GTRUE } else { glib_ffi::GFALSE })
|
|
||||||
}
|
|
||||||
);
|
|
||||||
impl_value_type_simple!(
|
|
||||||
i32,
|
|
||||||
Int,
|
|
||||||
gobject_ffi::G_TYPE_INT,
|
|
||||||
|value: &gobject_ffi::GValue| gobject_ffi::g_value_get_int(value),
|
|
||||||
|value: &mut gobject_ffi::GValue, v| gobject_ffi::g_value_set_int(value, v)
|
|
||||||
);
|
|
||||||
impl_value_type_simple!(
|
|
||||||
u32,
|
|
||||||
UInt,
|
|
||||||
gobject_ffi::G_TYPE_UINT,
|
|
||||||
|value: &gobject_ffi::GValue| gobject_ffi::g_value_get_uint(value),
|
|
||||||
|value: &mut gobject_ffi::GValue, v| gobject_ffi::g_value_set_uint(value, v)
|
|
||||||
);
|
|
||||||
impl_value_type_simple!(
|
|
||||||
i64,
|
|
||||||
Int64,
|
|
||||||
gobject_ffi::G_TYPE_INT64,
|
|
||||||
|value: &gobject_ffi::GValue| gobject_ffi::g_value_get_int64(value),
|
|
||||||
|value: &mut gobject_ffi::GValue, v| gobject_ffi::g_value_set_int64(value, v)
|
|
||||||
);
|
|
||||||
impl_value_type_simple!(
|
|
||||||
u64,
|
|
||||||
UInt64,
|
|
||||||
gobject_ffi::G_TYPE_UINT64,
|
|
||||||
|value: &gobject_ffi::GValue| gobject_ffi::g_value_get_uint64(value),
|
|
||||||
|value: &mut gobject_ffi::GValue, v| gobject_ffi::g_value_set_uint64(value, v)
|
|
||||||
);
|
|
||||||
impl_value_type_simple!(
|
|
||||||
Rational32,
|
|
||||||
Fraction,
|
|
||||||
*TYPE_FRACTION,
|
|
||||||
|value: &gobject_ffi::GValue| {
|
|
||||||
Rational32::new(
|
|
||||||
gst_ffi::gst_value_get_fraction_numerator(value),
|
|
||||||
gst_ffi::gst_value_get_fraction_denominator(value),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
|value: &mut gobject_ffi::GValue, v: Rational32| {
|
|
||||||
gst_ffi::gst_value_set_fraction(value, *v.numer(), *v.denom())
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
impl<'a> ValueType<'a> for &'a str {
|
|
||||||
fn g_type() -> glib_ffi::GType {
|
|
||||||
gobject_ffi::G_TYPE_STRING
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_value(value: &'a gobject_ffi::GValue) -> Option<Self> {
|
|
||||||
if value.g_type != Self::g_type() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let s = gobject_ffi::g_value_get_string(value);
|
|
||||||
if s.is_null() {
|
|
||||||
return Some("");
|
|
||||||
}
|
|
||||||
|
|
||||||
let cstr = CStr::from_ptr(s).to_str().expect("Invalid string");
|
|
||||||
Some(cstr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_value_view(value_view: &'a ValueView<'a>) -> Option<Self> {
|
|
||||||
if let ValueView::String(ref v) = *value_view {
|
|
||||||
Some(v.as_ref())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<Cow<'a, str>> for Value {
|
|
||||||
fn from(v: Cow<'a, str>) -> Value {
|
|
||||||
unsafe {
|
|
||||||
let mut value = Value(mem::zeroed());
|
|
||||||
|
|
||||||
gobject_ffi::g_value_init(&mut value.0, <&str as ValueType>::g_type());
|
|
||||||
let v_cstr = glib_ffi::g_strndup(v.as_ptr() as *const c_char, v.len());
|
|
||||||
gobject_ffi::g_value_take_string(&mut value.0, v_cstr);
|
|
||||||
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<String> for Value {
|
|
||||||
fn from(v: String) -> Value {
|
|
||||||
Value::from(Cow::Owned::<str>(v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a str> for Value {
|
|
||||||
fn from(v: &'a str) -> Value {
|
|
||||||
Value::from(Cow::Borrowed::<str>(v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ValueType<'a> for GstRc<Buffer> {
|
|
||||||
fn g_type() -> glib_ffi::GType {
|
|
||||||
*TYPE_BUFFER
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_value(value: &'a gobject_ffi::GValue) -> Option<Self> {
|
|
||||||
if value.g_type != Self::g_type() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let buffer = gobject_ffi::g_value_get_boxed(value) as *mut gst_ffi::GstBuffer;
|
|
||||||
Some(GstRc::from_unowned_ptr(buffer))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_value_view(value_view: &'a ValueView<'a>) -> Option<Self> {
|
|
||||||
if let ValueView::Buffer(ref v) = *value_view {
|
|
||||||
Some(v.clone())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<GstRc<Buffer>> for Value {
|
|
||||||
fn from(v: GstRc<Buffer>) -> Value {
|
|
||||||
Value::from(v.as_ref())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a GstRc<Buffer>> for Value {
|
|
||||||
fn from(v: &'a GstRc<Buffer>) -> Value {
|
|
||||||
Value::from(v.as_ref())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a Buffer> for Value {
|
|
||||||
fn from(v: &'a Buffer) -> Value {
|
|
||||||
unsafe {
|
|
||||||
let mut value = Value(mem::zeroed());
|
|
||||||
|
|
||||||
gobject_ffi::g_value_init(&mut value.0, <GstRc<Buffer> as ValueType>::g_type());
|
|
||||||
gobject_ffi::g_value_set_boxed(&mut value.0, v.as_ptr() as glib_ffi::gpointer);
|
|
||||||
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ValueType<'a> for &'a [Value] {
|
|
||||||
fn g_type() -> glib_ffi::GType {
|
|
||||||
*TYPE_GST_VALUE_ARRAY
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_value(value: &'a gobject_ffi::GValue) -> Option<Self> {
|
|
||||||
if value.g_type != Self::g_type() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let arr = value.data[0] as *const glib_ffi::GArray;
|
|
||||||
|
|
||||||
if arr.is_null() {
|
|
||||||
Some(&[])
|
|
||||||
} else {
|
|
||||||
let arr = &*arr;
|
|
||||||
Some(slice::from_raw_parts(
|
|
||||||
arr.data as *const Value,
|
|
||||||
arr.len as usize,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_value_view(value_view: &'a ValueView<'a>) -> Option<Self> {
|
|
||||||
if let ValueView::Array(ref v) = *value_view {
|
|
||||||
Some(v.as_ref())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<Cow<'a, [Value]>> for Value {
|
|
||||||
fn from(v: Cow<'a, [Value]>) -> Value {
|
|
||||||
unsafe {
|
|
||||||
let mut value = Value(mem::zeroed());
|
|
||||||
|
|
||||||
gobject_ffi::g_value_init(&mut value.0, <&[Value] as ValueType>::g_type());
|
|
||||||
|
|
||||||
match v {
|
|
||||||
Cow::Borrowed(array) => for e in array {
|
|
||||||
gst_ffi::gst_value_array_append_value(
|
|
||||||
&mut value.0,
|
|
||||||
e.as_ptr() as *mut gobject_ffi::GValue,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
Cow::Owned(array) => for e in array {
|
|
||||||
gst_ffi::gst_value_array_append_and_take_value(
|
|
||||||
&mut value.0,
|
|
||||||
e.as_ptr() as *mut gobject_ffi::GValue,
|
|
||||||
);
|
|
||||||
mem::forget(e);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<Value>> for Value {
|
|
||||||
fn from(v: Vec<Value>) -> Value {
|
|
||||||
Value::from(Cow::Owned::<[Value]>(v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a Vec<Value>> for Value {
|
|
||||||
fn from(v: &'a Vec<Value>) -> Value {
|
|
||||||
Value::from(Cow::Borrowed::<[Value]>(v.as_ref()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a [Value]> for Value {
|
|
||||||
fn from(v: &'a [Value]) -> Value {
|
|
||||||
Value::from(Cow::Borrowed::<[Value]>(v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<ValueView<'a>> for Value {
|
|
||||||
fn from(value_view: ValueView<'a>) -> Value {
|
|
||||||
Value::from_value_view(value_view)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a ValueView<'a>> for Value {
|
|
||||||
fn from(value_view: &'a ValueView<'a>) -> Value {
|
|
||||||
Value::from_value_view(value_view.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<(i32, i32)> for Value {
|
|
||||||
fn from((f_n, f_d): (i32, i32)) -> Value {
|
|
||||||
Value::from(Rational32::new(f_n, f_d))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
||||||
pub struct TypedValue<T> {
|
|
||||||
value: Value,
|
|
||||||
phantom: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> TypedValue<T>
|
|
||||||
where
|
|
||||||
T: ValueType<'a>,
|
|
||||||
{
|
|
||||||
pub fn new<VT: Into<TypedValue<T>>>(v: VT) -> TypedValue<T> {
|
|
||||||
v.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_value(value: Value) -> Option<TypedValue<T>> {
|
|
||||||
if value.0.g_type != T::g_type() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(TypedValue {
|
|
||||||
value: value,
|
|
||||||
phantom: PhantomData,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_typed_value_ref(v: &'a TypedValueRef<'a, T>) -> TypedValue<T> {
|
|
||||||
TypedValue {
|
|
||||||
value: Value::from_value_ref(&v.value),
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&'a self) -> T {
|
|
||||||
self.value.try_get::<T>().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_value(self) -> Value {
|
|
||||||
self.value
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn as_ptr(&self) -> *const gobject_ffi::GValue {
|
|
||||||
&self.value.0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn from_ptr(ptr: *const gobject_ffi::GValue) -> Option<TypedValue<T>> {
|
|
||||||
if let Some(value) = Value::from_ptr(ptr) {
|
|
||||||
return TypedValue::from_value(value);
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn from_raw(value: gobject_ffi::GValue) -> Option<TypedValue<T>> {
|
|
||||||
if let Some(value) = Value::from_raw(value) {
|
|
||||||
return TypedValue::from_value(value);
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn into_raw(mut self) -> gobject_ffi::GValue {
|
|
||||||
mem::replace(&mut self.value.0, mem::zeroed())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> From<T> for TypedValue<T>
|
|
||||||
where
|
|
||||||
T: ValueType<'a> + Into<Value>,
|
|
||||||
{
|
|
||||||
fn from(v: T) -> Self {
|
|
||||||
TypedValue::from_value(Value::new(v)).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<Cow<'a, str>> for TypedValue<&'a str> {
|
|
||||||
fn from(v: Cow<'a, str>) -> Self {
|
|
||||||
TypedValue::from_value(Value::new(v)).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<String> for TypedValue<&'a str> {
|
|
||||||
fn from(v: String) -> Self {
|
|
||||||
TypedValue::from_value(Value::new(v)).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<Vec<Value>> for TypedValue<&'a [Value]> {
|
|
||||||
fn from(v: Vec<Value>) -> Self {
|
|
||||||
TypedValue::from_value(Value::new(v)).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a Vec<Value>> for TypedValue<&'a [Value]> {
|
|
||||||
fn from(v: &'a Vec<Value>) -> Self {
|
|
||||||
TypedValue::from_value(Value::new(v)).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<Cow<'a, [Value]>> for TypedValue<&'a [Value]> {
|
|
||||||
fn from(v: Cow<'a, [Value]>) -> Self {
|
|
||||||
TypedValue::from_value(Value::new(v)).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a GstRc<Buffer>> for TypedValue<GstRc<Buffer>> {
|
|
||||||
fn from(v: &'a GstRc<Buffer>) -> Self {
|
|
||||||
TypedValue::from_value(Value::new(v)).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a Buffer> for TypedValue<GstRc<Buffer>> {
|
|
||||||
fn from(v: &'a Buffer) -> Self {
|
|
||||||
TypedValue::from_value(Value::new(v)).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
||||||
pub struct TypedValueRef<'a, T> {
|
|
||||||
value: ValueRef<'a>,
|
|
||||||
phantom: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> TypedValueRef<'a, T>
|
|
||||||
where
|
|
||||||
T: ValueType<'a>,
|
|
||||||
{
|
|
||||||
pub fn from_typed_value(v: &'a TypedValue<T>) -> TypedValueRef<'a, T> {
|
|
||||||
TypedValueRef {
|
|
||||||
value: ValueRef::from_value(&v.value),
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_value_ref(value: ValueRef<'a>) -> Option<TypedValueRef<'a, T>> {
|
|
||||||
if value.0.g_type != T::g_type() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(TypedValueRef {
|
|
||||||
value: value,
|
|
||||||
phantom: PhantomData,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&'a self) -> T {
|
|
||||||
self.value.try_get::<T>().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_value(self) -> ValueRef<'a> {
|
|
||||||
self.value
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn as_ptr(&self) -> *const gobject_ffi::GValue {
|
|
||||||
self.value.0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn from_ptr(ptr: *const gobject_ffi::GValue) -> Option<TypedValueRef<'a, T>> {
|
|
||||||
if let Some(value) = ValueRef::from_ptr(ptr) {
|
|
||||||
return TypedValueRef::from_value_ref(value);
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use std::ptr;
|
|
||||||
|
|
||||||
macro_rules! gen_test_value(
|
|
||||||
($name: ident, $typ:ty, $value:expr, $variant:ident) => {
|
|
||||||
#[test]
|
|
||||||
fn $name() {
|
|
||||||
unsafe { gst_ffi::gst_init(ptr::null_mut(), ptr::null_mut()) };
|
|
||||||
|
|
||||||
let value = Value::new($value);
|
|
||||||
if let ValueView::$variant(v) = value.get() {
|
|
||||||
assert_eq!(v, $value);
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(v) = value.get().try_get::<$typ>() {
|
|
||||||
assert_eq!(v, $value);
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
let value2 = value.clone();
|
|
||||||
if let ValueView::$variant(v) = value2.get() {
|
|
||||||
assert_eq!(v, $value);
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
let value2 = Value::from_value_view(value.get());
|
|
||||||
assert_eq!(value2, value);
|
|
||||||
|
|
||||||
let value3 = TypedValue::new($value);
|
|
||||||
assert_eq!(value3.get(), $value);
|
|
||||||
|
|
||||||
if let Some(value3) = TypedValue::<$typ>::from_value(value) {
|
|
||||||
assert_eq!(value3.get(), $value);
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
);
|
|
||||||
|
|
||||||
gen_test_value!(int, i32, 12i32, Int);
|
|
||||||
gen_test_value!(uint, u32, 12u32, UInt);
|
|
||||||
gen_test_value!(int64, i64, 12i64, Int64);
|
|
||||||
gen_test_value!(uint64, u64, 12u64, UInt64);
|
|
||||||
gen_test_value!(boolean, bool, true, Bool);
|
|
||||||
gen_test_value!(fraction, Rational32, Rational32::new(1, 2), Fraction);
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn string_owned() {
|
|
||||||
unsafe { gst_ffi::gst_init(ptr::null_mut(), ptr::null_mut()) };
|
|
||||||
|
|
||||||
let orig_v = String::from("foo");
|
|
||||||
|
|
||||||
let value = Value::new(orig_v.clone());
|
|
||||||
if let ValueView::String(v) = value.get() {
|
|
||||||
assert_eq!(v, orig_v);
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(v) = value.get().try_get::<&str>() {
|
|
||||||
assert_eq!(v, orig_v);
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
let value2 = value.clone();
|
|
||||||
if let ValueView::String(v) = value2.get() {
|
|
||||||
assert_eq!(v, orig_v);
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
let value2 = Value::from_value_view(value.get());
|
|
||||||
assert_eq!(value2, value);
|
|
||||||
|
|
||||||
|
|
||||||
let value2 = Value::from_value_view(value.get());
|
|
||||||
assert_eq!(value2, value);
|
|
||||||
|
|
||||||
let value3 = TypedValue::new(orig_v.clone());
|
|
||||||
assert_eq!(value3.get(), orig_v.as_str());
|
|
||||||
|
|
||||||
if let Some(value3) = TypedValue::<&str>::from_value(value) {
|
|
||||||
assert_eq!(value3.get(), orig_v.as_str());
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn string_borrowed() {
|
|
||||||
unsafe { gst_ffi::gst_init(ptr::null_mut(), ptr::null_mut()) };
|
|
||||||
|
|
||||||
let orig_v = "foo";
|
|
||||||
|
|
||||||
let value = Value::new(orig_v);
|
|
||||||
if let ValueView::String(v) = value.get() {
|
|
||||||
assert_eq!(v, orig_v);
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(v) = value.get().try_get::<&str>() {
|
|
||||||
assert_eq!(v, orig_v);
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
let value2 = value.clone();
|
|
||||||
if let ValueView::String(v) = value2.get() {
|
|
||||||
assert_eq!(v, orig_v);
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
let value2 = Value::from_value_view(value.get());
|
|
||||||
assert_eq!(value2, value);
|
|
||||||
|
|
||||||
let value3 = TypedValue::new(orig_v);
|
|
||||||
assert_eq!(value3.get(), orig_v);
|
|
||||||
|
|
||||||
if let Some(value3) = TypedValue::<&str>::from_value(value) {
|
|
||||||
assert_eq!(value3.get(), orig_v);
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn array_owned() {
|
|
||||||
unsafe { gst_ffi::gst_init(ptr::null_mut(), ptr::null_mut()) };
|
|
||||||
|
|
||||||
let orig_v = vec![Value::new("a"), Value::new("b")];
|
|
||||||
|
|
||||||
let value = Value::new(orig_v.clone());
|
|
||||||
if let ValueView::Array(arr) = value.get() {
|
|
||||||
assert_eq!(arr, orig_v.as_slice());
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(v) = value.get().try_get::<&[Value]>() {
|
|
||||||
assert_eq!(v, orig_v.as_slice());
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
let value2 = Value::from_value_view(value.get());
|
|
||||||
assert_eq!(value2, value);
|
|
||||||
|
|
||||||
let value2 = Value::from_value_view(value.get());
|
|
||||||
assert_eq!(value2, value);
|
|
||||||
|
|
||||||
let value3 = TypedValue::new(orig_v.clone());
|
|
||||||
assert_eq!(value3.get(), orig_v.as_slice());
|
|
||||||
|
|
||||||
if let Some(value3) = TypedValue::<&[Value]>::from_value(value) {
|
|
||||||
assert_eq!(value3.get(), orig_v.as_slice());
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn array_borrowed() {
|
|
||||||
unsafe { gst_ffi::gst_init(ptr::null_mut(), ptr::null_mut()) };
|
|
||||||
|
|
||||||
let orig_v = vec![Value::new("a"), Value::new("b")];
|
|
||||||
|
|
||||||
let value = Value::new(&orig_v);
|
|
||||||
if let ValueView::Array(arr) = value.get() {
|
|
||||||
assert_eq!(arr, orig_v.as_slice());
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(arr) = value.get().try_get::<&[Value]>() {
|
|
||||||
assert_eq!(arr, orig_v.as_slice());
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
let value2 = Value::from_value_view(value.get());
|
|
||||||
assert_eq!(value2, value);
|
|
||||||
|
|
||||||
let value3 = TypedValue::new(orig_v.as_slice());
|
|
||||||
assert_eq!(value3.get(), orig_v.as_slice());
|
|
||||||
|
|
||||||
if let Some(value3) = TypedValue::<&[Value]>::from_value(value) {
|
|
||||||
assert_eq!(value3.get(), orig_v.as_slice());
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn buffer() {
|
|
||||||
unsafe { gst_ffi::gst_init(ptr::null_mut(), ptr::null_mut()) };
|
|
||||||
|
|
||||||
let orig_v = Buffer::from_vec(vec![1, 2, 3, 4]).unwrap();
|
|
||||||
|
|
||||||
let value = Value::new(orig_v.clone());
|
|
||||||
if let ValueView::Buffer(buf) = value.get() {
|
|
||||||
assert_eq!(buf, orig_v);
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(buf) = value.get().try_get::<GstRc<Buffer>>() {
|
|
||||||
assert_eq!(buf, orig_v);
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
|
|
||||||
let value2 = Value::from_value_view(value.get());
|
|
||||||
assert_eq!(value2, value);
|
|
||||||
|
|
||||||
let value3 = TypedValue::new(&orig_v);
|
|
||||||
assert_eq!(value3.get(), orig_v);
|
|
||||||
|
|
||||||
if let Some(value3) = TypedValue::<GstRc<Buffer>>::from_value(value) {
|
|
||||||
assert_eq!(value3.get(), orig_v);
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue