Skip to content
Commits on Source (130)
/target/
**/*.rs.bk
Cargo.lock
This diff is collapsed.
......@@ -4,7 +4,9 @@ members = [
"gstreamer",
"gstreamer-app",
"gstreamer-audio",
"gstreamer-base",
"gstreamer-player",
"gstreamer-video",
"examples",
"tutorials",
]
......@@ -14,15 +14,12 @@ external_libraries = [
]
generate = [
"Gst.ClockTime",
"Gst.ClockTimeDiff",
"Gst.Pipeline",
"Gst.State",
"Gst.StateChangeReturn",
"Gst.StateChange",
"Gst.SeekFlags",
"Gst.SeekType",
"Gst.FlowReturn",
"Gst.PadDirection",
"Gst.PadPresence",
"Gst.URIHandler",
......@@ -44,7 +41,6 @@ generate = [
"Gst.DeviceMonitor",
"Gst.StreamType",
"Gst.StreamFlags",
"Gst.PadLinkReturn",
"Gst.ProgressType",
"Gst.BusSyncReply",
"Gst.TagMergeMode",
......@@ -55,7 +51,6 @@ generate = [
"Gst.SegmentFlags",
"Gst.PadMode",
"Gst.SchedulingFlags",
"Gst.IteratorResult",
"Gst.ChildProxy",
"Gst.Preset",
"Gst.TagSetter",
......@@ -66,14 +61,15 @@ generate = [
"Gst.TocLoopType",
"Gst.TocSetter",
"Gst.ClockType",
"Gst.ClockReturn",
"Gst.ElementFlags",
"Gst.Rank",
"Gst.PadLinkCheck",
"Gst.DebugLevel",
"Gst.DebugColorFlags",
"Gst.StackTraceFlags",
"Gst.DebugGraphDetails",
"Gst.ParseFlags",
"Gst.TaskState",
]
manual = [
......@@ -81,10 +77,16 @@ manual = [
"GLib.Source",
"GLib.DateTime",
"GObject.Object",
"Gst.Iterator",
"Gst.Segment",
"Gst.StaticCaps",
"Gst.StaticPadTemplate",
]
[[object]]
name = "Gst.ClockTime"
status = "manual"
conversion_type = "scalar"
[[object]]
name = "Gst.Bin"
status = "generate"
......@@ -299,6 +301,46 @@ status = "generate"
# Unsafe
ignore = true
[[object.function]]
name = "add_property_deep_notify_watch"
# ulong
ignore = true
[[object.function]]
name = "add_property_notify_watch"
# ulong
ignore = true
[[object.function]]
name = "remove_property_notify_watch"
# ulong
ignore = true
[[object.function]]
name = "query_duration"
# formatted value
ignore = true
[[object.function]]
name = "query_position"
# formatted value
ignore = true
[[object.function]]
name = "query_convert"
# formatted value
ignore = true
[[object.function]]
name = "seek"
# formatted value
ignore = true
[[object.function]]
name = "seek_simple"
# formatted value
ignore = true
[[object]]
name = "Gst.ElementFactory"
status = "generate"
......@@ -447,6 +489,41 @@ status = "generate"
# Pass by value
ignore = true
[[object.function]]
name = "new_from_static_template"
# Correct mutability
ignore = true
[[object.function]]
name = "query_duration"
# formatted value
ignore = true
[[object.function]]
name = "query_position"
# formatted value
ignore = true
[[object.function]]
name = "query_convert"
# formatted value
ignore = true
[[object.function]]
name = "peer_query_duration"
# formatted value
ignore = true
[[object.function]]
name = "peer_query_position"
# formatted value
ignore = true
[[object.function]]
name = "peer_query_convert"
# formatted value
ignore = true
[[object]]
name = "Gst.PadTemplate"
status = "generate"
......@@ -683,3 +760,23 @@ status = "generate"
pattern = "parse.*full"
# wrong mutable for context parameter
ignore = true
[[object]]
name = "Gst.StateChangeReturn"
status = "generate"
must_use = true
[[object]]
name = "Gst.FlowReturn"
status = "generate"
must_use = true
[[object]]
name = "Gst.PadLinkReturn"
status = "generate"
must_use = true
[[object]]
name = "Gst.ClockReturn"
status = "generate"
must_use = true
......@@ -12,6 +12,7 @@ external_libraries = [
"GLib",
"GObject",
"Gst",
"GstBase",
]
generate = [
......@@ -22,9 +23,11 @@ manual = [
"GObject.Object",
"Gst.Object",
"Gst.Element",
"Gst.ClockTime",
"Gst.URIHandler",
"Gst.FlowReturn",
"Gst.Format",
"GstBase.BaseSrc",
"GstBase.BaseSink",
]
[[object]]
......@@ -52,6 +55,12 @@ trait = false
# Action signal
ignore = true
[[object.function]]
name = "set_caps"
[[object.function.parameter]]
name = "caps"
nullable = true
[[object]]
name = "GstApp.AppSrc"
status = "generate"
......@@ -77,6 +86,12 @@ trait = false
# Action signal
ignore = true
[[object.function]]
name = "set_caps"
[[object.function.parameter]]
name = "caps"
nullable = true
[[object]]
name = "Gst.Structure"
status = "manual"
......@@ -96,3 +111,8 @@ ref_mode = "ref"
name = "Gst.Sample"
status = "manual"
ref_mode = "ref"
[[object]]
name = "Gst.ClockTime"
status = "manual"
conversion_type = "scalar"
[options]
girs_dir = "gir-files"
library = "GstBase"
version = "1.0"
min_cfg_version = "1.8"
target_path = "gstreamer-base"
work_mode = "normal"
concurrency = "send+sync"
generate_safety_asserts = true
external_libraries = [
"GLib",
"GObject",
"Gst",
]
generate = [
"GstBase.BaseSrc",
"GstBase.BaseSink",
"GstBase.BaseTransform",
"GstBase.PushSrc",
]
manual = [
"GObject.Object",
"GLib.Bytes",
"Gst.Object",
"Gst.Element",
"Gst.ClockTimeDiff",
"Gst.ClockReturn",
"Gst.FlowReturn",
"Gst.Format",
"Gst.Pad",
]
[[object]]
name = "GstBase.Adapter"
status = "generate"
trait = false
concurrency = "none"
[[object.function]]
name = "map"
# Unsafe
ignore = true
[[object.function]]
name = "unmap"
# Unsafe
ignore = true
[[object.function]]
name = "copy"
# Unsafe
ignore = true
[[object.function]]
name = "push"
# Move Buffer
ignore = true
[[object.function]]
name = "take"
# Useless copying of data
ignore = true
[[object]]
name = "GstBase.FlowCombiner"
# Manual because ref/unref functions were added much later
status = "manual"
trait = false
concurrency = "none"
[[object]]
name = "Gst.Structure"
status = "manual"
ref_mode = "ref-mut"
[[object]]
name = "Gst.Caps"
status = "manual"
ref_mode = "ref"
[[object]]
name = "Gst.Buffer"
status = "manual"
ref_mode = "ref"
[[object]]
name = "Gst.BufferList"
status = "manual"
ref_mode = "ref"
[[object]]
name = "Gst.Sample"
status = "manual"
ref_mode = "ref"
[[object]]
name = "Gst.ClockTime"
status = "manual"
conversion_type = "scalar"
......@@ -28,7 +28,6 @@ manual = [
"GLib.Error",
"GLib.MainContext",
"GObject.Object",
"Gst.ClockTime",
"Gst.Element",
]
......@@ -153,3 +152,13 @@ trait = false
name = "GstPlayer.PlayerGMainContextSignalDispatcher"
status = "generate"
trait = false
[[object.function]]
name = "new"
# Wrong return value
ignore = true
[[object]]
name = "Gst.ClockTime"
status = "manual"
conversion_type = "scalar"
# gstreamer-rs [![Build Status](https://travis-ci.org/sdroege/gstreamer-rs.svg?branch=master)](https://travis-ci.org/sdroege/gstreamer-rs)
# gstreamer-rs [![crates.io](https://img.shields.io/crates/v/gstreamer.svg)](https://crates.io/crates/gstreamer) [![Build Status](https://travis-ci.org/sdroege/gstreamer-rs.svg?branch=master)](https://travis-ci.org/sdroege/gstreamer-rs)
[GStreamer](https://gstreamer.freedesktop.org/) bindings for Rust.
Documentation can be found [here](https://sdroege.github.io/rustdoc/gstreamer/gstreamer/).
......@@ -16,6 +16,134 @@ The API of the two is incompatible.
A crate for writing GStreamer plugins in Rust can be found here: https://github.com/sdroege/gst-plugin-rs
## Table of Contents
1. [Installation](#installation)
1. [Linux/BSDs](#installation-linux)
1. [macOS](#installation-macos)
1. [Windows](#installation-windows)
1. [Getting Started](#getting-started)
1. [License](#license)
1. [Contribution](#contribution)
<a name="installation"/>
## Installation
To build the GStreamer bindings or anything depending on them, you need to
have at least GStreamer 1.8 and gst-plugins-base 1.8 installed. In addition,
some of the examples/tutorials require various GStreamer plugins to be
available, which can be found in gst-plugins-base, gst-plugins-good,
gst-plugins-bad, gst-plugins-ugly and/or gst-libav.
<a name="installation-linux"/>
### Linux/BSDs
You need to install the above mentioned packages with your distributions
package manager, or build them from source.
On Debian/Ubuntu they can be installed with
```
$ apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \
gstreamer1.0-plugins-base gstreamer1.0-plugins-good \
gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly \
gstreamer1.0-libav
```
Package names on other distributions should be similar.
Please submit a pull request with instructions for yours.
<a name="installation-macos"/>
### macOS
You can install GStreamer and the plugins via [Homebrew](https://brew.sh/) or
by installing the [binaries](https://gstreamer.freedesktop.org/data/pkg/osx/)
provided by the GStreamer project.
#### Homebrew
```
$ brew install gstreamer gst-plugins-base gst-plugins-good \
gst-plugins-bad gst-plugins-ugly gst-libav
```
#### GStreamer Binaries
You need to download the *two* `.pkg` files from the GStreamer website and
install them, e.g. `gstreamer-1.0-1.12.3-x86_64.pkg` and
`gstreamer-1.0-devel-1.12.3-x86_64.pkg`.
After installation, you also need to install `pkg-config` (e.g. via Homebrew)
and set the `PKG_CONFIG_PATH` environment variable
```
$ export PKG_CONFIG_PATH="/Library/Frameworks/GStreamer.framework/Versions/Current/lib/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}"
```
<a name="installation-windows"/>
### Windows
You can install GStreamer and the plugins via [MSYS2](http://www.msys2.org/)
with `pacman` or by installing the
[binaries](https://gstreamer.freedesktop.org/data/pkg/windows/) provided by
the GStreamer project.
#### MSYS2 / pacman
```
$ pacman -S pkg-config mingw-w64-x86_64-gstreamer mingw-w64-x86_64-gst-plugins-base \
mingw-w64-x86_64-gst-plugins-good mingw-w64-x86_64-gst-plugins-bad \
mingw-w64-x86_64-gst-plugins-ugly mingw-w64-x86_64-gst-libav
```
#### GStreamer Binaries
You need to download the *two* `.msi` files for your platform from the
GStreamer website and install them, e.g. `gstreamer-1.0-x86_64-1.12.3.msi` and
`gstreamer-1.0-devel-x86_64-1.12.3.msi`.
After installation, you also need to install `pkg-config` (e.g. via MSYS2 or
from [here](https://sourceforge.net/projects/pkgconfiglite/))
and set the `PKG_CONFIG_PATH` environment variable
```
$ export PKG_CONFIG_PATH="c:\\gstreamer\\1.0\\x86_64\\lib\\pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}"
```
<a name="getting-started"/>
## Getting Started
The API reference can be found
[here](https://sdroege.github.io/rustdoc/gstreamer/gstreamer/), however it is
only the Rust API reference and does not explain any of the concepts.
For getting started with GStreamer development, the best would be to follow
the [documentation](https://gstreamer.freedesktop.org/documentation/) on the
GStreamer website, especially the [Application Development
Manual](https://gstreamer.freedesktop.org/documentation/application-development/).
While being C-centric, it explains all the fundamental concepts of GStreamer
and the code examples should be relatively easily translatable to Rust. The
API is basically the same, function/struct names are the same and everything
is only more convenient (hopefully) and safer.
In addition there are
[tutorials](https://gstreamer.freedesktop.org/documentation/tutorials/) on the
GStreamer website. Many of them were ported to Rust already and the code can
be found in the
[tutorials](https://github.com/sdroege/gstreamer-rs/tree/master/tutorials)
directory.
Some further examples for various aspects of GStreamer and how to use it from
Rust can be found in the
[examples](https://github.com/sdroege/gstreamer-rs/tree/master/examples)
directory.
<a name="license"/>
## LICENSE
gstreamer-rs and all crates contained in here are licensed under either of
......@@ -31,6 +159,8 @@ GStreamer itself is licensed under the Lesser General Public License version
2.1 or (at your option) any later version:
https://www.gnu.org/licenses/lgpl-2.1.html
<a name="contribution"/>
## Contribution
Any kinds of contributions are welcome as a pull request.
......
......@@ -39,7 +39,7 @@ to avoid polling.
# Implements
[`ElementExt`](trait.ElementExt.html), [`ObjectExt`](trait.ObjectExt.html), [`ObjectExt`](trait.ObjectExt.html)
[`BaseSinkExt`](trait.BaseSinkExt.html), [`ElementExt`](trait.ElementExt.html), [`ObjectExt`](trait.ObjectExt.html), [`ObjectExt`](trait.ObjectExt.html), [`URIHandlerExt`](trait.URIHandlerExt.html)
<!-- impl AppSink::fn get_buffer_list_support -->
Check if `self` supports buffer lists.
......@@ -235,6 +235,125 @@ the maximum amount of time to wait for a sample
a `gst::Sample` or NULL when the appsink is stopped or EOS or the timeout expires.
Call `gst_sample_unref` after usage.
<!-- trait AppSinkExt::fn connect_eos -->
Signal that the end-of-stream has been reached. This signal is emitted from
the streaming thread.
<!-- trait AppSinkExt::fn connect_new_preroll -->
Signal that a new preroll sample is available.
This signal is emitted from the streaming thread and only when the
"emit-signals" property is `true`.
The new preroll sample can be retrieved with the "pull-preroll" action
signal or `AppSink::pull_preroll` either from this signal callback
or from any other thread.
Note that this signal is only emitted when the "emit-signals" property is
set to `true`, which it is not by default for performance reasons.
<!-- trait AppSinkExt::fn connect_new_sample -->
Signal that a new sample is available.
This signal is emitted from the streaming thread and only when the
"emit-signals" property is `true`.
The new sample can be retrieved with the "pull-sample" action
signal or `AppSink::pull_sample` either from this signal callback
or from any other thread.
Note that this signal is only emitted when the "emit-signals" property is
set to `true`, which it is not by default for performance reasons.
<!-- trait AppSinkExt::fn connect_pull_preroll -->
Get the last preroll sample in `appsink`. This was the sample that caused the
appsink to preroll in the PAUSED state. This sample can be pulled many times
and remains available to the application even after EOS.
This function is typically used when dealing with a pipeline in the PAUSED
state. Calling this function after doing a seek will give the sample right
after the seek position.
Note that the preroll sample will also be returned as the first sample
when calling `AppSink::pull_sample` or the "pull-sample" action signal.
If an EOS event was received before any buffers, this function returns
`None`. Use gst_app_sink_is_eos () to check for the EOS condition.
This function blocks until a preroll sample or EOS is received or the appsink
element is set to the READY/NULL state.
# Returns
a `gst::Sample` or NULL when the appsink is stopped or EOS.
<!-- trait AppSinkExt::fn connect_pull_sample -->
This function blocks until a sample or EOS becomes available or the appsink
element is set to the READY/NULL state.
This function will only return samples when the appsink is in the PLAYING
state. All rendered samples will be put in a queue so that the application
can pull samples at its own rate.
Note that when the application does not pull samples fast enough, the
queued samples could consume a lot of memory, especially when dealing with
raw video frames. It's possible to control the behaviour of the queue with
the "drop" and "max-buffers" properties.
If an EOS event was received before any buffers, this function returns
`None`. Use gst_app_sink_is_eos () to check for the EOS condition.
# Returns
a `gst::Sample` or NULL when the appsink is stopped or EOS.
<!-- trait AppSinkExt::fn connect_try_pull_preroll -->
Get the last preroll sample in `appsink`. This was the sample that caused the
appsink to preroll in the PAUSED state. This sample can be pulled many times
and remains available to the application even after EOS.
This function is typically used when dealing with a pipeline in the PAUSED
state. Calling this function after doing a seek will give the sample right
after the seek position.
Note that the preroll sample will also be returned as the first sample
when calling `AppSink::pull_sample` or the "pull-sample" action signal.
If an EOS event was received before any buffers or the timeout expires,
this function returns `None`. Use gst_app_sink_is_eos () to check for the EOS
condition.
This function blocks until a preroll sample or EOS is received, the appsink
element is set to the READY/NULL state, or the timeout expires.
Feature: `v1_10`
## `timeout`
the maximum amount of time to wait for the preroll sample
# Returns
a `gst::Sample` or NULL when the appsink is stopped or EOS or the timeout expires.
<!-- trait AppSinkExt::fn connect_try_pull_sample -->
This function blocks until a sample or EOS becomes available or the appsink
element is set to the READY/NULL state or the timeout expires.
This function will only return samples when the appsink is in the PLAYING
state. All rendered samples will be put in a queue so that the application
can pull samples at its own rate.
Note that when the application does not pull samples fast enough, the
queued samples could consume a lot of memory, especially when dealing with
raw video frames. It's possible to control the behaviour of the queue with
the "drop" and "max-buffers" properties.
If an EOS event was received before any buffers or the timeout expires,
this function returns `None`. Use gst_app_sink_is_eos () to check
for the EOS condition.
Feature: `v1_10`
## `timeout`
the maximum amount of time to wait for a sample
# Returns
a `gst::Sample` or NULL when the appsink is stopped or EOS or the timeout expires.
<!-- struct AppSrc -->
The appsrc element can be used by applications to insert data into a
GStreamer pipeline. Unlike most GStreamer elements, appsrc provides
......@@ -303,7 +422,7 @@ occurs or the state of the appsrc has gone through READY.
# Implements
[`ElementExt`](trait.ElementExt.html), [`ObjectExt`](trait.ObjectExt.html), [`ObjectExt`](trait.ObjectExt.html)
[`BaseSrcExt`](trait.BaseSrcExt.html), [`ElementExt`](trait.ElementExt.html), [`ObjectExt`](trait.ObjectExt.html), [`ObjectExt`](trait.ObjectExt.html), [`URIHandlerExt`](trait.URIHandlerExt.html)
<!-- impl AppSrc::fn end_of_stream -->
Indicates to the appsrc element that the last buffer queued in the
element is the last buffer of the stream.
......@@ -458,6 +577,56 @@ be connected to.
A stream_type stream
## `type_`
the new state
<!-- trait AppSrcExt::fn connect_end_of_stream -->
Notify `appsrc` that no more buffer are available.
<!-- trait AppSrcExt::fn connect_enough_data -->
Signal that the source has enough data. It is recommended that the
application stops calling push-buffer until the need-data signal is
emitted again to avoid excessive buffer queueing.
<!-- trait AppSrcExt::fn connect_need_data -->
Signal that the source needs more data. In the callback or from another
thread you should call push-buffer or end-of-stream.
`length` is just a hint and when it is set to -1, any number of bytes can be
pushed into `appsrc`.
You can call push-buffer multiple times until the enough-data signal is
fired.
## `length`
the amount of bytes needed.
<!-- trait AppSrcExt::fn connect_push_buffer -->
Adds a buffer to the queue of buffers that the appsrc element will
push to its source pad. This function does not take ownership of the
buffer so the buffer needs to be unreffed after calling this function.
When the block property is TRUE, this function can block until free space
becomes available in the queue.
## `buffer`
a buffer to push
<!-- trait AppSrcExt::fn connect_push_sample -->
Extract a buffer from the provided sample and adds the extracted buffer
to the queue of buffers that the appsrc element will
push to its source pad. This function set the appsrc caps based on the caps
in the sample and reset the caps if they change.
Only the caps and the buffer of the provided sample are used and not
for example the segment in the sample.
This function does not take ownership of the
sample so the sample needs to be unreffed after calling this function.
When the block property is TRUE, this function can block until free space
becomes available in the queue.
## `sample`
a sample from which extract buffer to push
<!-- trait AppSrcExt::fn connect_seek_data -->
Seek to the given offset. The next push-buffer should produce buffers from
the new `offset`.
This callback is only called for seekable stream types.
## `offset`
the offset to seek to
# Returns
`true` if the seek succeeded.
<!-- enum AppStreamType -->
The stream type.
<!-- enum AppStreamType::variant Stream -->
......
This diff is collapsed.
This diff is collapsed.
[package]
name = "examples"
version = "0.8.0"
version = "0.9.0"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
[dependencies]
glib = { version = "0.3", git = "https://github.com/gtk-rs/glib" }
gstreamer = { path = "../gstreamer" }
gstreamer-app = { path = "../gstreamer-app" }
gstreamer-audio = { path = "../gstreamer-audio" }
gstreamer-video = { path = "../gstreamer-video" }
gstreamer-player = { path = "../gstreamer-player", optional = true }
gtk = { version = "0.2", git = "https://github.com/gtk-rs/gtk", features = ["v3_6"], optional = true }
gdk = { version = "0.6", git = "https://github.com/gtk-rs/gdk", optional = true }
gio = { version = "0.2", git = "https://github.com/gtk-rs/gio", optional = true }
glib = "0.4"
gstreamer = { version = "0.9", path = "../gstreamer" }
gstreamer-app = { version = "0.9", path = "../gstreamer-app" }
gstreamer-audio = { version = "0.9", path = "../gstreamer-audio" }
gstreamer-video = { version = "0.9", path = "../gstreamer-video" }
gstreamer-player = { version = "0.9", path = "../gstreamer-player", optional = true }
gtk = { version = "0.3", features = ["v3_6"], optional = true }
gdk = { version = "0.7", optional = true }
gio = { version = "0.3", optional = true }
futures = { version = "0.1", optional = true }
tokio-core = { version = "0.1", optional = true }
send-cell = "0.1"
byte-slice-cast = "0.1"
failure = "0.1"
failure_derive = "0.1"
[features]
gst-player = ["gstreamer-player"]
......@@ -26,6 +28,7 @@ gtkvideooverlay-x11 = ["gtkvideooverlay"]
gtkvideooverlay-quartz = ["gtkvideooverlay"]
tokio = ["gstreamer/futures", "futures", "tokio-core"]
default-features = []
v1_10 = ["gstreamer/v1_10"]
[badges]
travis-ci = { repository = "sdroege/gstreamer-rs", branch = "master" }
#[macro_use]
extern crate gstreamer as gst;
use gst::prelude::*;
extern crate gstreamer_app as gst_app;
......@@ -8,23 +9,42 @@ extern crate glib;
extern crate byte_slice_cast;
use byte_slice_cast::*;
use std::u64;
use std::i16;
use std::i32;
use std::error::Error as StdError;
pub mod utils;
extern crate failure;
use failure::Error;
fn create_pipeline() -> Result<gst::Pipeline, utils::ExampleError> {
gst::init().map_err(utils::ExampleError::InitFailed)?;
let pipeline = gst::Pipeline::new(None);
let src = utils::create_element("audiotestsrc")?;
let sink = utils::create_element("appsink")?;
#[macro_use]
extern crate failure_derive;
#[path = "../examples-common.rs"]
mod examples_common;
#[derive(Debug, Fail)]
#[fail(display = "Missing element {}", _0)]
struct MissingElement(&'static str);
pipeline
.add_many(&[&src, &sink])
.expect("Unable to add elements in the pipeline");
#[derive(Debug, Fail)]
#[fail(display = "Received error from {}: {} (debug: {:?})", src, error, debug)]
struct ErrorMessage {
src: String,
error: String,
debug: Option<String>,
#[cause] cause: glib::Error,
}
fn create_pipeline() -> Result<gst::Pipeline, Error> {
gst::init()?;
utils::link_elements(&src, &sink)?;
let pipeline = gst::Pipeline::new(None);
let src =
gst::ElementFactory::make("audiotestsrc", None).ok_or(MissingElement("audiotestsrc"))?;
let sink = gst::ElementFactory::make("appsink", None).ok_or(MissingElement("appsink"))?;
pipeline.add_many(&[&src, &sink])?;
src.link(&sink)?;
let appsink = sink.clone()
.dynamic_cast::<gst_app::AppSink>()
......@@ -52,24 +72,46 @@ fn create_pipeline() -> Result<gst::Pipeline, utils::ExampleError> {
Some(sample) => sample,
};
let buffer = sample
.get_buffer()
.expect("Unable to extract buffer from the sample");
let buffer = if let Some(buffer) = sample.get_buffer() {
buffer
} else {
gst_element_error!(
appsink,
gst::ResourceError::Failed,
("Failed to get buffer from appsink")
);
return gst::FlowReturn::Error;
};
let map = if let Some(map) = buffer.map_readable() {
map
} else {
gst_element_error!(
appsink,
gst::ResourceError::Failed,
("Failed to map buffer readable")
);
let map = buffer
.map_readable()
.expect("Unable to map buffer for reading");
return gst::FlowReturn::Error;
};
let samples = if let Ok(samples) = map.as_slice().as_slice_of::<i16>() {
samples
} else {
gst_element_error!(
appsink,
gst::ResourceError::Failed,
("Failed to interprete buffer as S16 PCM")
);
return gst::FlowReturn::Error;
};
let sum: f64 = samples
.iter()
.map(|sample| {
let f = (*sample as f64) / (i16::MAX as f64);
let f = f64::from(*sample) / f64::from(i16::MAX);
f * f
})
.sum();
......@@ -83,45 +125,47 @@ fn create_pipeline() -> Result<gst::Pipeline, utils::ExampleError> {
Ok(pipeline)
}
fn main_loop() -> Result<(), utils::ExampleError> {
let pipeline = create_pipeline()?;
utils::set_state(&pipeline, gst::State::Playing)?;
fn main_loop(pipeline: gst::Pipeline) -> Result<(), Error> {
pipeline.set_state(gst::State::Playing).into_result()?;
let bus = pipeline
.get_bus()
.expect("Pipeline without bus. Shouldn't happen!");
loop {
while let Some(msg) = bus.timed_pop(gst::CLOCK_TIME_NONE) {
use gst::MessageView;
let msg = match bus.timed_pop(u64::MAX) {
None => break,
Some(msg) => msg,
};
match msg.view() {
MessageView::Eos(..) => break,
MessageView::Error(err) => {
utils::set_state(&pipeline, gst::State::Null)?;
return Err(utils::ExampleError::ElementError(
msg.get_src().get_path_string(),
err.get_error(),
err.get_debug().unwrap(),
));
pipeline.set_state(gst::State::Null).into_result()?;
Err(ErrorMessage {
src: msg.get_src()
.map(|s| s.get_path_string())
.unwrap_or(String::from("None")),
error: err.get_error().description().into(),
debug: err.get_debug(),
cause: err.get_error(),
})?;
}
_ => (),
}
}
utils::set_state(&pipeline, gst::State::Null)?;
pipeline.set_state(gst::State::Null).into_result()?;
Ok(())
}
fn main() {
match main_loop() {
fn example_main() {
match create_pipeline().and_then(main_loop) {
Ok(r) => r,
Err(e) => eprintln!("Error! {}", e),
}
}
fn main() {
// tutorials_common::run is only required to set up the application environent on macOS
// (but not necessary in normal Cocoa applications where this is set up autmatically)
examples_common::run(example_main);
}
......@@ -5,27 +5,47 @@ extern crate gstreamer_video as gst_video;
extern crate glib;
use std::u64;
use std::thread;
pub mod utils;
use std::error::Error as StdError;
extern crate failure;
use failure::Error;
#[macro_use]
extern crate failure_derive;
#[path = "../examples-common.rs"]
mod examples_common;
#[derive(Debug, Fail)]
#[fail(display = "Missing element {}", _0)]
struct MissingElement(&'static str);
#[derive(Debug, Fail)]
#[fail(display = "Received error from {}: {} (debug: {:?})", src, error, debug)]
struct ErrorMessage {
src: String,
error: String,
debug: Option<String>,
#[cause] cause: glib::Error,
}
const WIDTH: usize = 320;
const HEIGHT: usize = 240;
fn create_pipeline() -> Result<(gst::Pipeline, gst_app::AppSrc), utils::ExampleError> {
gst::init().map_err(utils::ExampleError::InitFailed)?;
fn create_pipeline() -> Result<(gst::Pipeline, gst_app::AppSrc), Error> {
gst::init()?;
let pipeline = gst::Pipeline::new(None);
let src = utils::create_element("appsrc")?;
let videoconvert = utils::create_element("videoconvert")?;
let sink = utils::create_element("autovideosink")?;
let src = gst::ElementFactory::make("appsrc", None).ok_or(MissingElement("appsrc"))?;
let videoconvert =
gst::ElementFactory::make("videoconvert", None).ok_or(MissingElement("videoconvert"))?;
let sink =
gst::ElementFactory::make("autovideosink", None).ok_or(MissingElement("autovideosink"))?;
pipeline
.add_many(&[&src, &videoconvert, &sink])
.expect("Unable to add elements in the pipeline");
utils::link_elements(&src, &videoconvert)?;
utils::link_elements(&videoconvert, &sink)?;
pipeline.add_many(&[&src, &videoconvert, &sink])?;
gst::Element::link_many(&[&src, &videoconvert, &sink])?;
let appsrc = src.clone()
.dynamic_cast::<gst_app::AppSrc>()
......@@ -34,7 +54,7 @@ fn create_pipeline() -> Result<(gst::Pipeline, gst_app::AppSrc), utils::ExampleE
let info = gst_video::VideoInfo::new(gst_video::VideoFormat::Bgrx, WIDTH as u32, HEIGHT as u32)
.fps(gst::Fraction::new(2, 1))
.build()
.unwrap();
.expect("Failed to create video info");
appsrc.set_caps(&info.to_caps().unwrap());
appsrc.set_property_format(gst::Format::Time);
......@@ -44,9 +64,7 @@ fn create_pipeline() -> Result<(gst::Pipeline, gst_app::AppSrc), utils::ExampleE
Ok((pipeline, appsrc))
}
fn main_loop() -> Result<(), utils::ExampleError> {
let (pipeline, appsrc) = create_pipeline()?;
fn main_loop(pipeline: gst::Pipeline, appsrc: gst_app::AppSrc) -> Result<(), Error> {
thread::spawn(move || {
for i in 0..100 {
println!("Producing frame {}", i);
......@@ -76,45 +94,49 @@ fn main_loop() -> Result<(), utils::ExampleError> {
}
}
appsrc.end_of_stream();
let _ = appsrc.end_of_stream();
});
utils::set_state(&pipeline, gst::State::Playing)?;
pipeline.set_state(gst::State::Playing).into_result()?;
let bus = pipeline
.get_bus()
.expect("Pipeline without bus. Shouldn't happen!");
loop {
while let Some(msg) = bus.timed_pop(gst::CLOCK_TIME_NONE) {
use gst::MessageView;
let msg = match bus.timed_pop(u64::MAX) {
None => break,
Some(msg) => msg,
};
match msg.view() {
MessageView::Eos(..) => break,
MessageView::Error(err) => {
utils::set_state(&pipeline, gst::State::Null)?;
return Err(utils::ExampleError::ElementError(
msg.get_src().get_path_string(),
err.get_error(),
err.get_debug().unwrap(),
));
pipeline.set_state(gst::State::Null).into_result()?;
Err(ErrorMessage {
src: msg.get_src()
.map(|s| s.get_path_string())
.unwrap_or(String::from("None")),
error: err.get_error().description().into(),
debug: err.get_debug(),
cause: err.get_error(),
})?;
}
_ => (),
}
}
utils::set_state(&pipeline, gst::State::Null)?;
pipeline.set_state(gst::State::Null).into_result()?;
Ok(())
}
fn main() {
match main_loop() {
fn example_main() {
match create_pipeline().and_then(|(pipeline, appsrc)| main_loop(pipeline, appsrc)) {
Ok(r) => r,
Err(e) => eprintln!("Error! {}", e),
}
}
fn main() {
// tutorials_common::run is only required to set up the application environent on macOS
// (but not necessary in normal Cocoa applications where this is set up autmatically)
examples_common::run(example_main);
}
#[macro_use]
extern crate gstreamer as gst;
use gst::prelude::*;
extern crate glib;
use std::env;
use std::u64;
use std::error::Error as StdError;
#[cfg(feature = "v1_10")]
use std::sync::{Arc, Mutex};
extern crate failure;
use failure::Error;
#[macro_use]
extern crate failure_derive;
#[path = "../examples-common.rs"]
mod examples_common;
#[derive(Debug, Fail)]
#[fail(display = "Missing element {}", _0)]
struct MissingElement(&'static str);
#[derive(Debug, Fail)]
#[fail(display = "Received error from {}: {} (debug: {:?})", src, error, debug)]
struct ErrorMessage {
src: String,
error: String,
debug: Option<String>,
#[cause] cause: glib::Error,
}
fn main() {
gst::init().unwrap();
fn example_main() -> Result<(), Error> {
gst::init()?;
let args: Vec<_> = env::args().collect();
let uri: &str = if args.len() == 2 {
args[1].as_ref()
} else {
panic!("Usage: decodebin file_path");
println!("Usage: decodebin file_path");
std::process::exit(-1);
};
let pipeline = gst::Pipeline::new(None);
let src = gst::ElementFactory::make("filesrc", None).unwrap();
let decodebin = gst::ElementFactory::make("decodebin", None).unwrap();
let src = gst::ElementFactory::make("filesrc", None).ok_or(MissingElement("filesrc"))?;
let decodebin =
gst::ElementFactory::make("decodebin", None).ok_or(MissingElement("decodebin"))?;
src.set_property("location", &glib::Value::from(uri))
.unwrap();
src.set_property("location", &uri)?;
pipeline.add_many(&[&src, &decodebin]).unwrap();
gst::Element::link_many(&[&src, &decodebin]).unwrap();
pipeline.add_many(&[&src, &decodebin])?;
gst::Element::link_many(&[&src, &decodebin])?;
// Need to move a new reference into the closure
let pipeline_clone = pipeline.clone();
decodebin.connect_pad_added(move |_, src_pad| {
decodebin.connect_pad_added(move |dbin, src_pad| {
let pipeline = &pipeline_clone;
let (is_audio, is_video) = {
let caps = src_pad.get_current_caps().unwrap();
let structure = caps.get_structure(0).unwrap();
let name = structure.get_name();
(name.starts_with("audio/"), name.starts_with("video/"))
let media_type = src_pad.get_current_caps().and_then(|caps| {
caps.get_structure(0).map(|s| {
let name = s.get_name();
(name.starts_with("audio/"), name.starts_with("video/"))
})
});
match media_type {
None => {
gst_element_warning!(
dbin,
gst::CoreError::Negotiation,
("Failed to get media type from pad {}", src_pad.get_name())
);
return;
}
Some(media_type) => media_type,
}
};
if is_audio {
let queue = gst::ElementFactory::make("queue", None).unwrap();
let convert = gst::ElementFactory::make("audioconvert", None).unwrap();
let resample = gst::ElementFactory::make("audioresample", None).unwrap();
let sink = gst::ElementFactory::make("autoaudiosink", None).unwrap();
let elements = &[&queue, &convert, &resample, &sink];
pipeline.add_many(elements).unwrap();
gst::Element::link_many(elements).unwrap();
for e in elements {
e.sync_state_with_parent().unwrap();
let insert_sink = |is_audio, is_video| -> Result<(), Error> {
if is_audio {
let queue =
gst::ElementFactory::make("queue", None).ok_or(MissingElement("queue"))?;
let convert = gst::ElementFactory::make("audioconvert", None)
.ok_or(MissingElement("audioconvert"))?;
let resample = gst::ElementFactory::make("audioresample", None)
.ok_or(MissingElement("audioresample"))?;
let sink = gst::ElementFactory::make("autoaudiosink", None)
.ok_or(MissingElement("autoaudiosink"))?;
let elements = &[&queue, &convert, &resample, &sink];
pipeline.add_many(elements)?;
gst::Element::link_many(elements)?;
for e in elements {
e.sync_state_with_parent()?;
}
let sink_pad = queue.get_static_pad("sink").expect("queue has no sinkpad");
src_pad.link(&sink_pad).into_result()?;
} else if is_video {
let queue =
gst::ElementFactory::make("queue", None).ok_or(MissingElement("queue"))?;
let convert = gst::ElementFactory::make("videoconvert", None)
.ok_or(MissingElement("videoconvert"))?;
let scale = gst::ElementFactory::make("videoscale", None)
.ok_or(MissingElement("videoscale"))?;
let sink = gst::ElementFactory::make("autovideosink", None)
.ok_or(MissingElement("autovideosink"))?;
let elements = &[&queue, &convert, &scale, &sink];
pipeline.add_many(elements)?;
gst::Element::link_many(elements)?;
for e in elements {
e.sync_state_with_parent()?
}
let sink_pad = queue.get_static_pad("sink").expect("queue has no sinkpad");
src_pad.link(&sink_pad).into_result()?;
}
let sink_pad = queue.get_static_pad("sink").unwrap();
assert_eq!(src_pad.link(&sink_pad), gst::PadLinkReturn::Ok);
} else if is_video {
let queue = gst::ElementFactory::make("queue", None).unwrap();
let convert = gst::ElementFactory::make("videoconvert", None).unwrap();
let scale = gst::ElementFactory::make("videoscale", None).unwrap();
let sink = gst::ElementFactory::make("autovideosink", None).unwrap();
let elements = &[&queue, &convert, &scale, &sink];
pipeline.add_many(elements).unwrap();
gst::Element::link_many(elements).unwrap();
for e in elements {
e.sync_state_with_parent().unwrap();
}
Ok(())
};
let sink_pad = queue.get_static_pad("sink").unwrap();
assert_eq!(src_pad.link(&sink_pad), gst::PadLinkReturn::Ok);
if let Err(err) = insert_sink(is_audio, is_video) {
#[cfg(feature = "v1_10")]
gst_element_error!(
dbin,
gst::LibraryError::Failed,
("Failed to insert sink"),
details: gst::Structure::builder("error-details")
.field("error",
glib::AnySendValue::new(Arc::new(Mutex::new(Some(err)))))
.build()
);
#[cfg(not(feature = "v1_10"))]
gst_element_error!(
dbin,
gst::LibraryError::Failed,
("Failed to insert sink"),
["{}", err]
);
}
});
assert_ne!(
pipeline.set_state(gst::State::Playing),
gst::StateChangeReturn::Failure
);
pipeline.set_state(gst::State::Playing).into_result()?;
let bus = pipeline.get_bus().unwrap();
let bus = pipeline
.get_bus()
.expect("Pipeline without bus. Shouldn't happen!");
loop {
while let Some(msg) = bus.timed_pop(gst::CLOCK_TIME_NONE) {
use gst::MessageView;
let msg = match bus.timed_pop(u64::MAX) {
None => break,
Some(msg) => msg,
};
match msg.view() {
MessageView::Eos(..) => break,
MessageView::Error(err) => {
println!(
"Error from {}: {} ({:?})",
msg.get_src().get_path_string(),
err.get_error(),
err.get_debug()
);
pipeline.set_state(gst::State::Null).into_result()?;
#[cfg(feature = "v1_10")]
{
match err.get_details() {
Some(details) if details.get_name() == "error-details" => details
.get::<&glib::AnySendValue>("error")
.cloned()
.and_then(|v| {
v.downcast_ref::<Arc<Mutex<Option<Error>>>>()
.and_then(|v| v.lock().unwrap().take())
})
.map(Result::Err)
.expect("error-details message without actual error"),
_ => Err(
ErrorMessage {
src: msg.get_src()
.map(|s| s.get_path_string())
.unwrap_or(String::from("None")),
error: err.get_error().description().into(),
debug: err.get_debug(),
cause: err.get_error(),
}.into(),
),
}?;
}
#[cfg(not(feature = "v1_10"))]
{
Err(ErrorMessage {
src: msg.get_src()
.map(|s| s.get_path_string())
.unwrap_or(String::from("None")),
error: err.get_error().description().into(),
debug: err.get_debug(),
cause: err.get_error(),
})?;
}
break;
}
MessageView::StateChanged(s) => {
println!(
"State changed from {}: {:?} -> {:?} ({:?})",
msg.get_src().get_path_string(),
"State changed from {:?}: {:?} -> {:?} ({:?})",
msg.get_src().map(|s| s.get_path_string()),
s.get_old(),
s.get_current(),
s.get_pending()
......@@ -113,8 +211,16 @@ fn main() {
}
}
assert_ne!(
pipeline.set_state(gst::State::Null),
gst::StateChangeReturn::Failure
);
pipeline.set_state(gst::State::Null).into_result()?;
Ok(())
}
fn main() {
// tutorials_common::run is only required to set up the application environent on macOS
// (but not necessary in normal Cocoa applications where this is set up autmatically)
match examples_common::run(example_main) {
Ok(r) => r,
Err(e) => eprintln!("Error! {}", e),
}
}
......@@ -3,7 +3,10 @@ use gst::prelude::*;
extern crate glib;
fn main() {
#[path = "../examples-common.rs"]
mod examples_common;
fn example_main() {
gst::init().unwrap();
let main_loop = glib::MainLoop::new(None, false);
......@@ -41,8 +44,8 @@ fn main() {
}
MessageView::Error(err) => {
println!(
"Error from {}: {} ({:?})",
msg.get_src().get_path_string(),
"Error from {:?}: {} ({:?})",
msg.get_src().map(|s| s.get_path_string()),
err.get_error(),
err.get_debug()
);
......@@ -59,3 +62,9 @@ fn main() {
let ret = pipeline.set_state(gst::State::Null);
assert_ne!(ret, gst::StateChangeReturn::Failure);
}
fn main() {
// tutorials_common::run is only required to set up the application environent on macOS
// (but not necessary in normal Cocoa applications where this is set up autmatically)
examples_common::run(example_main);
}
......@@ -59,25 +59,14 @@ fn create_ui(app: &gtk::Application) {
let pipeline_clone = pipeline.clone();
gtk::timeout_add(500, move || {
let pipeline = &pipeline_clone;
let position = pipeline.query_position(gst::Format::Time);
if let Some(position) = position {
let mut seconds = (position as gst::ClockTime) / gst::SECOND;
let mut minutes = seconds / 60;
let hours = minutes / 60;
seconds %= 60;
minutes %= 60;
label.set_text(&format!(
"Position: {:02}:{:02}:{:02}",
hours,
minutes,
seconds
));
let position = if let Some(gst::FormatValue::Time(position)) =
pipeline.query_position(gst::Format::Time)
{
position
} else {
label.set_text("Position: 00:00:00");
}
0.into()
};
label.set_text(&format!("Position: {:.0}", position));
glib::Continue(true)
});
......@@ -103,8 +92,8 @@ fn create_ui(app: &gtk::Application) {
MessageView::Eos(..) => gtk::main_quit(),
MessageView::Error(err) => {
println!(
"Error from {}: {} ({:?})",
msg.get_src().get_path_string(),
"Error from {:?}: {} ({:?})",
msg.get_src().map(|s| s.get_path_string()),
err.get_error(),
err.get_debug()
);
......@@ -129,12 +118,11 @@ fn main() {
gst::init().unwrap();
gtk::init().unwrap();
let app = gtk::Application::new(None, gio::APPLICATION_FLAGS_NONE).unwrap();
let app = gtk::Application::new(None, gio::ApplicationFlags::FLAGS_NONE).unwrap();
app.connect_activate(create_ui);
let args = env::args().collect::<Vec<_>>();
let args_ref = args.iter().map(|a| a.as_str()).collect::<Vec<_>>();
app.run(&args_ref);
app.run(&args);
}
#[cfg(not(feature = "gtksink"))]
......
......@@ -114,7 +114,6 @@ fn create_ui(app: &gtk::Application) {
process::exit(-1);
}
}
});
vbox.pack_start(&video_window, true, true, 0);
......@@ -130,25 +129,14 @@ fn create_ui(app: &gtk::Application) {
let pipeline_clone = pipeline.clone();
gtk::timeout_add(500, move || {
let pipeline = &pipeline_clone;
let position = pipeline.query_position(gst::Format::Time);
if let Some(position) = position {
let mut seconds = (position as gst::ClockTime) / gst::SECOND;
let mut minutes = seconds / 60;
let hours = minutes / 60;
seconds %= 60;
minutes %= 60;
label.set_text(&format!(
"Position: {:02}:{:02}:{:02}",
hours,
minutes,
seconds
));
let position = if let Some(gst::FormatValue::Time(position)) =
pipeline.query_position(gst::Format::Time)
{
position
} else {
label.set_text("Position: 00:00:00");
}
0.into()
};
label.set_text(&format!("Position: {:.0}", position));
glib::Continue(true)
});
......@@ -174,8 +162,8 @@ fn create_ui(app: &gtk::Application) {
MessageView::Eos(..) => gtk::main_quit(),
MessageView::Error(err) => {
println!(
"Error from {}: {} ({:?})",
msg.get_src().get_path_string(),
"Error from {:?}: {} ({:?})",
msg.get_src().map(|s| s.get_path_string()),
err.get_error(),
err.get_debug()
);
......@@ -206,12 +194,11 @@ fn main() {
gst::init().unwrap();
gtk::init().unwrap();
let app = gtk::Application::new(None, gio::APPLICATION_FLAGS_NONE).unwrap();
let app = gtk::Application::new(None, gio::ApplicationFlags::FLAGS_NONE).unwrap();
app.connect_activate(create_ui);
let args = env::args().collect::<Vec<_>>();
let args_ref = args.iter().map(|a| a.as_str()).collect::<Vec<_>>();
app.run(&args_ref);
app.run(&args);
}
#[cfg(not(feature = "gtkvideooverlay"))]
......
extern crate gstreamer as gst;
use gst::prelude::*;
#[path = "../examples-common.rs"]
mod examples_common;
fn example_main() {
gst::init().unwrap();
let identity = gst::ElementFactory::make("identity", None).unwrap();
let mut iter = identity.iterate_pads();
while let Some(res) = iter.next() {
match res {
Ok(pad) => println!("Pad: {}", pad.get_name()),
Err(gst::IteratorError::Resync) => {
println!("Iterator resync");
iter.resync();
}
Err(gst::IteratorError::Error) => {
println!("Error");
break;
}
}
}
}
fn main() {
// tutorials_common::run is only required to set up the application environent on macOS
// (but not necessary in normal Cocoa applications where this is set up autmatically)
examples_common::run(example_main);
}
extern crate gstreamer as gst;
use gst::prelude::*;
use std::u64;
use std::env;
use std::process;
fn main() {
#[path = "../examples-common.rs"]
mod examples_common;
fn example_main() {
let pipeline_str = env::args().collect::<Vec<String>>()[1..].join(" ");
gst::init().unwrap();
let mut context = gst::ParseContext::new();
let pipeline =
match gst::parse_launch_full(&pipeline_str, Some(&mut context), gst::PARSE_FLAG_NONE) {
match gst::parse_launch_full(&pipeline_str, Some(&mut context), gst::ParseFlags::NONE) {
Ok(pipeline) => pipeline,
Err(err) => {
if let Some(gst::ParseError::NoSuchElement) = err.kind::<gst::ParseError>() {
......@@ -21,7 +23,7 @@ fn main() {
println!("Failed to parse pipeline: {}", err);
}
process::exit(-1);
process::exit(-1)
}
};
let bus = pipeline.get_bus().unwrap();
......@@ -29,20 +31,15 @@ fn main() {
let ret = pipeline.set_state(gst::State::Playing);
assert_ne!(ret, gst::StateChangeReturn::Failure);
loop {
while let Some(msg) = bus.timed_pop(gst::CLOCK_TIME_NONE) {
use gst::MessageView;
let msg = match bus.timed_pop(u64::MAX) {
None => break,
Some(msg) => msg,
};
match msg.view() {
MessageView::Eos(..) => break,
MessageView::Error(err) => {
println!(
"Error from {}: {} ({:?})",
msg.get_src().get_path_string(),
"Error from {:?}: {} ({:?})",
msg.get_src().map(|s| s.get_path_string()),
err.get_error(),
err.get_debug()
);
......@@ -55,3 +52,9 @@ fn main() {
let ret = pipeline.set_state(gst::State::Null);
assert_ne!(ret, gst::StateChangeReturn::Failure);
}
fn main() {
// tutorials_common::run is only required to set up the application environent on macOS
// (but not necessary in normal Cocoa applications where this is set up autmatically)
examples_common::run(example_main);
}