Compare commits

...

52 commits
main ... 0.15.4

Author SHA1 Message Date
Sebastian Dröge
ef77a5ae92 Update versions to 0.15.4 2020-03-09 23:01:42 +02:00
Sebastian Dröge
079095b55c Update CHANGELOG.md for 0.15.4 2020-03-09 23:01:42 +02:00
Sebastian Dröge
2599acc681 gstreamer/caps: Assert on ANY caps in fixate() and work around bug in handling EMPTY caps
See https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/388
2020-03-09 23:01:42 +02:00
Sebastian Dröge
671605d8ac Minor cleanup in various places 2020-03-09 22:49:51 +02:00
Sebastian Dröge
01da01c9e6 app: Handle panicking callbacks by converting into an error message
And never calling the callbacks again but instead just failing.

Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/issues/241
2020-03-09 22:49:51 +02:00
Sebastian Dröge
4e30798ac7 Add #[must_use] attribute to mutex guards / stream lock
It's usually a mistake if creating one of these and immediately dropping
them again as that would immediately unlock the mutex again.
2020-03-09 22:49:51 +02:00
Sebastian Dröge
ed80467ff6 Make sure to hold MutexGuard for the remainder of the function in various places
Not assigning it to a variable would cause it to be dropped immediately
and the lock to be released again immediately.
2020-03-09 22:49:50 +02:00
Sebastian Dröge
19295f75b5 rtsp_server: Add example making use of subclassing RTSPMediaFactory and RTSPMedia 2020-03-09 22:49:50 +02:00
Sebastian Dröge
e02ec7a8c9 rtsp_server: Add support for subclassing RTSPServer and RTSPClient 2020-03-09 22:49:50 +02:00
Sebastian Dröge
21ffeb5e82 rtsp_server: Add subclassing support for MediaFactory and Media 2020-03-09 22:49:50 +02:00
Sebastian Dröge
aa86c804bd rtsp_server/media: Implement take_pipeline() manually
Because of floating reference problems in the API that have to be worked
around.
2020-03-09 22:42:49 +02:00
Sebastian Dröge
38071f1897 rtsp-server: Add bindings for RTSPThread 2020-03-09 22:42:42 +02:00
Sebastian Dröge
d429fad50d gstreamer/base_transform: Add support for implementing prepare_output_buffer()
This requires some acrobatics due to inconsistent ownership handling of
the buffers on the C side.
2020-03-09 22:42:35 +02:00
Sebastian Dröge
67e6afc628 gstreamer/log: Allow any glib::Object as target for logging
gst::Object is not actually required. For plain glib::Objects only the
pointer address is printed instead of a name but it works fine.
2020-03-09 22:42:26 +02:00
Sebastian Dröge
0e69898faf Update version to 0.15.3 2020-02-15 10:46:41 +01:00
Sebastian Dröge
45b7676d02 Update CHANGELOG.md for 0.15.3 2020-02-15 10:46:14 +01:00
Sebastian Dröge
315a59e47d Allow changing bus sync handler and appsink/src callbacks when running with 1.16.3 or newer
Previously it was not thread-safe to change them and could lead to
crashes but with 1.16.3 it is now.

Unsetting the bus sync handler before 1.16.3 will have no effect at all,
setting a new bus sync handler or appsink/src callbacks will panic.

This partially reverts 2f88dc6576
2020-02-15 10:35:35 +01:00
Sebastian Dröge
3b56f470e5 Only allow setting Bus sync handler and AppSrc/Sink callbacks once
Re-setting them is not thread-safe and can cause segfaults or worse.

See https://gitlab.freedesktop.org/gstreamer/gstreamer/issues/506
and https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/issues/729
2020-02-15 10:33:53 +01:00
Valmir Pretto
0d34e70e3e gstreamer-app: AppSink futures adapter
An adapter for AppSink that adds futures capabilities to it in the form of a Stream.
2020-02-15 10:29:56 +01:00
Valmir Pretto
7a05ff52af gstreamer-app: AppSrc futures adapter
An adapter for AppSrc that adds futures capabilities to it in the form of a Sink.
2020-02-15 10:29:49 +01:00
Valmir Pretto
66338881e8 gstreamer: BusStream tests 2020-02-15 10:29:43 +01:00
Valmir Pretto
5beb871419 gstreamer: BusStream cleanup
A few small readability changes
2020-02-15 10:29:38 +01:00
Sebastian Dröge
462f4ac3b8 video/video-info: Don't use bool return of gst_video_info_set_format()/align() when running with GStreamer < 1.11.1
The bool return value was added in 1.11.1 and using the return value
with older versions gives a random value that might be true or false,
and then causes spurious errors.

Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/issues/236#note_399872
2020-02-15 10:15:03 +01:00
Sebastian Dröge
a61c50c21a base/base_transform: Fix minor clippy warning
warning: called `map(f)` on an Option value where `f` is a unit closure
   --> gstreamer-base/src/subclass/base_transform.rs:683:13
    |
683 | /             (*parent_class)
684 | |                 .before_transform
685 | |                 .map(|f| f(element.to_glib_none().0, inbuf.as_ptr() as *mut _));
    | |_______________________________________________________________________________^
    |
    = note: `#[warn(clippy::option_map_unit_fn)]` on by default
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn
2020-02-15 10:14:54 +01:00
Sebastian Dröge
e6d3ac4e4b base/base_transform: Add before_transform/copy_metadata/transform_meta support for subclasses 2020-02-15 10:14:46 +01:00
Sebastian Dröge
89b9dc80dc base/base_transform: Add submit_input_buffer/generate_output support for subclasses
Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/issues/155
2020-02-15 10:14:41 +01:00
Sebastian Dröge
9b593f626b gstreamer/promise: Add Future constructor for Promise
This returns a tuple that basically works like a oneshot channel: the
Promise acts as the "sender" and once the promise resolves the
"receiver" contains the result.
2020-02-15 10:14:35 +01:00
Sebastian Dröge
605d4fbf24 video: Add Future variant of convert_sample_async()
Requires to be spawned on the GLib main context futures executor as the
function itself requires a GLib main context to call the callback.
2020-02-15 10:14:22 +01:00
Sebastian Dröge
7fa810873f audio/stream_align: Don't require mutable references in getters 2020-02-15 10:13:48 +01:00
Sebastian Dröge
0ffd86c37e base/flow_combiner: UniqueFlowCombiner::clear() should take &mut self
It's modifying the state of the combiner and requires a unique, mutable
reference.
2020-02-15 10:13:40 +01:00
Sebastian Dröge
ea38e022be video: Add VideoConverter bindings 2020-02-15 10:11:41 +01:00
Sebastian Dröge
23fdd2c5ad video: Autogenerate bindings for a few more enums 2020-02-15 10:11:35 +01:00
Sebastian Dröge
3ae8b48400 video/video-frame: Implement immutable frame functions more generically 2020-02-15 10:11:25 +01:00
Sebastian Dröge
188ef1e242 impl Clone for BufferPoolConfig and PlayerConfig 2020-02-15 10:11:19 +01:00
Sebastian Dröge
a47a6ea76c Update versions to 0.15.2 2020-01-30 00:20:38 +02:00
Sebastian Dröge
e5397f5a33 Update CHANGELOG.md for 0.15.2 2020-01-30 00:20:22 +02:00
Sebastian Dröge
41663f15ef bus: Don't use the bus sync handler in the bus Stream to notify about messages being available
This is racy and can cause the consumer of the messages to never be
woken up anymore:

1. Waker is stored because no message on the bus
2. Sync handler is called, waker is woken up
3. Bus is polled again and no message is on it (yet),
   new waker is registered
4. Bus stores the message from 2. in its queue (after
   the sync handler has returned BusSyncReply::Pass)
5. No new message ever appears on the bus because all
   this happened for the very last message

Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/issues/235
2020-01-28 23:43:41 +02:00
Sebastian Dröge
8a9be64c3f Update version to 0.15.1 2020-01-23 08:06:17 +02:00
Sebastian Dröge
3a8ae5ddd4 Update CHANGELOG.md for 0.15.1 2020-01-23 08:06:00 +02:00
Sebastian Dröge
fc1e909925 gstreamer/childproxy: Fix unused variable compiler warnings 2020-01-22 22:31:10 +02:00
Sebastian Dröge
5577e8a457 Don't derive Debug impls for generic types where the type parameters don't have to impl Debug themselves
This allows to use MappedBuffer and similar types to be properly
debug-printed.

Also change VideoFrame/VideoFrameRef/RTPBuffer from a tuple struct to a
struct with proper field names to make the code easier to understand.
2020-01-22 22:31:10 +02:00
Sebastian Dröge
456ad9fb4a gstreamer/childproxy: Don't require implementing child_added/removed() signal vfuncs
Most implementers will not care about default handlers for these signals
so requiring to implement them is only unnecessarily verbose.
2020-01-22 22:31:10 +02:00
Sebastian Dröge
b127f93cb9 bus: Take the mutex before popping messages for the bus stream
Otherwise a message might arrive between popping, getting None and
locking the mutex for storing the waker. In that case we would never
be woken up.
2020-01-22 22:31:10 +02:00
Sebastian Dröge
db1c341cdf gstreamer/element: Add call_async_future() that returns a future
The future would resolve into the return value of the closure that is
called asynchronously on the thread pool, and allows asynchronous
awaiting for it to finish.

  let res = element.call_async(|element| {
      element.set_state(gst::State::Playing)
  }).await;

  assert_eq!(res, Ok(gst::StateChangeSuccess::Success))
2020-01-22 22:31:10 +02:00
Guillaume Desmottes
c29a7638d3 gstreamer: caps: BuilderFull: prevent adding features if using any
Rework the API to statically prevent users adding extra features if the
builder has been created with builder_full_with_any_features(). It
doesn't make sense to add extra features if all are already included.
2020-01-22 19:44:43 +02:00
Guillaume Desmottes
7f07bac0c7 gstreamer: add Caps::builder_full()
API to create a caps containing multiple structures.
Fix #231
2020-01-22 19:44:15 +02:00
Sebastian Dröge
3425bcfe9d gstreamer: Add getters/setters for BinFlags and fix flags API for PadFlags
The BinFlags API was accidentally used for PadFlags, allowing to set
BinFlags on pads which is not very useful.
2020-01-22 19:44:09 +02:00
Sebastian Dröge
28cc573875 Run clippy on --all-targets
Also ignore clippy::missing_safety_doc for now.
2019-12-23 13:40:58 +02:00
Sebastian Dröge
fedf4b664a Fix various new clippy warnings from 1.40 2019-12-23 13:40:58 +02:00
Sebastian Dröge
d2e508eca1 video: Use static inner lifetime for VideoCodecState<Readable>
It does not borrow from anything else and can be safely stored away.

Only the VideoCodecState<InNegotiation> is actively borrowing from the
element to ensure that it is only modified during negotiation in a safe
way.
2019-12-23 13:15:34 +02:00
Sebastian Dröge
d62d788630 gstreamer-rtp: Add README.md 2019-12-19 00:37:51 +02:00
Sebastian Dröge
6fd13cc807 Update versions of all dependencies and point to releases instead of GIT 2019-12-18 19:15:21 +02:00
120 changed files with 7440 additions and 645 deletions

View file

@ -137,12 +137,12 @@ clippy:
FEATURES=v1_16
fi
cargo clippy --color=always --manifest-path $crate/Cargo.toml --features=$FEATURES -- -A clippy::redundant_pattern_matching -A clippy::single_match -A clippy::cast_lossless
cargo clippy --color=always --manifest-path $crate/Cargo.toml --features=$FEATURES --all-targets -- -A clippy::redundant_pattern_matching -A clippy::single_match -A clippy::cast_lossless -A clippy::missing_safety_doc
done
# And also run over all the examples/tutorials
- |
cargo clippy --color=always --manifest-path examples/Cargo.toml --bins --examples --all-features -- -A clippy::redundant_pattern_matching -A clippy::single_match -A clippy::cast_lossless
cargo clippy --color=always --manifest-path tutorials/Cargo.toml --bins --examples --all-features -- -A clippy::redundant_pattern_matching -A clippy::single_match -A clippy::cast_lossless
cargo clippy --color=always --manifest-path examples/Cargo.toml --all-targets --all-features -- -A clippy::redundant_pattern_matching -A clippy::single_match -A clippy::cast_lossless -A clippy::missing_safety_doc
cargo clippy --color=always --manifest-path tutorials/Cargo.toml --all-targets --all-features -- -A clippy::redundant_pattern_matching -A clippy::single_match -A clippy::cast_lossless -A clippy::missing_safety_doc
audit:
extends: '.tarball_setup'

View file

@ -144,6 +144,12 @@ status = "generate"
# bool does not signal error
ignore = true
[[object.function]]
pattern = "get_.*"
[[object.function.parameter]]
name = "align"
const = true
[[object]]
name = "GstAudio.AudioDecoder"
status = "generate"

View file

@ -96,6 +96,11 @@ name = "GstRtspServer.RTSPToken"
status = "manual"
ref_mode = "ref"
[[object]]
name = "GstRtspServer.RTSPThread"
status = "manual"
ref_mode = "ref"
[[object]]
name = "Gst.ClockTime"
status = "manual"
@ -246,11 +251,20 @@ status = "generate"
[object.function.return]
bool_return_is_error = "Failed to unprepare media"
[[object.function]]
name = "prepare"
[object.function.return]
bool_return_is_error = "Failed to prepare media"
[[object.function]]
name = "unsuspend"
[object.function.return]
bool_return_is_error = "Failed to unsuspend media"
[[object.function]]
name = "take_pipeline"
ignore = true
[[object]]
name = "GstRtspServer.RTSPMediaFactory"
status = "generate"

View file

@ -40,6 +40,13 @@ generate = [
"GstVideo.VideoBufferPool",
"GstVideo.VideoPackFlags",
"GstVideo.VideoBufferFlags",
"GstVideo.VideoAlphaMode",
"GstVideo.VideoChromaMode",
"GstVideo.VideoMatrixMode",
"GstVideo.VideoGammaMode",
"GstVideo.VideoPrimariesMode",
"GstVideo.VideoResamplerMethod",
"GstVideo.VideoDitherMethod",
]
manual = [

View file

@ -5,29 +5,30 @@ license = "MIT"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
[dependencies]
glib = { git = "https://github.com/gtk-rs/glib" }
gstreamer = { path = "../gstreamer" }
gstreamer-gl = { path = "../gstreamer-gl", optional = true }
gstreamer-app = { path = "../gstreamer-app" }
gstreamer-audio = { path = "../gstreamer-audio" }
gstreamer-base = { path = "../gstreamer-base" }
gstreamer-video = { path = "../gstreamer-video" }
gstreamer-pbutils = { path = "../gstreamer-pbutils" }
gstreamer-player = { path = "../gstreamer-player", optional = true }
gstreamer-editing-services = { path = "../gstreamer-editing-services", optional = true }
gstreamer-rtsp = { path = "../gstreamer-rtsp", optional = true }
gstreamer-rtsp-server = { path = "../gstreamer-rtsp-server", optional = true }
gstreamer-rtsp-server-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"], optional = true }
gtk = { git = "https://github.com/gtk-rs/gtk", optional = true }
gdk = { git = "https://github.com/gtk-rs/gdk", optional = true }
gio = { git = "https://github.com/gtk-rs/gio", optional = true }
glib = { version = "0.9" }
gstreamer = { version = "0.15", path = "../gstreamer" }
gstreamer-gl = { version = "0.15", path = "../gstreamer-gl", optional = true }
gstreamer-app = { version = "0.15", path = "../gstreamer-app" }
gstreamer-audio = { version = "0.15", path = "../gstreamer-audio" }
gstreamer-base = { version = "0.15", path = "../gstreamer-base" }
gstreamer-video = { version = "0.15", path = "../gstreamer-video" }
gstreamer-pbutils = { version = "0.15", path = "../gstreamer-pbutils" }
gstreamer-player = { version = "0.15", path = "../gstreamer-player", optional = true }
gstreamer-editing-services = { version = "0.15", path = "../gstreamer-editing-services", optional = true }
gstreamer-sdp = { version = "0.15", path = "../gstreamer-sdp", optional = true }
gstreamer-rtsp = { version = "0.15", path = "../gstreamer-rtsp", optional = true }
gstreamer-rtsp-server = { version = "0.15", path = "../gstreamer-rtsp-server", optional = true }
gstreamer-rtsp-server-sys = { version = "0.8", features = ["v1_8"], optional = true }
gtk = { version = "0.8", optional = true }
gdk = { version = "0.12", optional = true }
gio = { version = "0.8", optional = true }
futures = "0.3"
byte-slice-cast = "0.3"
failure = "0.1"
failure_derive = "0.1"
cairo-rs = { git = "https://github.com/gtk-rs/cairo", features=["use_glib"], optional = true }
pango = { git = "https://github.com/gtk-rs/pango", optional = true }
pangocairo = { git = "https://github.com/gtk-rs/pangocairo", optional = true }
cairo-rs = { version = "0.8", features=["use_glib"], optional = true }
pango = { version = "0.8", optional = true }
pangocairo = { version = "0.9", optional = true }
glutin = { version = "0.21", optional = true }
winit = { version = "0.19", optional = true }
lazy_static = "1.0"
@ -43,7 +44,7 @@ gtksink = ["gtk", "gio"]
gtkvideooverlay = ["gtk", "gdk", "gio"]
gtkvideooverlay-x11 = ["gtkvideooverlay"]
gtkvideooverlay-quartz = ["gtkvideooverlay"]
gst-rtsp-server = ["gstreamer-rtsp-server"]
gst-rtsp-server = ["gstreamer-rtsp-server", "gstreamer-rtsp", "gstreamer-sdp"]
gst-rtsp-server-record = ["gstreamer-rtsp-server-sys", "gstreamer-rtsp-server", "gstreamer-rtsp", "gio"]
v1_10 = ["gstreamer/v1_10"]
pango-cairo = ["pango", "pangocairo", "cairo-rs"]
@ -110,6 +111,10 @@ name = "rtpfecserver"
name = "rtsp-server"
required-features = ["gst-rtsp-server"]
[[bin]]
name = "rtsp-server-subclass"
required-features = ["gst-rtsp-server"]
[[bin]]
name = "tagsetter"

View file

@ -168,7 +168,7 @@ fn main_loop(pipeline: gst::Pipeline) -> Result<(), Error> {
.map(|s| String::from(s.get_path_string()))
.unwrap_or_else(|| String::from("None")),
error: err.get_error().description().into(),
debug: Some(err.get_debug().unwrap().to_string()),
debug: err.get_debug(),
cause: err.get_error(),
}
.into());

View file

@ -160,7 +160,7 @@ fn main_loop(pipeline: gst::Pipeline) -> Result<(), Error> {
.map(|s| String::from(s.get_path_string()))
.unwrap_or_else(|| String::from("None")),
error: err.get_error().description().into(),
debug: Some(err.get_debug().unwrap().to_string()),
debug: err.get_debug(),
cause: err.get_error(),
}
.into());

View file

@ -281,7 +281,7 @@ fn example_main() -> Result<(), Error> {
.map(|s| String::from(s.get_path_string()))
.unwrap_or_else(|| String::from("None")),
error: err.get_error().description().into(),
debug: Some(err.get_debug().unwrap().to_string()),
debug: err.get_debug(),
cause: err.get_error(),
}
.into()),
@ -295,7 +295,7 @@ fn example_main() -> Result<(), Error> {
.map(|s| String::from(s.get_path_string()))
.unwrap_or_else(|| String::from("None")),
error: err.get_error().description().into(),
debug: Some(err.get_debug().unwrap().to_string()),
debug: err.get_debug(),
cause: err.get_error(),
}
.into());

View file

@ -311,7 +311,7 @@ fn example_main() -> Result<(), Error> {
.map(|s| String::from(s.get_path_string()))
.unwrap_or_else(|| String::from("None")),
error: err.get_error().description().into(),
debug: Some(err.get_debug().unwrap().to_string()),
debug: err.get_debug(),
cause: err.get_error(),
}
.into()),
@ -325,7 +325,7 @@ fn example_main() -> Result<(), Error> {
.map(|s| String::from(s.get_path_string()))
.unwrap_or_else(|| String::from("None")),
error: err.get_error().description().into(),
debug: Some(err.get_debug().unwrap().to_string()),
debug: err.get_debug(),
cause: err.get_error(),
}
.into());

View file

@ -594,7 +594,7 @@ impl App {
.map(|s| String::from(s.get_path_string()))
.unwrap_or_else(|| String::from("None")),
error: err.get_error().description().into(),
debug: Some(err.get_debug().unwrap().to_string()),
debug: err.get_debug(),
cause: err.get_error(),
}
.into());

View file

@ -144,7 +144,6 @@ fn create_ui(app: &gtk::Application) {
// simplifies the code within the callback. What this actually does, however, is creating
// a memory leak.
let video_overlay = sink
.clone()
.dynamic_cast::<gst_video::VideoOverlay>()
.unwrap()
.downgrade();

View file

@ -213,7 +213,6 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
})
.unwrap();
let drawer_clone = drawer.clone();
// Add a signal handler to the overlay's "caps-changed" signal. This could e.g.
// be called when the sink that we render to does not support resizing the image
// itself - but the user just changed the window-size. The element after the overlay
@ -226,7 +225,6 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
let _overlay = args[0].get::<gst::Element>().unwrap().unwrap();
let caps = args[1].get::<gst::Caps>().unwrap().unwrap();
let drawer = &drawer_clone;
let mut drawer = drawer.lock().unwrap();
drawer.info = Some(gst_video::VideoInfo::from_caps(&caps).unwrap());
@ -257,7 +255,7 @@ fn main_loop(pipeline: gst::Pipeline) -> Result<(), Error> {
.map(|s| String::from(s.get_path_string()))
.unwrap_or_else(|| String::from("None")),
error: err.get_error().description().into(),
debug: Some(err.get_debug().unwrap().to_string()),
debug: err.get_debug(),
cause: err.get_error(),
}
.into());

View file

@ -275,7 +275,7 @@ fn example_main() -> Result<(), Error> {
.map(|s| String::from(s.get_path_string()))
.unwrap_or_else(|| String::from("None")),
error: err.get_error().description().into(),
debug: Some(err.get_debug().unwrap().to_string()),
debug: err.get_debug(),
cause: err.get_error(),
}
.into());

View file

@ -146,9 +146,8 @@ fn example_main() -> Result<(), Error> {
let sinkpad = get_static_pad(&sink, "sink")?;
srcpad.link(&sinkpad)?;
let convclone = conv.clone();
src.connect_pad_added(move |decodebin, src_pad| {
match connect_decodebin_pad(&src_pad, &convclone) {
src.connect_pad_added(
move |decodebin, src_pad| match connect_decodebin_pad(&src_pad, &conv) {
Ok(_) => (),
Err(err) => {
gst_element_error!(
@ -158,8 +157,8 @@ fn example_main() -> Result<(), Error> {
["{}", err]
);
}
}
});
},
);
let video_caps = gst::Caps::new_simple("video/x-raw", &[]);
@ -199,7 +198,7 @@ fn example_main() -> Result<(), Error> {
.map(|s| String::from(s.get_path_string()))
.unwrap_or_else(|| String::from("None")),
error: err.get_error().description().into(),
debug: Some(err.get_debug().unwrap().to_string()),
debug: err.get_debug(),
cause: err.get_error(),
}
.into());

View file

@ -0,0 +1,481 @@
// This example demonstrates how to set up a rtsp server using GStreamer
// and extending the behaviour by subclass RTSPMediaFactory and RTSPMedia.
// For this, the example creates a videotestsrc pipeline manually to be used
// by the RTSP server for providing data, and adds a custom attribute to the
// SDP provided to the client.
//
// It also comes with a custom RTSP server/client subclass for hooking into
// the client machinery and printing some status.
extern crate gstreamer as gst;
extern crate gstreamer_rtsp as gst_rtsp;
extern crate gstreamer_rtsp_server as gst_rtsp_server;
extern crate gstreamer_sdp as gst_sdp;
use gst_rtsp_server::prelude::*;
#[macro_use]
extern crate glib;
extern crate failure;
use failure::Error;
#[macro_use]
extern crate failure_derive;
#[path = "../examples-common.rs"]
mod examples_common;
#[derive(Debug, Fail)]
#[fail(display = "Could not get mount points")]
struct NoMountPoints;
#[derive(Debug, Fail)]
#[fail(display = "Usage: {} LAUNCH_LINE", _0)]
struct UsageError(String);
fn main_loop() -> Result<(), Error> {
let main_loop = glib::MainLoop::new(None, false);
let server = server::Server::new();
// Much like HTTP servers, RTSP servers have multiple endpoints that
// provide different streams. Here, we ask our server to give
// us a reference to his list of endpoints, so we can add our
// test endpoint, providing the pipeline from the cli.
let mounts = server.get_mount_points().ok_or(NoMountPoints)?;
// Next, we create our custom factory for the endpoint we want to create.
// The job of the factory is to create a new pipeline for each client that
// connects, or (if configured to do so) to reuse an existing pipeline.
let factory = media_factory::Factory::new();
// This setting specifies whether each connecting client gets the output
// of a new instance of the pipeline, or whether all connected clients share
// the output of the same pipeline.
// If you want to stream a fixed video you have stored on the server to any
// client, you would not set this to shared here (since every client wants
// to start at the beginning of the video). But if you want to distribute
// a live source, you will probably want to set this to shared, to save
// computing and memory capacity on the server.
factory.set_shared(true);
// Now we add a new mount-point and tell the RTSP server to serve the content
// provided by the factory we configured above, when a client connects to
// this specific path.
mounts.add_factory("/test", &factory);
// Attach the server to our main context.
// A main context is the thing where other stuff is registering itself for its
// events (e.g. sockets, GStreamer bus, ...) and the main loop is something that
// polls the main context for its events and dispatches them to whoever is
// interested in them. In this example, we only do have one, so we can
// leave the context parameter empty, it will automatically select
// the default one.
let id = server.attach(None);
println!(
"Stream ready at rtsp://127.0.0.1:{}/test",
server.get_bound_port()
);
// Start the mainloop. From this point on, the server will start to serve
// our quality content to connecting clients.
main_loop.run();
glib::source_remove(id);
Ok(())
}
// Our custom media factory that creates a media input manually
mod media_factory {
use super::*;
use glib::subclass;
use glib::subclass::prelude::*;
use glib::translate::*;
extern crate gstreamer_rtsp_server as gst_rtsp_server;
use gst_rtsp_server::subclass::prelude::*;
// In the imp submodule we include the actual implementation
mod imp {
use super::*;
// This is the private data of our factory
pub struct Factory {}
// This trait registers our type with the GObject object system and
// provides the entry points for creating a new instance and setting
// up the class data
impl ObjectSubclass for Factory {
const NAME: &'static str = "RsRTSPMediaFactory";
type ParentType = gst_rtsp_server::RTSPMediaFactory;
type Instance = gst::subclass::ElementInstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
// This macro provides some boilerplate
glib_object_subclass!();
// Called when a new instance is to be created. We need to return an instance
// of our struct here.
fn new() -> Self {
Self {}
}
}
// Implementation of glib::Object virtual methods
impl ObjectImpl for Factory {
// This macro provides some boilerplate.
glib_object_impl!();
fn constructed(&self, obj: &glib::Object) {
self.parent_constructed(obj);
let factory = obj
.downcast_ref::<gst_rtsp_server::RTSPMediaFactory>()
.unwrap();
// All media created by this factory are our custom media type. This would
// not require a media factory subclass and can also be called on the normal
// RTSPMediaFactory.
factory.set_media_gtype(super::media::Media::static_type());
}
}
// Implementation of gst_rtsp_server::RTSPMediaFactory virtual methods
impl RTSPMediaFactoryImpl for Factory {
fn create_element(
&self,
_factory: &gst_rtsp_server::RTSPMediaFactory,
_url: &gst_rtsp::RTSPUrl,
) -> Option<gst::Element> {
// Create a simple VP8 videotestsrc input
let bin = gst::Bin::new(None);
let src = gst::ElementFactory::make("videotestsrc", None).unwrap();
let enc = gst::ElementFactory::make("vp8enc", None).unwrap();
// The names of the payloaders must be payX
let pay = gst::ElementFactory::make("rtpvp8pay", Some("pay0")).unwrap();
// Configure the videotestsrc live
src.set_property("is-live", &true).unwrap();
// Produce encoded data as fast as possible
enc.set_property("deadline", &1i64).unwrap();
bin.add_many(&[&src, &enc, &pay]).unwrap();
gst::Element::link_many(&[&src, &enc, &pay]).unwrap();
Some(bin.upcast())
}
}
}
// This here defines the public interface of our factory and implements
// the corresponding traits so that it behaves like any other RTSPMediaFactory
glib_wrapper! {
pub struct Factory(
Object<
gst::subclass::ElementInstanceStruct<imp::Factory>,
subclass::simple::ClassStruct<imp::Factory>,
FactoryClass
>
) @extends gst_rtsp_server::RTSPMediaFactory;
match fn {
get_type => || imp::Factory::get_type().to_glib(),
}
}
// Factories must be Send+Sync, and ours is
unsafe impl Send for Factory {}
unsafe impl Sync for Factory {}
impl Factory {
// Creates a new instance of our factory
pub fn new() -> Factory {
glib::Object::new(Self::static_type(), &[])
.expect("Failed to create factory")
.downcast()
.expect("Created factory is of wrong type")
}
}
}
// Our custom media subclass that adds a custom attribute to the SDP returned by DESCRIBE
mod media {
use super::*;
use glib::subclass;
use glib::subclass::prelude::*;
use glib::translate::*;
extern crate gstreamer_rtsp_server as gst_rtsp_server;
use gst_rtsp_server::subclass::prelude::*;
// In the imp submodule we include the actual implementation
mod imp {
use super::*;
// This is the private data of our media
pub struct Media {}
// This trait registers our type with the GObject object system and
// provides the entry points for creating a new instance and setting
// up the class data
impl ObjectSubclass for Media {
const NAME: &'static str = "RsRTSPMedia";
type ParentType = gst_rtsp_server::RTSPMedia;
type Instance = gst::subclass::ElementInstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
// This macro provides some boilerplate
glib_object_subclass!();
// Called when a new instance is to be created. We need to return an instance
// of our struct here.
fn new() -> Self {
Self {}
}
}
// Implementation of glib::Object virtual methods
impl ObjectImpl for Media {
// This macro provides some boilerplate.
glib_object_impl!();
}
// Implementation of gst_rtsp_server::RTSPMedia virtual methods
impl RTSPMediaImpl for Media {
fn setup_sdp(
&self,
media: &gst_rtsp_server::RTSPMedia,
sdp: &mut gst_sdp::SDPMessageRef,
info: &gst_rtsp_server::subclass::rtsp_media::SDPInfo,
) -> Result<(), gst::LoggableError> {
self.parent_setup_sdp(media, sdp, info)?;
sdp.add_attribute("my-custom-attribute", Some("has-a-value"));
Ok(())
}
}
}
// This here defines the public interface of our factory and implements
// the corresponding traits so that it behaves like any other RTSPMedia
glib_wrapper! {
pub struct Media(
Object<
gst::subclass::ElementInstanceStruct<imp::Media>,
subclass::simple::ClassStruct<imp::Media>,
MediaClass
>
) @extends gst_rtsp_server::RTSPMedia;
match fn {
get_type => || imp::Media::get_type().to_glib(),
}
}
// Medias must be Send+Sync, and ours is
unsafe impl Send for Media {}
unsafe impl Sync for Media {}
}
// Our custom RTSP server subclass that reports when clients are connecting and uses
// our custom RTSP client subclass for each client
mod server {
use super::*;
use glib::subclass;
use glib::subclass::prelude::*;
use glib::translate::*;
extern crate gstreamer_rtsp_server as gst_rtsp_server;
use gst_rtsp_server::subclass::prelude::*;
// In the imp submodule we include the actual implementation
mod imp {
use super::*;
// This is the private data of our server
pub struct Server {}
// This trait registers our type with the GObject object system and
// provides the entry points for creating a new instance and setting
// up the class data
impl ObjectSubclass for Server {
const NAME: &'static str = "RsRTSPServer";
type ParentType = gst_rtsp_server::RTSPServer;
type Instance = gst::subclass::ElementInstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
// This macro provides some boilerplate
glib_object_subclass!();
// Called when a new instance is to be created. We need to return an instance
// of our struct here.
fn new() -> Self {
Self {}
}
}
// Implementation of glib::Object virtual methods
impl ObjectImpl for Server {
// This macro provides some boilerplate.
glib_object_impl!();
}
// Implementation of gst_rtsp_server::RTSPServer virtual methods
impl RTSPServerImpl for Server {
fn create_client(
&self,
server: &gst_rtsp_server::RTSPServer,
) -> Option<gst_rtsp_server::RTSPClient> {
let client = super::client::Client::new();
// Duplicated from the default implementation
client.set_session_pool(server.get_session_pool().as_ref());
client.set_mount_points(server.get_mount_points().as_ref());
client.set_auth(server.get_auth().as_ref());
client.set_thread_pool(server.get_thread_pool().as_ref());
Some(client.upcast())
}
fn client_connected(
&self,
server: &gst_rtsp_server::RTSPServer,
client: &gst_rtsp_server::RTSPClient,
) {
self.parent_client_connected(server, client);
println!("Client {:?} connected", client);
}
}
}
// This here defines the public interface of our factory and implements
// the corresponding traits so that it behaves like any other RTSPServer
glib_wrapper! {
pub struct Server(
Object<
gst::subclass::ElementInstanceStruct<imp::Server>,
subclass::simple::ClassStruct<imp::Server>,
ServerClass
>
) @extends gst_rtsp_server::RTSPServer;
match fn {
get_type => || imp::Server::get_type().to_glib(),
}
}
// Servers must be Send+Sync, and ours is
unsafe impl Send for Server {}
unsafe impl Sync for Server {}
impl Server {
// Creates a new instance of our factory
pub fn new() -> Server {
glib::Object::new(Self::static_type(), &[])
.expect("Failed to create server")
.downcast()
.expect("Created server is of wrong type")
}
}
}
// Our custom RTSP client subclass.
mod client {
use super::*;
use glib::subclass;
use glib::subclass::prelude::*;
use glib::translate::*;
extern crate gstreamer_rtsp_server as gst_rtsp_server;
use gst_rtsp_server::subclass::prelude::*;
// In the imp submodule we include the actual implementation
mod imp {
use super::*;
// This is the private data of our server
pub struct Client {}
// This trait registers our type with the GObject object system and
// provides the entry points for creating a new instance and setting
// up the class data
impl ObjectSubclass for Client {
const NAME: &'static str = "RsRTSPClient";
type ParentType = gst_rtsp_server::RTSPClient;
type Instance = gst::subclass::ElementInstanceStruct<Self>;
type Class = subclass::simple::ClassStruct<Self>;
// This macro provides some boilerplate
glib_object_subclass!();
// Called when a new instance is to be created. We need to return an instance
// of our struct here.
fn new() -> Self {
Self {}
}
}
// Implementation of glib::Object virtual methods
impl ObjectImpl for Client {
// This macro provides some boilerplate.
glib_object_impl!();
}
// Implementation of gst_rtsp_server::RTSPClient virtual methods
impl RTSPClientImpl for Client {
fn closed(&self, client: &gst_rtsp_server::RTSPClient) {
self.parent_closed(client);
println!("Client {:?} closed", client);
}
}
}
// This here defines the public interface of our factory and implements
// the corresponding traits so that it behaves like any other RTSPClient
glib_wrapper! {
pub struct Client(
Object<
gst::subclass::ElementInstanceStruct<imp::Client>,
subclass::simple::ClassStruct<imp::Client>,
ClientClass
>
) @extends gst_rtsp_server::RTSPClient;
match fn {
get_type => || imp::Client::get_type().to_glib(),
}
}
// Clients must be Send+Sync, and ours is
unsafe impl Send for Client {}
unsafe impl Sync for Client {}
impl Client {
// Creates a new instance of our factory
pub fn new() -> Client {
glib::Object::new(Self::static_type(), &[])
.expect("Failed to create client")
.downcast()
.expect("Created client is of wrong type")
}
}
}
fn example_main() -> Result<(), Error> {
gst::init()?;
main_loop()
}
fn main() {
match examples_common::run(example_main) {
Ok(r) => r,
Err(e) => eprintln!("Error! {}", e),
}
}

View file

@ -385,7 +385,7 @@ fn main_loop(pipeline: gst::Pipeline) -> Result<(), Error> {
.map(|s| String::from(s.get_path_string()))
.unwrap_or_else(|| String::from("None")),
error: err.get_error().description().into(),
debug: Some(err.get_debug().unwrap().to_string()),
debug: err.get_debug(),
cause: err.get_error(),
}
.into());

View file

@ -109,7 +109,7 @@ fn example_main() -> Result<(), Error> {
.unwrap_or_else(|| "None".into())
.to_string(),
error: err.get_error().description().into(),
debug: Some(err.get_debug().unwrap().to_string()),
debug: err.get_debug(),
cause: err.get_error(),
}
.into());

View file

@ -175,7 +175,7 @@ fn example_main() -> Result<(), Error> {
.map(|s| String::from(s.get_path_string()))
.unwrap_or_else(|| String::from("None")),
error: err.get_error().description().into(),
debug: Some(err.get_debug().unwrap().to_string()),
debug: err.get_debug(),
cause: err.get_error(),
}
.into());

View file

@ -5,6 +5,75 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +685,11 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...HEAD
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-app"
version = "0.15.0"
version = "0.15.4"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer App library"
@ -13,19 +13,26 @@ keywords = ["gstreamer", "multimedia", "audio", "video", "gnome"]
build = "build.rs"
[dependencies]
futures-core = "0.3"
futures-sink = "0.3"
bitflags = "1.0"
libc = "0.2"
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-app-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
glib = { git = "https://github.com/gtk-rs/glib" }
gstreamer = { path = "../gstreamer" }
gstreamer-base = { path = "../gstreamer-base" }
glib-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-app-sys = { version = "0.8", features = ["v1_8"] }
glib = { version = "0.9" }
gstreamer = { version = "0.15", path = "../gstreamer" }
gstreamer-base = { version = "0.15", path = "../gstreamer-base" }
lazy_static = "1.0"
[build-dependencies]
rustdoc-stripper = { version = "0.1", optional = true }
[dev-dependencies]
futures-util = { version = "0.3", features = ["sink"] }
futures-executor = "0.3"
[features]
default = []
v1_10 = ["gstreamer/v1_10", "gstreamer-base/v1_10", "gstreamer-app-sys/v1_10"]

View file

@ -12,14 +12,32 @@ use glib::signal::SignalHandlerId;
use glib::translate::*;
use glib_sys::gpointer;
use gst;
use gst::gst_element_error;
use gst_app_sys;
use gst_sys;
use std::boxed::Box as Box_;
use std::cell::RefCell;
use std::mem::transmute;
use std::panic;
use std::ptr;
use std::sync::atomic::{AtomicBool, Ordering};
use AppSink;
#[cfg(any(feature = "v1_10"))]
use {
futures_core::Stream,
std::{
pin::Pin,
sync::{Arc, Mutex},
task::{Context, Poll, Waker},
},
};
lazy_static! {
static ref SET_ONCE_QUARK: glib::Quark =
glib::Quark::from_string("gstreamer-rs-app-sink-callbacks");
}
#[allow(clippy::type_complexity)]
pub struct AppSinkCallbacks {
eos: Option<RefCell<Box<dyn FnMut(&AppSink) + Send + 'static>>>,
@ -33,6 +51,7 @@ pub struct AppSinkCallbacks {
Box<dyn FnMut(&AppSink) -> Result<gst::FlowSuccess, gst::FlowError> + Send + 'static>,
>,
>,
panicked: AtomicBool,
callbacks: gst_app_sys::GstAppSinkCallbacks,
}
@ -107,6 +126,7 @@ impl AppSinkCallbacksBuilder {
eos: self.eos,
new_preroll: self.new_preroll,
new_sample: self.new_sample,
panicked: AtomicBool::new(false),
callbacks: gst_app_sys::GstAppSinkCallbacks {
eos: if have_eos { Some(trampoline_eos) } else { None },
new_preroll: if have_new_preroll {
@ -130,11 +150,37 @@ impl AppSinkCallbacksBuilder {
}
}
fn post_panic_error_message(element: &AppSink, err: &dyn std::any::Any) {
if let Some(cause) = err.downcast_ref::<&str>() {
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked: {}", cause]);
} else if let Some(cause) = err.downcast_ref::<String>() {
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked: {}", cause]);
} else {
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked"]);
}
}
unsafe extern "C" fn trampoline_eos(appsink: *mut gst_app_sys::GstAppSink, callbacks: gpointer) {
let callbacks = &*(callbacks as *const AppSinkCallbacks);
let element: AppSink = from_glib_borrow(appsink);
if callbacks.panicked.load(Ordering::Relaxed) {
let element: AppSink = from_glib_borrow(appsink);
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked"]);
return;
}
if let Some(ref eos) = callbacks.eos {
(&mut *eos.borrow_mut())(&from_glib_borrow(appsink))
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
(&mut *eos.borrow_mut())(&element)
}));
match result {
Ok(result) => result,
Err(err) => {
callbacks.panicked.store(true, Ordering::Relaxed);
post_panic_error_message(&element, &err);
}
}
}
}
@ -143,9 +189,27 @@ unsafe extern "C" fn trampoline_new_preroll(
callbacks: gpointer,
) -> gst_sys::GstFlowReturn {
let callbacks = &*(callbacks as *const AppSinkCallbacks);
let element: AppSink = from_glib_borrow(appsink);
if callbacks.panicked.load(Ordering::Relaxed) {
let element: AppSink = from_glib_borrow(appsink);
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked"]);
return gst::FlowReturn::Error.to_glib();
}
let ret = if let Some(ref new_preroll) = callbacks.new_preroll {
(&mut *new_preroll.borrow_mut())(&from_glib_borrow(appsink)).into()
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
(&mut *new_preroll.borrow_mut())(&element).into()
}));
match result {
Ok(result) => result,
Err(err) => {
callbacks.panicked.store(true, Ordering::Relaxed);
post_panic_error_message(&element, &err);
gst::FlowReturn::Error
}
}
} else {
gst::FlowReturn::Error
};
@ -158,9 +222,27 @@ unsafe extern "C" fn trampoline_new_sample(
callbacks: gpointer,
) -> gst_sys::GstFlowReturn {
let callbacks = &*(callbacks as *const AppSinkCallbacks);
let element: AppSink = from_glib_borrow(appsink);
if callbacks.panicked.load(Ordering::Relaxed) {
let element: AppSink = from_glib_borrow(appsink);
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked"]);
return gst::FlowReturn::Error.to_glib();
}
let ret = if let Some(ref new_sample) = callbacks.new_sample {
(&mut *new_sample.borrow_mut())(&from_glib_borrow(appsink)).into()
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
(&mut *new_sample.borrow_mut())(&element).into()
}));
match result {
Ok(result) => result,
Err(err) => {
callbacks.panicked.store(true, Ordering::Relaxed);
post_panic_error_message(&element, &err);
gst::FlowReturn::Error
}
}
} else {
gst::FlowReturn::Error
};
@ -175,8 +257,26 @@ unsafe extern "C" fn destroy_callbacks(ptr: gpointer) {
impl AppSink {
pub fn set_callbacks(&self, callbacks: AppSinkCallbacks) {
unsafe {
let sink = self.to_glib_none().0;
// This is not thread-safe before 1.16.3, see
// https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/merge_requests/570
if gst::version() < (1, 16, 3, 0) {
if !gobject_sys::g_object_get_qdata(sink as *mut _, SET_ONCE_QUARK.to_glib())
.is_null()
{
panic!("AppSink callbacks can only be set once");
}
gobject_sys::g_object_set_qdata(
sink as *mut _,
SET_ONCE_QUARK.to_glib(),
1 as *mut _,
);
}
gst_app_sys::gst_app_sink_set_callbacks(
self.to_glib_none().0,
sink,
mut_override(&callbacks.callbacks),
Box::into_raw(Box::new(callbacks)) as *mut _,
Some(destroy_callbacks),
@ -217,6 +317,11 @@ impl AppSink {
)
}
}
#[cfg(any(feature = "v1_10"))]
pub fn stream(&self) -> AppSinkStream {
AppSinkStream::new(self)
}
}
unsafe extern "C" fn new_sample_trampoline<
@ -240,3 +345,116 @@ unsafe extern "C" fn new_preroll_trampoline<
let ret: gst::FlowReturn = f(&from_glib_borrow(this)).into();
ret.to_glib()
}
#[cfg(any(feature = "v1_10"))]
#[derive(Debug)]
pub struct AppSinkStream {
app_sink: AppSink,
waker_reference: Arc<Mutex<Option<Waker>>>,
}
#[cfg(any(feature = "v1_10"))]
impl AppSinkStream {
fn new(app_sink: &AppSink) -> Self {
skip_assert_initialized!();
let app_sink = app_sink.clone();
let waker_reference = Arc::new(Mutex::new(None as Option<Waker>));
app_sink.set_callbacks(
AppSinkCallbacks::new()
.new_sample({
let waker_reference = Arc::clone(&waker_reference);
move |_| {
if let Some(waker) = waker_reference.lock().unwrap().take() {
waker.wake();
}
Ok(gst::FlowSuccess::Ok)
}
})
.eos({
let waker_reference = Arc::clone(&waker_reference);
move |_| {
if let Some(waker) = waker_reference.lock().unwrap().take() {
waker.wake();
}
}
})
.build(),
);
Self {
app_sink,
waker_reference,
}
}
}
#[cfg(any(feature = "v1_10"))]
impl Drop for AppSinkStream {
fn drop(&mut self) {
// This is not thread-safe before 1.16.3, see
// https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/merge_requests/570
if gst::version() >= (1, 16, 3, 0) {
self.app_sink.set_callbacks(AppSinkCallbacks::new().build());
}
}
}
#[cfg(any(feature = "v1_10"))]
impl Stream for AppSinkStream {
type Item = gst::Sample;
fn poll_next(self: Pin<&mut Self>, context: &mut Context) -> Poll<Option<Self::Item>> {
let mut waker = self.waker_reference.lock().unwrap();
self.app_sink
.try_pull_sample(gst::ClockTime::from_mseconds(0))
.map(|sample| Poll::Ready(Some(sample)))
.unwrap_or_else(|| {
if self.app_sink.is_eos() {
return Poll::Ready(None);
}
waker.replace(context.waker().to_owned());
Poll::Pending
})
}
}
#[cfg(any(feature = "v1_10"))]
#[cfg(test)]
mod tests {
use super::*;
use futures_util::StreamExt;
use gst::prelude::*;
#[test]
fn test_app_sink_stream() {
gst::init().unwrap();
let videotestsrc = gst::ElementFactory::make("videotestsrc", None).unwrap();
let appsink = gst::ElementFactory::make("appsink", None).unwrap();
videotestsrc.set_property("num-buffers", &5).unwrap();
let pipeline = gst::Pipeline::new(None);
pipeline.add(&videotestsrc).unwrap();
pipeline.add(&appsink).unwrap();
videotestsrc.link(&appsink).unwrap();
let app_sink_stream = appsink.dynamic_cast::<AppSink>().unwrap().stream();
let samples_future = app_sink_stream.collect::<Vec<gst::Sample>>();
pipeline.set_state(gst::State::Playing).unwrap();
let samples = futures_executor::block_on(samples_future);
pipeline.set_state(gst::State::Null).unwrap();
assert_eq!(samples.len(), 5);
}
}

View file

@ -6,20 +6,34 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use futures_sink::Sink;
use glib::translate::*;
use glib_sys::{gboolean, gpointer};
use gst;
use gst::gst_element_error;
use gst_app_sys;
use std::cell::RefCell;
use std::mem;
use std::panic;
use std::pin::Pin;
use std::ptr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll, Waker};
use AppSrc;
lazy_static! {
static ref SET_ONCE_QUARK: glib::Quark =
glib::Quark::from_string("gstreamer-rs-app-src-callbacks");
}
#[allow(clippy::type_complexity)]
pub struct AppSrcCallbacks {
need_data: Option<RefCell<Box<dyn FnMut(&AppSrc, u32) + Send + 'static>>>,
enough_data: Option<Box<dyn Fn(&AppSrc) + Send + Sync + 'static>>,
seek_data: Option<Box<dyn Fn(&AppSrc, u64) -> bool + Send + Sync + 'static>>,
panicked: AtomicBool,
callbacks: gst_app_sys::GstAppSrcCallbacks,
}
@ -80,6 +94,7 @@ impl AppSrcCallbacksBuilder {
need_data: self.need_data,
enough_data: self.enough_data,
seek_data: self.seek_data,
panicked: AtomicBool::new(false),
callbacks: gst_app_sys::GstAppSrcCallbacks {
need_data: if have_need_data {
Some(trampoline_need_data)
@ -107,15 +122,41 @@ impl AppSrcCallbacksBuilder {
}
}
fn post_panic_error_message(element: &AppSrc, err: &dyn std::any::Any) {
if let Some(cause) = err.downcast_ref::<&str>() {
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked: {}", cause]);
} else if let Some(cause) = err.downcast_ref::<String>() {
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked: {}", cause]);
} else {
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked"]);
}
}
unsafe extern "C" fn trampoline_need_data(
appsrc: *mut gst_app_sys::GstAppSrc,
length: u32,
callbacks: gpointer,
) {
let callbacks = &*(callbacks as *const AppSrcCallbacks);
let element: AppSrc = from_glib_borrow(appsrc);
if callbacks.panicked.load(Ordering::Relaxed) {
let element: AppSrc = from_glib_borrow(appsrc);
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked"]);
return;
}
if let Some(ref need_data) = callbacks.need_data {
(&mut *need_data.borrow_mut())(&from_glib_borrow(appsrc), length);
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| {
(&mut *need_data.borrow_mut())(&element, length)
}));
match result {
Ok(result) => result,
Err(err) => {
callbacks.panicked.store(true, Ordering::Relaxed);
post_panic_error_message(&element, &err);
}
}
}
}
@ -124,9 +165,23 @@ unsafe extern "C" fn trampoline_enough_data(
callbacks: gpointer,
) {
let callbacks = &*(callbacks as *const AppSrcCallbacks);
let element: AppSrc = from_glib_borrow(appsrc);
if callbacks.panicked.load(Ordering::Relaxed) {
let element: AppSrc = from_glib_borrow(appsrc);
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked"]);
return;
}
if let Some(ref enough_data) = callbacks.enough_data {
(*enough_data)(&from_glib_borrow(appsrc));
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| (*enough_data)(&element)));
match result {
Ok(result) => result,
Err(err) => {
callbacks.panicked.store(true, Ordering::Relaxed);
post_panic_error_message(&element, &err);
}
}
}
}
@ -136,9 +191,26 @@ unsafe extern "C" fn trampoline_seek_data(
callbacks: gpointer,
) -> gboolean {
let callbacks = &*(callbacks as *const AppSrcCallbacks);
let element: AppSrc = from_glib_borrow(appsrc);
if callbacks.panicked.load(Ordering::Relaxed) {
let element: AppSrc = from_glib_borrow(appsrc);
gst_element_error!(&element, gst::LibraryError::Failed, ["Panicked"]);
return false.to_glib();
}
let ret = if let Some(ref seek_data) = callbacks.seek_data {
(*seek_data)(&from_glib_borrow(appsrc), offset)
let result =
panic::catch_unwind(panic::AssertUnwindSafe(|| (*seek_data)(&element, offset)));
match result {
Ok(result) => result,
Err(err) => {
callbacks.panicked.store(true, Ordering::Relaxed);
post_panic_error_message(&element, &err);
false
}
}
} else {
false
};
@ -196,8 +268,25 @@ impl AppSrc {
pub fn set_callbacks(&self, callbacks: AppSrcCallbacks) {
unsafe {
let src = self.to_glib_none().0;
// This is not thread-safe before 1.16.3, see
// https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/merge_requests/570
if gst::version() < (1, 16, 3, 0) {
if !gobject_sys::g_object_get_qdata(src as *mut _, SET_ONCE_QUARK.to_glib())
.is_null()
{
panic!("AppSrc callbacks can only be set once");
}
gobject_sys::g_object_set_qdata(
src as *mut _,
SET_ONCE_QUARK.to_glib(),
1 as *mut _,
);
}
gst_app_sys::gst_app_src_set_callbacks(
self.to_glib_none().0,
src,
mut_override(&callbacks.callbacks),
Box::into_raw(Box::new(callbacks)) as *mut _,
Some(destroy_callbacks),
@ -227,4 +316,156 @@ impl AppSrc {
(from_glib(min.assume_init()), from_glib(max.assume_init()))
}
}
pub fn sink(&self) -> AppSrcSink {
AppSrcSink::new(self)
}
}
#[derive(Debug)]
pub struct AppSrcSink {
app_src: AppSrc,
waker_reference: Arc<Mutex<Option<Waker>>>,
}
impl AppSrcSink {
fn new(app_src: &AppSrc) -> Self {
skip_assert_initialized!();
let app_src = app_src.clone();
let waker_reference = Arc::new(Mutex::new(None as Option<Waker>));
app_src.set_callbacks(
AppSrcCallbacks::new()
.need_data({
let waker_reference = Arc::clone(&waker_reference);
move |_, _| {
if let Some(waker) = waker_reference.lock().unwrap().take() {
waker.wake();
}
}
})
.build(),
);
Self {
app_src,
waker_reference,
}
}
}
impl Drop for AppSrcSink {
fn drop(&mut self) {
// This is not thread-safe before 1.16.3, see
// https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/merge_requests/570
if gst::version() >= (1, 16, 3, 0) {
self.app_src.set_callbacks(AppSrcCallbacks::new().build());
}
}
}
impl Sink<gst::Sample> for AppSrcSink {
type Error = gst::FlowError;
fn poll_ready(self: Pin<&mut Self>, context: &mut Context) -> Poll<Result<(), Self::Error>> {
let mut waker = self.waker_reference.lock().unwrap();
let current_level_bytes = self.app_src.get_current_level_bytes();
let max_bytes = self.app_src.get_max_bytes();
if current_level_bytes >= max_bytes && max_bytes != 0 {
waker.replace(context.waker().to_owned());
Poll::Pending
} else {
Poll::Ready(Ok(()))
}
}
fn start_send(self: Pin<&mut Self>, sample: gst::Sample) -> Result<(), Self::Error> {
self.app_src.push_sample(&sample)?;
Ok(())
}
fn poll_flush(self: Pin<&mut Self>, _: &mut Context) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn poll_close(self: Pin<&mut Self>, _: &mut Context) -> Poll<Result<(), Self::Error>> {
self.app_src.end_of_stream()?;
Poll::Ready(Ok(()))
}
}
#[cfg(test)]
mod tests {
use super::*;
use futures_util::{sink::SinkExt, stream::StreamExt};
use gst::prelude::*;
use std::sync::atomic::{AtomicUsize, Ordering};
#[test]
fn test_app_src_sink() {
gst::init().unwrap();
let appsrc = gst::ElementFactory::make("appsrc", None).unwrap();
let fakesink = gst::ElementFactory::make("fakesink", None).unwrap();
fakesink.set_property("signal-handoffs", &true).unwrap();
let pipeline = gst::Pipeline::new(None);
pipeline.add(&appsrc).unwrap();
pipeline.add(&fakesink).unwrap();
appsrc.link(&fakesink).unwrap();
let mut bus_stream = pipeline.get_bus().unwrap().stream();
let mut app_src_sink = appsrc.dynamic_cast::<AppSrc>().unwrap().sink();
let sample_quantity = 5;
let samples = (0..sample_quantity)
.map(|_| gst::Sample::new().buffer(&gst::Buffer::new()).build())
.collect::<Vec<gst::Sample>>();
let mut sample_stream = futures_util::stream::iter(samples).map(Ok);
let handoff_count_reference = Arc::new(AtomicUsize::new(0));
fakesink
.connect("handoff", false, {
let handoff_count_reference = Arc::clone(&handoff_count_reference);
move |_| {
handoff_count_reference.fetch_add(1, Ordering::AcqRel);
None
}
})
.unwrap();
pipeline.set_state(gst::State::Playing).unwrap();
futures_executor::block_on(app_src_sink.send_all(&mut sample_stream)).unwrap();
futures_executor::block_on(app_src_sink.close()).unwrap();
while let Some(message) = futures_executor::block_on(bus_stream.next()) {
match message.view() {
gst::MessageView::Eos(_) => break,
gst::MessageView::Error(_) => unreachable!(),
_ => continue,
}
}
pipeline.set_state(gst::State::Null).unwrap();
assert_eq!(
handoff_count_reference.load(Ordering::Acquire),
sample_quantity
);
}
}

View file

@ -8,6 +8,8 @@
extern crate libc;
extern crate futures_core;
extern crate futures_sink;
extern crate glib_sys;
extern crate gobject_sys;
extern crate gstreamer as gst;
@ -15,9 +17,18 @@ extern crate gstreamer_app_sys as gst_app_sys;
extern crate gstreamer_base as gst_base;
extern crate gstreamer_sys as gst_sys;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate glib;
#[cfg(test)]
extern crate futures_util;
#[cfg(test)]
extern crate futures_executor;
macro_rules! skip_assert_initialized {
() => {};
}
@ -28,10 +39,11 @@ macro_rules! skip_assert_initialized {
mod auto;
pub use auto::*;
mod app_sink;
mod app_src;
pub use app_sink::*;
pub use app_src::*;
pub mod app_sink;
pub use app_sink::AppSinkCallbacks;
pub mod app_src;
pub use app_src::AppSrcCallbacks;
// Re-export all the traits in a prelude module, so that applications
// can always "use gst::prelude::*" without getting conflicts

View file

@ -5,6 +5,75 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +685,11 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...HEAD
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-audio"
version = "0.15.0"
version = "0.15.4"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer Audio library"
@ -15,14 +15,14 @@ build = "build.rs"
[dependencies]
libc = "0.2"
bitflags = "1.0"
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-base-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-audio-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
glib = { git = "https://github.com/gtk-rs/glib" }
gstreamer = { path = "../gstreamer" }
gstreamer-base = { path = "../gstreamer-base" }
glib-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-base-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-audio-sys = { version = "0.8", features = ["v1_8"] }
glib = { version = "0.9" }
gstreamer = { version = "0.15", path = "../gstreamer" }
gstreamer-base = { version = "0.15", path = "../gstreamer-base" }
array-init = "0.1"
[build-dependencies]

View file

@ -37,46 +37,48 @@ impl AudioStreamAlign {
}
#[cfg(any(feature = "v1_14", feature = "dox"))]
pub fn get_alignment_threshold(&mut self) -> gst::ClockTime {
pub fn get_alignment_threshold(&self) -> gst::ClockTime {
unsafe {
from_glib(
gst_audio_sys::gst_audio_stream_align_get_alignment_threshold(
self.to_glib_none_mut().0,
),
gst_audio_sys::gst_audio_stream_align_get_alignment_threshold(mut_override(
self.to_glib_none().0,
)),
)
}
}
#[cfg(any(feature = "v1_14", feature = "dox"))]
pub fn get_discont_wait(&mut self) -> gst::ClockTime {
pub fn get_discont_wait(&self) -> gst::ClockTime {
unsafe {
from_glib(gst_audio_sys::gst_audio_stream_align_get_discont_wait(
self.to_glib_none_mut().0,
mut_override(self.to_glib_none().0),
))
}
}
#[cfg(any(feature = "v1_14", feature = "dox"))]
pub fn get_rate(&mut self) -> i32 {
unsafe { gst_audio_sys::gst_audio_stream_align_get_rate(self.to_glib_none_mut().0) }
}
#[cfg(any(feature = "v1_14", feature = "dox"))]
pub fn get_samples_since_discont(&mut self) -> u64 {
pub fn get_rate(&self) -> i32 {
unsafe {
gst_audio_sys::gst_audio_stream_align_get_samples_since_discont(
self.to_glib_none_mut().0,
)
gst_audio_sys::gst_audio_stream_align_get_rate(mut_override(self.to_glib_none().0))
}
}
#[cfg(any(feature = "v1_14", feature = "dox"))]
pub fn get_timestamp_at_discont(&mut self) -> gst::ClockTime {
pub fn get_samples_since_discont(&self) -> u64 {
unsafe {
gst_audio_sys::gst_audio_stream_align_get_samples_since_discont(mut_override(
self.to_glib_none().0,
))
}
}
#[cfg(any(feature = "v1_14", feature = "dox"))]
pub fn get_timestamp_at_discont(&self) -> gst::ClockTime {
unsafe {
from_glib(
gst_audio_sys::gst_audio_stream_align_get_timestamp_at_discont(
self.to_glib_none_mut().0,
),
gst_audio_sys::gst_audio_stream_align_get_timestamp_at_discont(mut_override(
self.to_glib_none().0,
)),
)
}
}

View file

@ -5,6 +5,75 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +685,11 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...HEAD
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-base"
version = "0.15.0"
version = "0.15.4"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer Base library"
@ -15,12 +15,12 @@ build = "build.rs"
[dependencies]
libc = "0.2"
bitflags = "1.0"
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-base-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
glib = { git = "https://github.com/gtk-rs/glib" }
gstreamer = { path = "../gstreamer" }
glib-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-base-sys = { version = "0.8", features = ["v1_8"] }
glib = { version = "0.9" }
gstreamer = { version = "0.15", path = "../gstreamer" }
[build-dependencies]
rustdoc-stripper = { version = "0.1", optional = true }

View file

@ -21,7 +21,7 @@ impl<O: IsA<AggregatorPad>> AggregatorPadExtManual for O {
fn get_segment(&self) -> gst::Segment {
unsafe {
let ptr: &gst_base_sys::GstAggregatorPad = &*(self.as_ptr() as *const _);
::utils::MutexGuard::lock(&ptr.parent.object.lock);
let _guard = ::utils::MutexGuard::lock(&ptr.parent.object.lock);
from_glib_none(&ptr.segment as *const gst_sys::GstSegment)
}
}

View file

@ -39,7 +39,7 @@ impl<O: IsA<BaseSink>> BaseSinkExtManual for O {
fn get_segment(&self) -> gst::Segment {
unsafe {
let sink: &gst_base_sys::GstBaseSink = &*(self.as_ptr() as *const _);
::utils::MutexGuard::lock(&sink.element.object.lock);
let _guard = ::utils::MutexGuard::lock(&sink.element.object.lock);
from_glib_none(&sink.segment as *const _)
}
}

View file

@ -29,7 +29,7 @@ impl<O: IsA<BaseSrc>> BaseSrcExtManual for O {
fn get_segment(&self) -> gst::Segment {
unsafe {
let src: &gst_base_sys::GstBaseSrc = &*(self.as_ptr() as *const _);
::utils::MutexGuard::lock(&src.element.object.lock);
let _guard = ::utils::MutexGuard::lock(&src.element.object.lock);
from_glib_none(&src.segment as *const _)
}
}

View file

@ -20,7 +20,7 @@ impl<O: IsA<BaseTransform>> BaseTransformExtManual for O {
fn get_segment(&self) -> gst::Segment {
unsafe {
let trans: &gst_base_sys::GstBaseTransform = &*(self.as_ptr() as *const _);
::utils::MutexGuard::lock(&trans.element.object.lock);
let _guard = ::utils::MutexGuard::lock(&trans.element.object.lock);
from_glib_none(&trans.segment as *const _)
}
}

View file

@ -111,7 +111,7 @@ impl UniqueFlowCombiner {
self.0.add_pad(pad);
}
pub fn clear(&self) {
pub fn clear(&mut self) {
self.0.clear();
}

View file

@ -18,6 +18,7 @@ use gst;
use gst::subclass::prelude::*;
use std::mem;
use std::ptr;
use BaseTransform;
use BaseTransformClass;
@ -101,6 +102,14 @@ pub trait BaseTransformImpl: BaseTransformImplExt + ElementImpl + Send + Sync +
self.parent_src_event(element, event)
}
fn prepare_output_buffer(
&self,
element: &BaseTransform,
inbuf: &gst::BufferRef,
) -> Result<PreparedOutputBuffer, gst::FlowError> {
self.parent_prepare_output_buffer(element, inbuf)
}
fn transform(
&self,
element: &BaseTransform,
@ -125,6 +134,42 @@ pub trait BaseTransformImpl: BaseTransformImplExt + ElementImpl + Send + Sync +
) -> Result<gst::FlowSuccess, gst::FlowError> {
self.parent_transform_ip_passthrough(element, buf)
}
fn copy_metadata(
&self,
element: &BaseTransform,
inbuf: &gst::BufferRef,
outbuf: &mut gst::BufferRef,
) -> Result<(), gst::LoggableError> {
self.parent_copy_metadata(element, inbuf, outbuf)
}
fn transform_meta<'a>(
&self,
element: &BaseTransform,
outbuf: &mut gst::BufferRef,
meta: gst::MetaRef<'a, gst::Meta>,
inbuf: &'a gst::BufferRef,
) -> bool {
self.parent_transform_meta(element, outbuf, meta, inbuf)
}
fn before_transform(&self, element: &BaseTransform, inbuf: &gst::BufferRef) {
self.parent_before_transform(element, inbuf);
}
fn submit_input_buffer(
&self,
element: &BaseTransform,
is_discont: bool,
inbuf: gst::Buffer,
) -> Result<gst::FlowSuccess, gst::FlowError> {
self.parent_submit_input_buffer(element, is_discont, inbuf)
}
fn generate_output(&self, element: &BaseTransform) -> Result<GeneratedOutput, gst::FlowError> {
self.parent_generate_output(element)
}
}
pub trait BaseTransformImplExt {
@ -184,6 +229,12 @@ pub trait BaseTransformImplExt {
fn parent_src_event(&self, element: &BaseTransform, event: gst::Event) -> bool;
fn parent_prepare_output_buffer(
&self,
element: &BaseTransform,
inbuf: &gst::BufferRef,
) -> Result<PreparedOutputBuffer, gst::FlowError>;
fn parent_transform(
&self,
element: &BaseTransform,
@ -202,6 +253,45 @@ pub trait BaseTransformImplExt {
element: &BaseTransform,
buf: &gst::Buffer,
) -> Result<gst::FlowSuccess, gst::FlowError>;
fn parent_copy_metadata(
&self,
element: &BaseTransform,
inbuf: &gst::BufferRef,
outbuf: &mut gst::BufferRef,
) -> Result<(), gst::LoggableError>;
fn parent_transform_meta<'a>(
&self,
element: &BaseTransform,
outbuf: &mut gst::BufferRef,
meta: gst::MetaRef<'a, gst::Meta>,
inbuf: &'a gst::BufferRef,
) -> bool;
fn parent_before_transform(&self, element: &BaseTransform, inbuf: &gst::BufferRef);
fn parent_submit_input_buffer(
&self,
element: &BaseTransform,
is_discont: bool,
inbuf: gst::Buffer,
) -> Result<gst::FlowSuccess, gst::FlowError>;
fn parent_generate_output(
&self,
element: &BaseTransform,
) -> Result<GeneratedOutput, gst::FlowError>;
fn take_queued_buffer(&self) -> Option<gst::Buffer>
where
Self: ObjectSubclass,
<Self as ObjectSubclass>::ParentType: IsA<BaseTransform>;
fn get_queued_buffer(&self) -> Option<gst::Buffer>
where
Self: ObjectSubclass,
<Self as ObjectSubclass>::ParentType: IsA<BaseTransform>;
}
impl<T: BaseTransformImpl + ObjectImpl> BaseTransformImplExt for T {
@ -458,6 +548,41 @@ impl<T: BaseTransformImpl + ObjectImpl> BaseTransformImplExt for T {
}
}
fn parent_prepare_output_buffer(
&self,
element: &BaseTransform,
inbuf: &gst::BufferRef,
) -> Result<PreparedOutputBuffer, gst::FlowError> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_base_sys::GstBaseTransformClass;
(*parent_class)
.prepare_output_buffer
.map(|f| {
let mut outbuf: *mut gst_sys::GstBuffer = ptr::null_mut();
// FIXME: Wrong signature in FFI
let res = from_glib(f(
element.to_glib_none().0,
inbuf.as_ptr() as *mut gst_sys::GstBuffer,
(&mut outbuf) as *mut *mut gst_sys::GstBuffer as *mut gst_sys::GstBuffer,
));
match gst::FlowReturn::into_result(res) {
Err(err) => Err(err),
Ok(_) => {
if outbuf == inbuf.as_ptr() as *mut _ {
Ok(PreparedOutputBuffer::InputBuffer)
} else {
Ok(PreparedOutputBuffer::Buffer(from_glib_full(outbuf)))
}
}
}
})
.unwrap_or(Err(gst::FlowError::NotSupported))
}
}
fn parent_transform(
&self,
element: &BaseTransform,
@ -547,6 +672,149 @@ impl<T: BaseTransformImpl + ObjectImpl> BaseTransformImplExt for T {
gst::FlowReturn::from_glib(f(element.to_glib_none().0, buf as *mut _)).into_result()
}
}
fn parent_copy_metadata(
&self,
element: &BaseTransform,
inbuf: &gst::BufferRef,
outbuf: &mut gst::BufferRef,
) -> Result<(), gst::LoggableError> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_base_sys::GstBaseTransformClass;
if let Some(ref f) = (*parent_class).copy_metadata {
gst_result_from_gboolean!(
f(
element.to_glib_none().0,
inbuf.as_ptr() as *mut _,
outbuf.as_mut_ptr()
),
gst::CAT_RUST,
"Parent function `copy_metadata` failed"
)
} else {
Ok(())
}
}
}
fn parent_transform_meta<'a>(
&self,
element: &BaseTransform,
outbuf: &mut gst::BufferRef,
meta: gst::MetaRef<'a, gst::Meta>,
inbuf: &'a gst::BufferRef,
) -> bool {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_base_sys::GstBaseTransformClass;
(*parent_class)
.transform_meta
.map(|f| {
from_glib(f(
element.to_glib_none().0,
outbuf.as_mut_ptr(),
meta.as_ptr() as *mut _,
inbuf.as_ptr() as *mut _,
))
})
.unwrap_or(false)
}
}
fn parent_before_transform(&self, element: &BaseTransform, inbuf: &gst::BufferRef) {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_base_sys::GstBaseTransformClass;
if let Some(ref f) = (*parent_class).before_transform {
f(element.to_glib_none().0, inbuf.as_ptr() as *mut _);
}
}
}
fn parent_submit_input_buffer(
&self,
element: &BaseTransform,
is_discont: bool,
inbuf: gst::Buffer,
) -> Result<gst::FlowSuccess, gst::FlowError> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_base_sys::GstBaseTransformClass;
let f = (*parent_class)
.submit_input_buffer
.expect("Missing parent function `submit_input_buffer`");
gst::FlowReturn::from_glib(f(
element.to_glib_none().0,
is_discont.to_glib(),
inbuf.into_ptr(),
))
.into_result()
}
}
fn parent_generate_output(
&self,
element: &BaseTransform,
) -> Result<GeneratedOutput, gst::FlowError> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_base_sys::GstBaseTransformClass;
let f = (*parent_class)
.generate_output
.expect("Missing parent function `generate_output`");
let mut outbuf = ptr::null_mut();
gst::FlowReturn::from_glib(f(element.to_glib_none().0, &mut outbuf))
.into_result()
.map(|res| {
if res == ::BASE_TRANSFORM_FLOW_DROPPED {
GeneratedOutput::Dropped
} else if res != gst::FlowSuccess::Ok || outbuf.is_null() {
GeneratedOutput::NoOutput
} else {
GeneratedOutput::Buffer(from_glib_full(outbuf))
}
})
}
}
fn take_queued_buffer(&self) -> Option<gst::Buffer>
where
Self: ObjectSubclass,
<Self as ObjectSubclass>::ParentType: IsA<BaseTransform>,
{
unsafe {
let element = self.get_instance();
let ptr: *mut gst_base_sys::GstBaseTransform = element.to_glib_none().0 as *mut _;
let sinkpad: gst::Pad = from_glib_borrow((*ptr).sinkpad);
let _stream_lock = sinkpad.stream_lock();
let buffer = (*ptr).queued_buf;
(*ptr).queued_buf = ptr::null_mut();
from_glib_full(buffer)
}
}
fn get_queued_buffer(&self) -> Option<gst::Buffer>
where
Self: ObjectSubclass,
<Self as ObjectSubclass>::ParentType: IsA<BaseTransform>,
{
unsafe {
let element = self.get_instance();
let ptr: *mut gst_base_sys::GstBaseTransform = element.to_glib_none().0 as *mut _;
let sinkpad: gst::Pad = from_glib_borrow((*ptr).sinkpad);
let _stream_lock = sinkpad.stream_lock();
let buffer = (*ptr).queued_buf;
from_glib_none(buffer)
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@ -573,8 +841,14 @@ where
klass.query = Some(base_transform_query::<T>);
klass.transform_size = Some(base_transform_transform_size::<T>);
klass.get_unit_size = Some(base_transform_get_unit_size::<T>);
klass.prepare_output_buffer = Some(base_transform_prepare_output_buffer::<T>);
klass.sink_event = Some(base_transform_sink_event::<T>);
klass.src_event = Some(base_transform_src_event::<T>);
klass.transform_meta = Some(base_transform_transform_meta::<T>);
klass.copy_metadata = Some(base_transform_copy_metadata::<T>);
klass.before_transform = Some(base_transform_before_transform::<T>);
klass.submit_input_buffer = Some(base_transform_submit_input_buffer::<T>);
klass.generate_output = Some(base_transform_generate_output::<T>);
}
}
}
@ -618,6 +892,19 @@ where
{
}
#[derive(Debug)]
pub enum GeneratedOutput {
Buffer(gst::Buffer),
NoOutput,
Dropped,
}
#[derive(Debug)]
pub enum PreparedOutputBuffer {
Buffer(gst::Buffer),
InputBuffer,
}
unsafe extern "C" fn base_transform_start<T: ObjectSubclass>(
ptr: *mut gst_base_sys::GstBaseTransform,
) -> glib_sys::gboolean
@ -848,6 +1135,38 @@ where
.to_glib()
}
unsafe extern "C" fn base_transform_prepare_output_buffer<T: ObjectSubclass>(
ptr: *mut gst_base_sys::GstBaseTransform,
inbuf: *mut gst_sys::GstBuffer,
outbuf: *mut gst_sys::GstBuffer,
) -> gst_sys::GstFlowReturn
where
T: BaseTransformImpl,
T::Instance: PanicPoison,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: BaseTransform = from_glib_borrow(ptr);
// FIXME: Wrong signature in FFI
let outbuf = outbuf as *mut *mut gst_sys::GstBuffer;
gst_panic_to_error!(&wrap, &instance.panicked(), gst::FlowReturn::Error, {
match imp.prepare_output_buffer(&wrap, gst::BufferRef::from_ptr(inbuf)) {
Ok(PreparedOutputBuffer::InputBuffer) => {
*outbuf = inbuf;
gst::FlowReturn::Ok
}
Ok(PreparedOutputBuffer::Buffer(buf)) => {
*outbuf = buf.into_ptr();
gst::FlowReturn::Ok
}
Err(err) => err.into(),
}
})
.to_glib()
}
unsafe extern "C" fn base_transform_sink_event<T: ObjectSubclass>(
ptr: *mut gst_base_sys::GstBaseTransform,
event: *mut gst_sys::GstEvent,
@ -934,3 +1253,133 @@ where
})
.to_glib()
}
unsafe extern "C" fn base_transform_transform_meta<T: ObjectSubclass>(
ptr: *mut gst_base_sys::GstBaseTransform,
outbuf: *mut gst_sys::GstBuffer,
meta: *mut gst_sys::GstMeta,
inbuf: *mut gst_sys::GstBuffer,
) -> glib_sys::gboolean
where
T: BaseTransformImpl,
T::Instance: PanicPoison,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: BaseTransform = from_glib_borrow(ptr);
let inbuf = gst::BufferRef::from_ptr(inbuf);
gst_panic_to_error!(&wrap, &instance.panicked(), false, {
imp.transform_meta(
&wrap,
gst::BufferRef::from_mut_ptr(outbuf),
gst::Meta::from_ptr(inbuf, meta),
inbuf,
)
})
.to_glib()
}
unsafe extern "C" fn base_transform_copy_metadata<T: ObjectSubclass>(
ptr: *mut gst_base_sys::GstBaseTransform,
inbuf: *mut gst_sys::GstBuffer,
outbuf: *mut gst_sys::GstBuffer,
) -> glib_sys::gboolean
where
T: BaseTransformImpl,
T::Instance: PanicPoison,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: BaseTransform = from_glib_borrow(ptr);
if gst_sys::gst_mini_object_is_writable(outbuf as *mut _) == glib_sys::GFALSE {
gst_warning!(
gst::CAT_RUST,
obj: &wrap,
"buffer {:?} not writable",
outbuf
);
return glib_sys::GFALSE;
}
gst_panic_to_error!(&wrap, &instance.panicked(), true, {
match imp.copy_metadata(
&wrap,
gst::BufferRef::from_ptr(inbuf),
gst::BufferRef::from_mut_ptr(outbuf),
) {
Ok(_) => true,
Err(err) => {
err.log_with_object(&wrap);
false
}
}
})
.to_glib()
}
unsafe extern "C" fn base_transform_before_transform<T: ObjectSubclass>(
ptr: *mut gst_base_sys::GstBaseTransform,
inbuf: *mut gst_sys::GstBuffer,
) where
T: BaseTransformImpl,
T::Instance: PanicPoison,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: BaseTransform = from_glib_borrow(ptr);
gst_panic_to_error!(&wrap, &instance.panicked(), (), {
imp.before_transform(&wrap, gst::BufferRef::from_ptr(inbuf));
})
}
unsafe extern "C" fn base_transform_submit_input_buffer<T: ObjectSubclass>(
ptr: *mut gst_base_sys::GstBaseTransform,
is_discont: glib_sys::gboolean,
buf: *mut gst_sys::GstBuffer,
) -> gst_sys::GstFlowReturn
where
T: BaseTransformImpl,
T::Instance: PanicPoison,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: BaseTransform = from_glib_borrow(ptr);
gst_panic_to_error!(&wrap, &instance.panicked(), gst::FlowReturn::Error, {
imp.submit_input_buffer(&wrap, from_glib(is_discont), from_glib_full(buf))
.into()
})
.to_glib()
}
unsafe extern "C" fn base_transform_generate_output<T: ObjectSubclass>(
ptr: *mut gst_base_sys::GstBaseTransform,
buf: *mut *mut gst_sys::GstBuffer,
) -> gst_sys::GstFlowReturn
where
T: BaseTransformImpl,
T::Instance: PanicPoison,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: BaseTransform = from_glib_borrow(ptr);
*buf = ptr::null_mut();
gst_panic_to_error!(&wrap, &instance.panicked(), gst::FlowReturn::Error, {
match imp.generate_output(&wrap) {
Ok(GeneratedOutput::Dropped) => ::BASE_TRANSFORM_FLOW_DROPPED.into(),
Ok(GeneratedOutput::NoOutput) => gst::FlowReturn::Ok,
Ok(GeneratedOutput::Buffer(outbuf)) => {
*buf = outbuf.into_ptr();
gst::FlowReturn::Ok
}
Err(err) => err.into(),
}
})
.to_glib()
}

View file

@ -9,6 +9,7 @@
use glib::translate::mut_override;
use glib_sys;
#[must_use = "if unused the Mutex will immediately unlock"]
pub struct MutexGuard<'a>(&'a glib_sys::GMutex);
impl<'a> MutexGuard<'a> {

View file

@ -5,6 +5,75 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +685,11 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...HEAD
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-check"
version = "0.15.0"
version = "0.15.4"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer Check library"
@ -14,12 +14,12 @@ build = "build.rs"
[dependencies]
bitflags = "1.0"
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-check-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
glib = { git = "https://github.com/gtk-rs/glib" }
gstreamer = { path = "../gstreamer" }
glib-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-check-sys = { version = "0.8", features = ["v1_8"] }
glib = { version = "0.9" }
gstreamer = { version = "0.15", path = "../gstreamer" }
[build-dependencies]
rustdoc-stripper = { version = "0.1", optional = true }

View file

@ -730,7 +730,7 @@ impl Harness {
None
} else {
Some(Ref(
Some(Harness(
mem::ManuallyDrop::new(Harness(
ptr::NonNull::new_unchecked(sink_harness),
PhantomData,
)),
@ -747,7 +747,7 @@ impl Harness {
None
} else {
Some(Ref(
Some(Harness(
mem::ManuallyDrop::new(Harness(
ptr::NonNull::new_unchecked(src_harness),
PhantomData,
)),
@ -764,7 +764,7 @@ impl Harness {
None
} else {
Some(RefMut(
Some(Harness(
mem::ManuallyDrop::new(Harness(
ptr::NonNull::new_unchecked(sink_harness),
PhantomData,
)),
@ -781,7 +781,7 @@ impl Harness {
None
} else {
Some(RefMut(
Some(Harness(
mem::ManuallyDrop::new(Harness(
ptr::NonNull::new_unchecked(src_harness),
PhantomData,
)),
@ -793,44 +793,30 @@ impl Harness {
}
#[derive(Debug)]
pub struct Ref<'a>(Option<Harness>, PhantomData<&'a Harness>);
pub struct Ref<'a>(mem::ManuallyDrop<Harness>, PhantomData<&'a Harness>);
impl<'a> ops::Deref for Ref<'a> {
type Target = Harness;
fn deref(&self) -> &Harness {
self.0.as_ref().unwrap()
}
}
impl<'a> Drop for Ref<'a> {
fn drop(&mut self) {
// We only really borrow
mem::forget(self.0.take())
&*self.0
}
}
#[derive(Debug)]
pub struct RefMut<'a>(Option<Harness>, PhantomData<&'a mut Harness>);
pub struct RefMut<'a>(mem::ManuallyDrop<Harness>, PhantomData<&'a mut Harness>);
impl<'a> ops::Deref for RefMut<'a> {
type Target = Harness;
fn deref(&self) -> &Harness {
self.0.as_ref().unwrap()
&*self.0
}
}
impl<'a> ops::DerefMut for RefMut<'a> {
fn deref_mut(&mut self) -> &mut Harness {
self.0.as_mut().unwrap()
}
}
impl<'a> Drop for RefMut<'a> {
fn drop(&mut self) {
// We only really borrow
mem::forget(self.0.take())
&mut *self.0
}
}

View file

@ -5,6 +5,75 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +685,11 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...HEAD
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-editing-services"
version = "0.15.0"
version = "0.15.4"
authors = ["Thibault Saunier <tsaunier@igalia.com>", "Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer Editing Services"
@ -15,16 +15,16 @@ build = "build.rs"
[dependencies]
libc = "0.2"
bitflags = "1.0"
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gio-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-editing-services-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"]}
glib = { git = "https://github.com/gtk-rs/glib" }
gio = { git = "https://github.com/gtk-rs/gio" }
gstreamer = { path = "../gstreamer" }
gstreamer-base = { path = "../gstreamer-base" }
gstreamer-pbutils = { path = "../gstreamer-pbutils" }
glib-sys = { version = "0.9" }
gio-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-editing-services-sys = { version = "0.8", features = ["v1_8"]}
glib = { version = "0.9" }
gio = { version = "0.8" }
gstreamer = { version = "0.15", path = "../gstreamer" }
gstreamer-base = { version = "0.15", path = "../gstreamer-base" }
gstreamer-pbutils = { version = "0.15", path = "../gstreamer-pbutils" }
[build-dependencies]
rustdoc-stripper = { version = "0.1", optional = true }

View file

@ -5,6 +5,75 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +685,11 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...HEAD
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-gl"
version = "0.15.0"
version = "0.15.4"
authors = ["Sebastian Dröge <sebastian@centricular.com>",
"Víctor M. Jáquez L. <vjaquez@igalia.com>"]
categories = ["api-bindings", "multimedia"]
@ -18,15 +18,15 @@ bitflags = "1.0"
byteorder = "1"
libc = "0.2"
lazy_static = "1.0"
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_14"] }
gstreamer-video-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_14"] }
gstreamer-gl-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys" }
glib = { git = "https://github.com/gtk-rs/glib" }
gstreamer = { path = "../gstreamer", features = ["v1_14"] }
gstreamer-base = { path = "../gstreamer-base", features = ["v1_14"] }
gstreamer-video = { path = "../gstreamer-video", features = ["v1_14"] }
glib-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_14"] }
gstreamer-video-sys = { version = "0.8", features = ["v1_14"] }
gstreamer-gl-sys = { version = "0.8" }
glib = { version = "0.9" }
gstreamer = { version = "0.15", path = "../gstreamer", features = ["v1_14"] }
gstreamer-base = { version = "0.15", path = "../gstreamer-base", features = ["v1_14"] }
gstreamer-video = { version = "0.15", path = "../gstreamer-video", features = ["v1_14"] }
[build-dependencies]
rustdoc-stripper = { version = "0.1", optional = true }

View file

@ -5,6 +5,75 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +685,11 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...HEAD
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-net"
version = "0.15.0"
version = "0.15.4"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer Net library"
@ -13,13 +13,13 @@ keywords = ["gstreamer", "multimedia", "audio", "video", "gnome"]
build = "build.rs"
[dependencies]
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-net-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
glib = { git = "https://github.com/gtk-rs/glib" }
gstreamer = { path = "../gstreamer" }
gio = { git = "https://github.com/gtk-rs/gio" }
glib-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-net-sys = { version = "0.8", features = ["v1_8"] }
glib = { version = "0.9" }
gstreamer = { version = "0.15", path = "../gstreamer" }
gio = { version = "0.8" }
[build-dependencies]
rustdoc-stripper = { version = "0.1", optional = true }

View file

@ -5,6 +5,75 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +685,11 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...HEAD
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-pbutils"
version = "0.15.0"
version = "0.15.4"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer Base Utils library"
@ -15,12 +15,12 @@ build = "build.rs"
[dependencies]
bitflags = "1.0"
libc = "0.2"
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-pbutils-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
glib = { git = "https://github.com/gtk-rs/glib" }
gstreamer = { path = "../gstreamer" }
glib-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-pbutils-sys = { version = "0.8", features = ["v1_8"] }
glib = { version = "0.9" }
gstreamer = { version = "0.15", path = "../gstreamer" }
[build-dependencies]
rustdoc-stripper = { version = "0.1", optional = true }

View file

@ -488,19 +488,19 @@ mod tests {
use auto::EncodingVideoProfile;
use gst;
const AUDIO_PROFILE_NAME: &'static str = "audio-profile";
const AUDIO_PROFILE_DESCRIPTION: &'static str = "audio-profile-description";
const PRESET: &'static str = "preset";
const PRESET_NAME: &'static str = "preset-name";
const AUDIO_PROFILE_NAME: &str = "audio-profile";
const AUDIO_PROFILE_DESCRIPTION: &str = "audio-profile-description";
const PRESET: &str = "preset";
const PRESET_NAME: &str = "preset-name";
const PRESENCE: u32 = 5;
const ALLOW_DYNAMIC_OUTPUT: bool = false;
const ENABLED: bool = false;
const VIDEO_PROFILE_NAME: &'static str = "video-profile";
const VIDEO_PROFILE_DESCRIPTION: &'static str = "video-profile-description";
const VIDEO_PROFILE_NAME: &str = "video-profile";
const VIDEO_PROFILE_DESCRIPTION: &str = "video-profile-description";
const CONTAINER_PROFILE_NAME: &'static str = "container-profile";
const CONTAINER_PROFILE_DESCRIPTION: &'static str = "container-profile-description";
const CONTAINER_PROFILE_NAME: &str = "container-profile";
const CONTAINER_PROFILE_DESCRIPTION: &str = "container-profile-description";
// Video profile exclusive attributes
const PASS: u32 = 8;

View file

@ -5,6 +5,75 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +685,11 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...HEAD
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-player"
version = "0.15.0"
version = "0.15.4"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer Player library"
@ -15,13 +15,13 @@ build = "build.rs"
[dependencies]
bitflags = "1.0"
libc = "0.2"
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_12"] }
gstreamer-player-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys" }
glib = { git = "https://github.com/gtk-rs/glib" }
gstreamer = { path = "../gstreamer", features = ["v1_12"] }
gstreamer-video = { path = "../gstreamer-video", features = ["v1_12"] }
glib-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_12"] }
gstreamer-player-sys = { version = "0.8" }
glib = { version = "0.9" }
gstreamer = { version = "0.15", path = "../gstreamer", features = ["v1_12"] }
gstreamer-video = { version = "0.15", path = "../gstreamer-video", features = ["v1_12"] }
[build-dependencies]
rustdoc-stripper = { version = "0.1", optional = true }

View file

@ -14,7 +14,7 @@ use gst_sys;
use std::mem;
use std::ops;
#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PlayerConfig(gst::Structure);
impl ops::Deref for PlayerConfig {

View file

@ -5,6 +5,75 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +685,11 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...HEAD
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-rtp"
version = "0.15.0"
version = "0.15.4"
authors = ["Mathieu Duponchelle <mathieu@centricular.com>", "Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer Rtp library"
@ -15,12 +15,12 @@ build = "build.rs"
[dependencies]
bitflags = "1.0"
lazy_static = "1.0"
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-rtp-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
glib = { git = "https://github.com/gtk-rs/glib" }
gstreamer = { path = "../gstreamer" }
glib-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-rtp-sys = { version = "0.8", features = ["v1_8"] }
glib = { version = "0.9" }
gstreamer = { version = "0.15", path = "../gstreamer" }
[build-dependencies.rustdoc-stripper]
version = "0.1"

196
gstreamer-rtp/README.md Normal file
View file

@ -0,0 +1,196 @@
# gstreamer-rs [![crates.io](https://img.shields.io/crates/v/gstreamer-rtp.svg)](https://crates.io/crates/gstreamer-rtp) [![pipeline status](https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/badges/master/pipeline.svg)](https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/commits/master)
[GStreamer](https://gstreamer.freedesktop.org/) bindings for Rust.
Documentation can be found [here](https://slomo.pages.freedesktop.org/rustdocs/gstreamer/gstreamer_rtp/).
These bindings are providing a safe API that can be used to interface with
GStreamer, e.g. for writing GStreamer-based applications and GStreamer plugins.
The bindings are mostly autogenerated with [gir](https://github.com/gtk-rs/gir/)
based on the [GObject-Introspection](https://wiki.gnome.org/Projects/GObjectIntrospection/)
API metadata provided by the GStreamer project.
## 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 libgstrtspserver-1.0-dev
```
The minimum required version of the above libraries is >= 1.8. If you
build the gstreamer-player sub-crate, or any of the examples that
depend on gstreamer-player, you must ensure that in addition to the
above packages, `libgstreamer-plugins-bad1.0-dev` is installed and
that the version is >= 1.12. See the `Cargo.toml` files for the full
details,
```
# Only if you wish to install gstreamer-player, make sure the version
# of this package is >= 1.12.
$ apt-get install libgstreamer-plugins-bad1.0-dev
```
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
Homebrew only installs various plugins if explicitly enabled, so some extra
`--with-*` flags may be required.
```
$ brew install gstreamer gst-plugins-base gst-plugins-good \
gst-plugins-bad gst-plugins-ugly gst-libav gst-rtsp-server \
gst-editing-services --with-orc --with-libogg --with-opus \
--with-pango --with-theora --with-libvorbis --with-libvpx \
--enable-gtk3
```
If you wish to install the gstreamer-player sub-crate, make sure the
version of these libraries is >= 1.12. Otherwise, a version >= 1.8 is
sufficient.
#### 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 \
mingw-w64-x86_64-gst-rtsp-server
```
If you wish to install the gstreamer-player sub-crate, make sure the
version of these libraries is >= 1.12. Otherwise, a version >= 1.8 is
sufficient.
#### 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://slomo.pages.freedesktop.org/rustdocs/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://gitlab.freedesktop.org/gstreamer/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://gitlab.freedesktop.org/gstreamer/gstreamer-rs/tree/master/examples)
directory.
Various GStreamer plugins written in Rust can be found in the
[gst-plugins-rs](https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs)
repository.
<a name="license"/>
## LICENSE
gstreamer-rs and all crates contained in here are licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
http://opensource.org/licenses/MIT)
at your option.
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.
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in gstreamer-rs by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.

View file

@ -1,4 +1,5 @@
use glib::translate::{from_glib, FromGlibPtrFull};
use std::fmt;
use std::marker::PhantomData;
use std::mem;
@ -8,12 +9,25 @@ use gst_rtp_sys;
pub enum Readable {}
pub enum Writable {}
#[repr(C)]
pub struct RTPBuffer<'a, T>(gst_rtp_sys::GstRTPBuffer, &'a gst::Buffer, PhantomData<T>);
pub struct RTPBuffer<'a, T> {
rtp_buffer: gst_rtp_sys::GstRTPBuffer,
buffer: &'a gst::Buffer,
phantom: PhantomData<T>,
}
unsafe impl<'a, T> Send for RTPBuffer<'a, T> {}
unsafe impl<'a, T> Sync for RTPBuffer<'a, T> {}
impl<'a, T> fmt::Debug for RTPBuffer<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("RTPBuffer")
.field("rtp_buffer", &self.rtp_buffer)
.field("buffer", &self.buffer)
.field("phantom", &self.phantom)
.finish()
}
}
impl<'a> RTPBuffer<'a, Readable> {
pub fn from_buffer_readable(
buffer: &gst::Buffer,
@ -27,7 +41,11 @@ impl<'a> RTPBuffer<'a, Readable> {
));
if res {
Ok(RTPBuffer(rtp_buffer.assume_init(), buffer, PhantomData))
Ok(RTPBuffer {
rtp_buffer: rtp_buffer.assume_init(),
buffer,
phantom: PhantomData,
})
} else {
Err(glib_bool_error!("Failed to map RTP buffer readable"))
}
@ -48,7 +66,11 @@ impl<'a> RTPBuffer<'a, Writable> {
));
if res {
Ok(RTPBuffer(rtp_buffer.assume_init(), buffer, PhantomData))
Ok(RTPBuffer {
rtp_buffer: rtp_buffer.assume_init(),
buffer,
phantom: PhantomData,
})
} else {
Err(glib_bool_error!("Failed to map RTP buffer writable"))
}
@ -57,41 +79,41 @@ impl<'a> RTPBuffer<'a, Writable> {
pub fn set_seq(&mut self, seq: u16) {
unsafe {
gst_rtp_sys::gst_rtp_buffer_set_seq(&mut self.0, seq);
gst_rtp_sys::gst_rtp_buffer_set_seq(&mut self.rtp_buffer, seq);
}
}
pub fn set_payload_type(&mut self, pt: u8) {
unsafe {
gst_rtp_sys::gst_rtp_buffer_set_payload_type(&mut self.0, pt);
gst_rtp_sys::gst_rtp_buffer_set_payload_type(&mut self.rtp_buffer, pt);
}
}
pub fn set_timestamp(&mut self, rtptime: u32) {
unsafe {
gst_rtp_sys::gst_rtp_buffer_set_timestamp(&mut self.0, rtptime);
gst_rtp_sys::gst_rtp_buffer_set_timestamp(&mut self.rtp_buffer, rtptime);
}
}
}
impl<'a, T> RTPBuffer<'a, T> {
pub fn get_seq(&mut self) -> u16 {
unsafe { gst_rtp_sys::gst_rtp_buffer_get_seq(&mut self.0) }
unsafe { gst_rtp_sys::gst_rtp_buffer_get_seq(&mut self.rtp_buffer) }
}
pub fn get_payload_type(&mut self) -> u8 {
unsafe { gst_rtp_sys::gst_rtp_buffer_get_payload_type(&mut self.0) }
unsafe { gst_rtp_sys::gst_rtp_buffer_get_payload_type(&mut self.rtp_buffer) }
}
pub fn get_timestamp(&mut self) -> u32 {
unsafe { gst_rtp_sys::gst_rtp_buffer_get_timestamp(&mut self.0) }
unsafe { gst_rtp_sys::gst_rtp_buffer_get_timestamp(&mut self.rtp_buffer) }
}
}
impl<'a, T> Drop for RTPBuffer<'a, T> {
fn drop(&mut self) {
unsafe {
gst_rtp_sys::gst_rtp_buffer_unmap(&mut self.0);
gst_rtp_sys::gst_rtp_buffer_unmap(&mut self.rtp_buffer);
}
}
}

View file

@ -5,6 +5,75 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +685,11 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...HEAD
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-rtsp-server"
version = "0.15.0"
version = "0.15.4"
authors = ["Mathieu Duponchelle <mathieu@centricular.com>", "Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer RTSP Server library"
@ -16,18 +16,20 @@ build = "build.rs"
bitflags = "1.0"
libc = "0.2"
lazy_static = "1.0"
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gio-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-rtsp-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-rtsp-server-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-net-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
glib = { git = "https://github.com/gtk-rs/glib" }
gio = { git = "https://github.com/gtk-rs/gio" }
gstreamer = { path = "../gstreamer" }
gstreamer-rtsp = { path = "../gstreamer-rtsp" }
gstreamer-net = { path = "../gstreamer-net" }
glib-sys = { version = "0.9" }
gio-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-sdp-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-rtsp-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-rtsp-server-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-net-sys = { version = "0.8", features = ["v1_8"] }
glib = { version = "0.9" }
gio = { version = "0.8" }
gstreamer = { version = "0.15", path = "../gstreamer" }
gstreamer-sdp = { version = "0.15", path = "../gstreamer-sdp" }
gstreamer-rtsp = { version = "0.15", path = "../gstreamer-rtsp" }
gstreamer-net = { version = "0.15", path = "../gstreamer-net" }
[build-dependencies]
rustdoc-stripper = { version = "0.1", optional = true }

View file

@ -24,6 +24,7 @@ use RTSPMediaStatus;
use RTSPPublishClockMode;
use RTSPStream;
use RTSPSuspendMode;
use RTSPThread;
use RTSPTransportMode;
glib_wrapper! {
@ -123,7 +124,7 @@ pub trait RTSPMediaExt: 'static {
fn n_streams(&self) -> u32;
//fn prepare(&self, thread: /*Ignored*/Option<&mut RTSPThread>) -> bool;
fn prepare(&self, thread: Option<&RTSPThread>) -> Result<(), glib::error::BoolError>;
//fn seek(&self, range: /*Ignored*/&mut gst_rtsp::RTSPTimeRange) -> bool;
@ -182,8 +183,6 @@ pub trait RTSPMediaExt: 'static {
fn suspend(&self) -> Result<(), glib::error::BoolError>;
fn take_pipeline<P: IsA<gst::Pipeline>>(&self, pipeline: &P);
fn unprepare(&self) -> Result<(), glib::error::BoolError>;
fn unsuspend(&self) -> Result<(), glib::error::BoolError>;
@ -542,9 +541,17 @@ impl<O: IsA<RTSPMedia>> RTSPMediaExt for O {
unsafe { gst_rtsp_server_sys::gst_rtsp_media_n_streams(self.as_ref().to_glib_none().0) }
}
//fn prepare(&self, thread: /*Ignored*/Option<&mut RTSPThread>) -> bool {
// unsafe { TODO: call gst_rtsp_server_sys:gst_rtsp_media_prepare() }
//}
fn prepare(&self, thread: Option<&RTSPThread>) -> Result<(), glib::error::BoolError> {
unsafe {
glib_result_from_gboolean!(
gst_rtsp_server_sys::gst_rtsp_media_prepare(
self.as_ref().to_glib_none().0,
thread.to_glib_full()
),
"Failed to prepare media"
)
}
}
//fn seek(&self, range: /*Ignored*/&mut gst_rtsp::RTSPTimeRange) -> bool {
// unsafe { TODO: call gst_rtsp_server_sys:gst_rtsp_media_seek() }
@ -755,15 +762,6 @@ impl<O: IsA<RTSPMedia>> RTSPMediaExt for O {
}
}
fn take_pipeline<P: IsA<gst::Pipeline>>(&self, pipeline: &P) {
unsafe {
gst_rtsp_server_sys::gst_rtsp_media_take_pipeline(
self.as_ref().to_glib_none().0,
pipeline.as_ref().to_glib_full(),
);
}
}
fn unprepare(&self) -> Result<(), glib::error::BoolError> {
unsafe {
glib_result_from_gboolean!(

View file

@ -11,6 +11,9 @@ use glib_sys;
use gst_rtsp_server_sys;
use std::boxed::Box as Box_;
use std::mem::transmute;
use RTSPContext;
use RTSPThread;
use RTSPThreadType;
glib_wrapper! {
pub struct RTSPThreadPool(Object<gst_rtsp_server_sys::GstRTSPThreadPool, gst_rtsp_server_sys::GstRTSPThreadPoolClass, RTSPThreadPoolClass>);
@ -48,7 +51,7 @@ pub const NONE_RTSP_THREAD_POOL: Option<&RTSPThreadPool> = None;
pub trait RTSPThreadPoolExt: 'static {
fn get_max_threads(&self) -> i32;
//fn get_thread(&self, type_: RTSPThreadType, ctx: &RTSPContext) -> /*Ignored*/Option<RTSPThread>;
fn get_thread(&self, type_: RTSPThreadType, ctx: &RTSPContext) -> Option<RTSPThread>;
fn set_max_threads(&self, max_threads: i32);
@ -67,9 +70,15 @@ impl<O: IsA<RTSPThreadPool>> RTSPThreadPoolExt for O {
}
}
//fn get_thread(&self, type_: RTSPThreadType, ctx: &RTSPContext) -> /*Ignored*/Option<RTSPThread> {
// unsafe { TODO: call gst_rtsp_server_sys:gst_rtsp_thread_pool_get_thread() }
//}
fn get_thread(&self, type_: RTSPThreadType, ctx: &RTSPContext) -> Option<RTSPThread> {
unsafe {
from_glib_full(gst_rtsp_server_sys::gst_rtsp_thread_pool_get_thread(
self.as_ref().to_glib_none().0,
type_.to_glib(),
ctx.to_glib_none().0,
))
}
}
fn set_max_threads(&self, max_threads: i32) {
unsafe {

View file

@ -26,6 +26,8 @@ extern crate gstreamer_net_sys as gst_net_sys;
extern crate gstreamer_rtsp as gst_rtsp;
extern crate gstreamer_rtsp_server_sys as gst_rtsp_server_sys;
extern crate gstreamer_rtsp_sys as gst_rtsp_sys;
extern crate gstreamer_sdp as gst_sdp;
extern crate gstreamer_sdp_sys as gst_sdp_sys;
extern crate gstreamer_sys as gst_sys;
macro_rules! assert_initialized_main_thread {
@ -52,21 +54,27 @@ mod rtsp_address_pool;
mod rtsp_auth;
mod rtsp_client;
mod rtsp_context;
mod rtsp_media;
mod rtsp_media_factory;
mod rtsp_server;
mod rtsp_session_pool;
mod rtsp_stream;
mod rtsp_stream_transport;
mod rtsp_thread;
mod rtsp_token;
pub mod subclass;
pub use rtsp_address_pool::RTSPAddressPoolExtManual;
pub use rtsp_auth::RTSPAuthExtManual;
pub use rtsp_client::RTSPClientExtManual;
pub use rtsp_media::RTSPMediaExtManual;
pub use rtsp_media_factory::RTSPMediaFactoryExtManual;
pub use rtsp_server::RTSPServerExtManual;
pub use rtsp_session_pool::RTSPSessionPoolExtManual;
pub use rtsp_stream::RTSPStreamExtManual;
pub use rtsp_stream_transport::RTSPStreamTransportExtManual;
pub use rtsp_thread::*;
pub use rtsp_context::*;
pub use rtsp_token::*;
@ -140,6 +148,7 @@ pub mod prelude {
pub use rtsp_address_pool::RTSPAddressPoolExtManual;
pub use rtsp_auth::RTSPAuthExtManual;
pub use rtsp_client::RTSPClientExtManual;
pub use rtsp_media::RTSPMediaExtManual;
pub use rtsp_media_factory::RTSPMediaFactoryExtManual;
pub use rtsp_server::RTSPServerExtManual;
pub use rtsp_session_pool::RTSPSessionPoolExtManual;

View file

@ -37,3 +37,18 @@ impl glib::translate::FromGlibPtrBorrow<*mut gst_rtsp_server_sys::GstRTSPContext
RTSPContext(ptr::NonNull::new_unchecked(ptr))
}
}
#[doc(hidden)]
impl<'a> glib::translate::ToGlibPtr<'a, *mut gst_rtsp_server_sys::GstRTSPContext> for RTSPContext {
type Storage = &'a RTSPContext;
fn to_glib_none(
&'a self,
) -> glib::translate::Stash<'a, *mut gst_rtsp_server_sys::GstRTSPContext, Self> {
glib::translate::Stash(self.0.as_ptr(), self)
}
fn to_glib_full(&self) -> *mut gst_rtsp_server_sys::GstRTSPContext {
unimplemented!()
}
}

View file

@ -0,0 +1,27 @@
use glib::object::IsA;
use glib::translate::*;
use gst;
use gst_rtsp_server_sys;
use RTSPMedia;
pub trait RTSPMediaExtManual: 'static {
fn take_pipeline<P: IsA<gst::Pipeline>>(&self, pipeline: &P);
}
impl<O: IsA<RTSPMedia>> RTSPMediaExtManual for O {
fn take_pipeline<P: IsA<gst::Pipeline>>(&self, pipeline: &P) {
unsafe {
let pipeline = pipeline.as_ref().to_glib_full();
// See https://gitlab.freedesktop.org/gstreamer/gst-rtsp-server/merge_requests/109
gobject_sys::g_object_force_floating(pipeline as *mut _);
gst_rtsp_server_sys::gst_rtsp_media_take_pipeline(
self.as_ref().to_glib_none().0,
pipeline,
);
if gobject_sys::g_object_is_floating(pipeline as *mut _) != glib_sys::GFALSE {
gobject_sys::g_object_ref_sink(pipeline as *mut _);
}
}
}
}

View file

@ -0,0 +1,47 @@
use glib;
use glib::translate::*;
use gst_rtsp_server_sys;
use gst::prelude::*;
gst_define_mini_object_wrapper!(
RTSPThread,
RTSPThreadRef,
gst_rtsp_server_sys::GstRTSPThread,
[],
|| gst_rtsp_server_sys::gst_rtsp_thread_get_type()
);
impl RTSPThread {
pub fn new(type_: ::RTSPThreadType) -> Option<Self> {
unsafe { from_glib_full(gst_rtsp_server_sys::gst_rtsp_thread_new(type_.to_glib())) }
}
}
impl RTSPThreadRef {
pub fn reuse(&self) -> bool {
unsafe {
from_glib(gst_rtsp_server_sys::gst_rtsp_thread_reuse(
self.as_mut_ptr(),
))
}
}
pub fn stop(&self) {
unsafe {
gst_rtsp_server_sys::gst_rtsp_thread_stop(self.as_mut_ptr());
}
}
pub fn type_(&self) -> ::RTSPThreadType {
unsafe { from_glib((*self.as_ptr()).type_) }
}
pub fn context(&self) -> glib::MainContext {
unsafe { from_glib_none((*self.as_ptr()).context) }
}
pub fn loop_(&self) -> glib::MainLoop {
unsafe { from_glib_none((*self.as_ptr()).loop_) }
}
}

View file

@ -0,0 +1,20 @@
// Copyright (C) 2020 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.
#![allow(clippy::cast_ptr_alignment)]
pub mod rtsp_client;
pub mod rtsp_media;
pub mod rtsp_media_factory;
pub mod rtsp_server;
pub mod prelude {
pub use super::rtsp_client::{RTSPClientImpl, RTSPClientImplExt};
pub use super::rtsp_media::{RTSPMediaImpl, RTSPMediaImplExt};
pub use super::rtsp_media_factory::{RTSPMediaFactoryImpl, RTSPMediaFactoryImplExt};
pub use super::rtsp_server::{RTSPServerImpl, RTSPServerImplExt};
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,780 @@
// Copyright (C) 2020 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 gst_rtsp_server_sys;
use glib::translate::*;
use glib::subclass::prelude::*;
use gst::prelude::*;
use std::ptr;
use RTSPMedia;
use RTSPMediaClass;
use RTSPThread;
#[derive(Debug)]
pub struct SDPInfo(ptr::NonNull<gst_rtsp_server_sys::GstSDPInfo>);
impl SDPInfo {
pub fn is_ipv6(&self) -> bool {
unsafe { from_glib(self.0.as_ref().is_ipv6) }
}
pub fn server_ip(&self) -> &str {
unsafe {
use std::ffi::CStr;
CStr::from_ptr(self.0.as_ref().server_ip).to_str().unwrap()
}
}
}
pub trait RTSPMediaImpl: RTSPMediaImplExt + ObjectImpl + Send + Sync + 'static {
fn handle_message(&self, media: &RTSPMedia, message: &gst::MessageRef) -> bool {
self.parent_handle_message(media, message)
}
fn prepare(&self, media: &RTSPMedia, thread: &RTSPThread) -> Result<(), gst::LoggableError> {
self.parent_prepare(media, thread)
}
fn unprepare(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError> {
self.parent_unprepare(media)
}
fn suspend(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError> {
self.parent_suspend(media)
}
fn unsuspend(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError> {
self.parent_unsuspend(media)
}
// TODO missing: convert_range
fn query_position(&self, media: &RTSPMedia) -> Option<gst::ClockTime> {
self.parent_query_position(media)
}
fn query_stop(&self, media: &RTSPMedia) -> Option<gst::ClockTime> {
self.parent_query_stop(media)
}
fn create_rtpbin(&self, media: &RTSPMedia) -> Option<gst::Element> {
self.parent_create_rtpbin(media)
}
fn setup_rtpbin(
&self,
media: &RTSPMedia,
rtpbin: &gst::Element,
) -> Result<(), gst::LoggableError> {
self.parent_setup_rtpbin(media, rtpbin)
}
fn setup_sdp(
&self,
media: &RTSPMedia,
sdp: &mut gst_sdp::SDPMessageRef,
info: &SDPInfo,
) -> Result<(), gst::LoggableError> {
self.parent_setup_sdp(media, sdp, info)
}
fn new_stream(&self, media: &RTSPMedia, stream: &::RTSPStream) {
self.parent_new_stream(media, stream);
}
fn removed_stream(&self, media: &RTSPMedia, stream: &::RTSPStream) {
self.parent_removed_stream(media, stream);
}
fn prepared(&self, media: &RTSPMedia) {
self.parent_prepared(media);
}
fn unprepared(&self, media: &RTSPMedia) {
self.parent_unprepared(media);
}
fn target_state(&self, media: &RTSPMedia, state: gst::State) {
self.parent_target_state(media, state);
}
fn new_state(&self, media: &RTSPMedia, state: gst::State) {
self.parent_new_state(media, state);
}
fn handle_sdp(
&self,
media: &RTSPMedia,
sdp: &gst_sdp::SDPMessageRef,
) -> Result<(), gst::LoggableError> {
self.parent_handle_sdp(media, sdp)
}
}
pub trait RTSPMediaImplExt {
fn parent_handle_message(&self, media: &RTSPMedia, message: &gst::MessageRef) -> bool;
fn parent_prepare(
&self,
media: &RTSPMedia,
thread: &RTSPThread,
) -> Result<(), gst::LoggableError>;
fn parent_unprepare(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError>;
fn parent_suspend(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError>;
fn parent_unsuspend(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError>;
// TODO missing: convert_range
fn parent_query_position(&self, media: &RTSPMedia) -> Option<gst::ClockTime>;
fn parent_query_stop(&self, media: &RTSPMedia) -> Option<gst::ClockTime>;
fn parent_create_rtpbin(&self, media: &RTSPMedia) -> Option<gst::Element>;
fn parent_setup_rtpbin(
&self,
media: &RTSPMedia,
rtpbin: &gst::Element,
) -> Result<(), gst::LoggableError>;
fn parent_setup_sdp(
&self,
media: &RTSPMedia,
sdp: &mut gst_sdp::SDPMessageRef,
info: &SDPInfo,
) -> Result<(), gst::LoggableError>;
fn parent_new_stream(&self, media: &RTSPMedia, stream: &::RTSPStream);
fn parent_removed_stream(&self, media: &RTSPMedia, stream: &::RTSPStream);
fn parent_prepared(&self, media: &RTSPMedia);
fn parent_unprepared(&self, media: &RTSPMedia);
fn parent_target_state(&self, media: &RTSPMedia, state: gst::State);
fn parent_new_state(&self, media: &RTSPMedia, state: gst::State);
fn parent_handle_sdp(
&self,
media: &RTSPMedia,
sdp: &gst_sdp::SDPMessageRef,
) -> Result<(), gst::LoggableError>;
}
impl<T: RTSPMediaImpl + ObjectImpl> RTSPMediaImplExt for T {
fn parent_handle_message(&self, media: &RTSPMedia, message: &gst::MessageRef) -> bool {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).handle_message {
from_glib(f(media.to_glib_none().0, message.as_ptr() as *mut _))
} else {
false
}
}
}
fn parent_prepare(
&self,
media: &RTSPMedia,
thread: &RTSPThread,
) -> Result<(), gst::LoggableError> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).prepare {
gst_result_from_gboolean!(
f(media.to_glib_none().0, thread.to_glib_none().0),
gst::CAT_RUST,
"Parent function `prepare` failed"
)
} else {
Ok(())
}
}
}
fn parent_unprepare(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).unprepare {
gst_result_from_gboolean!(
f(media.to_glib_none().0),
gst::CAT_RUST,
"Parent function `unprepare` failed"
)
} else {
Ok(())
}
}
}
fn parent_suspend(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).suspend {
gst_result_from_gboolean!(
f(media.to_glib_none().0),
gst::CAT_RUST,
"Parent function `suspend` failed"
)
} else {
Ok(())
}
}
}
fn parent_unsuspend(&self, media: &RTSPMedia) -> Result<(), gst::LoggableError> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).unsuspend {
gst_result_from_gboolean!(
f(media.to_glib_none().0),
gst::CAT_RUST,
"Parent function `unsuspend` failed"
)
} else {
Ok(())
}
}
}
// TODO missing: convert_range
fn parent_query_position(&self, media: &RTSPMedia) -> Option<gst::ClockTime> {
unsafe {
use std::mem;
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).query_position {
let mut position = mem::MaybeUninit::uninit();
if f(media.to_glib_none().0, position.as_mut_ptr()) == glib_sys::GFALSE {
None
} else {
Some(from_glib(position.assume_init() as u64))
}
} else {
None
}
}
}
fn parent_query_stop(&self, media: &RTSPMedia) -> Option<gst::ClockTime> {
unsafe {
use std::mem;
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).query_stop {
let mut stop = mem::MaybeUninit::uninit();
if f(media.to_glib_none().0, stop.as_mut_ptr()) == glib_sys::GFALSE {
None
} else {
Some(from_glib(stop.assume_init() as u64))
}
} else {
None
}
}
}
fn parent_create_rtpbin(&self, media: &RTSPMedia) -> Option<gst::Element> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
let f = (*parent_class)
.create_rtpbin
.expect("No `create_rtpbin` virtual method implementation in parent class");
from_glib_none(f(media.to_glib_none().0))
}
}
fn parent_setup_rtpbin(
&self,
media: &RTSPMedia,
rtpbin: &gst::Element,
) -> Result<(), gst::LoggableError> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).setup_rtpbin {
let ptr = rtpbin.to_glib_none().0;
// The C code assumes to pass a floating reference around so let's make sure we do
gobject_sys::g_object_force_floating(ptr as *mut _);
let res = gst_result_from_gboolean!(
f(media.to_glib_none().0, ptr),
gst::CAT_RUST,
"Parent function `setup_sdp` failed"
);
// If the code didn't accidentally sink it then we have to do that
// here now so that we don't have any floating reference on our side
// anymore
if gobject_sys::g_object_is_floating(ptr as *mut _) != glib_sys::GFALSE {
gobject_sys::g_object_ref_sink(ptr as *mut _);
}
res
} else {
Ok(())
}
}
}
fn parent_setup_sdp(
&self,
media: &RTSPMedia,
sdp: &mut gst_sdp::SDPMessageRef,
info: &SDPInfo,
) -> Result<(), gst::LoggableError> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
let f = (*parent_class)
.setup_sdp
.expect("No `setup_sdp` virtual method implementation in parent class");
gst_result_from_gboolean!(
f(
media.to_glib_none().0,
sdp as *mut _ as *mut gst_sdp_sys::GstSDPMessage,
info.0.as_ptr()
),
gst::CAT_RUST,
"Parent function `setup_sdp` failed"
)
}
}
fn parent_new_stream(&self, media: &RTSPMedia, stream: &::RTSPStream) {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).new_stream {
f(media.to_glib_none().0, stream.to_glib_none().0);
}
}
}
fn parent_removed_stream(&self, media: &RTSPMedia, stream: &::RTSPStream) {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).removed_stream {
f(media.to_glib_none().0, stream.to_glib_none().0);
}
}
}
fn parent_prepared(&self, media: &RTSPMedia) {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).prepared {
f(media.to_glib_none().0);
}
}
}
fn parent_unprepared(&self, media: &RTSPMedia) {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).unprepared {
f(media.to_glib_none().0);
}
}
}
fn parent_target_state(&self, media: &RTSPMedia, state: gst::State) {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).target_state {
f(media.to_glib_none().0, state.to_glib());
}
}
}
fn parent_new_state(&self, media: &RTSPMedia, state: gst::State) {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
if let Some(f) = (*parent_class).new_state {
f(media.to_glib_none().0, state.to_glib());
}
}
}
fn parent_handle_sdp(
&self,
media: &RTSPMedia,
sdp: &gst_sdp::SDPMessageRef,
) -> Result<(), gst::LoggableError> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPMediaClass;
let f = (*parent_class)
.handle_sdp
.expect("No `handle_sdp` virtual method implementation in parent class");
gst_result_from_gboolean!(
f(
media.to_glib_none().0,
sdp as *const _ as *mut gst_sdp_sys::GstSDPMessage
),
gst::CAT_RUST,
"Parent function `handle_sdp` failed"
)
}
}
}
unsafe impl<T: ObjectSubclass + RTSPMediaImpl> IsSubclassable<T> for RTSPMediaClass {
fn override_vfuncs(&mut self) {
<glib::ObjectClass as IsSubclassable<T>>::override_vfuncs(self);
unsafe {
let klass = &mut *(self as *mut Self as *mut gst_rtsp_server_sys::GstRTSPMediaClass);
klass.handle_message = Some(media_handle_message::<T>);
klass.prepare = Some(media_prepare::<T>);
klass.unprepare = Some(media_unprepare::<T>);
klass.suspend = Some(media_suspend::<T>);
klass.unsuspend = Some(media_unsuspend::<T>);
klass.query_position = Some(media_query_position::<T>);
klass.query_stop = Some(media_query_stop::<T>);
klass.create_rtpbin = Some(media_create_rtpbin::<T>);
klass.setup_rtpbin = Some(media_setup_rtpbin::<T>);
klass.setup_sdp = Some(media_setup_sdp::<T>);
klass.new_stream = Some(media_new_stream::<T>);
klass.removed_stream = Some(media_removed_stream::<T>);
klass.prepared = Some(media_prepared::<T>);
klass.unprepared = Some(media_unprepared::<T>);
klass.target_state = Some(media_target_state::<T>);
klass.new_state = Some(media_new_state::<T>);
klass.handle_sdp = Some(media_handle_sdp::<T>);
}
}
}
unsafe extern "C" fn media_handle_message<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
message: *mut gst_sys::GstMessage,
) -> glib_sys::gboolean
where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
imp.handle_message(&wrap, gst::MessageRef::from_ptr(message))
.to_glib()
}
unsafe extern "C" fn media_prepare<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
thread: *mut gst_rtsp_server_sys::GstRTSPThread,
) -> glib_sys::gboolean
where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
match imp.prepare(&wrap, &from_glib_borrow(thread)) {
Ok(()) => glib_sys::GTRUE,
Err(err) => {
err.log_with_object(&wrap);
glib_sys::GFALSE
}
}
}
unsafe extern "C" fn media_unprepare<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
) -> glib_sys::gboolean
where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
match imp.unprepare(&wrap) {
Ok(()) => glib_sys::GTRUE,
Err(err) => {
err.log_with_object(&wrap);
glib_sys::GFALSE
}
}
}
unsafe extern "C" fn media_suspend<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
) -> glib_sys::gboolean
where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
match imp.suspend(&wrap) {
Ok(()) => glib_sys::GTRUE,
Err(err) => {
err.log_with_object(&wrap);
glib_sys::GFALSE
}
}
}
unsafe extern "C" fn media_unsuspend<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
) -> glib_sys::gboolean
where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
match imp.unsuspend(&wrap) {
Ok(()) => glib_sys::GTRUE,
Err(err) => {
err.log_with_object(&wrap);
glib_sys::GFALSE
}
}
}
unsafe extern "C" fn media_query_position<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
position: *mut i64,
) -> glib_sys::gboolean
where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
match imp.query_position(&wrap) {
Some(pos) => {
*position = pos.to_glib() as i64;
glib_sys::GTRUE
}
None => glib_sys::GFALSE,
}
}
unsafe extern "C" fn media_query_stop<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
stop: *mut i64,
) -> glib_sys::gboolean
where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
match imp.query_stop(&wrap) {
Some(s) => {
*stop = s.to_glib() as i64;
glib_sys::GTRUE
}
None => glib_sys::GFALSE,
}
}
unsafe extern "C" fn media_create_rtpbin<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
) -> *mut gst_sys::GstElement
where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
let res: *mut gst_sys::GstElement = imp.create_rtpbin(&wrap).to_glib_full();
if !res.is_null() {
gobject_sys::g_object_force_floating(res as *mut _);
}
res
}
unsafe extern "C" fn media_setup_rtpbin<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
rtpbin: *mut gst_sys::GstElement,
) -> glib_sys::gboolean
where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
// If the rtpbin was floating before make sure it is not anymore for now so
// we don't accidentally take ownership of it somewhere along the line
if gobject_sys::g_object_is_floating(rtpbin as *mut _) != glib_sys::GFALSE {
gobject_sys::g_object_ref_sink(rtpbin as *mut _);
}
let res = match imp.setup_rtpbin(&wrap, &from_glib_borrow(rtpbin)) {
Ok(()) => glib_sys::GTRUE,
Err(err) => {
err.log_with_object(&wrap);
glib_sys::GFALSE
}
};
// Ensure that the rtpbin is still floating afterwards here
gobject_sys::g_object_force_floating(rtpbin as *mut _);
res
}
unsafe extern "C" fn media_setup_sdp<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
sdp: *mut gst_sdp_sys::GstSDPMessage,
info: *mut gst_rtsp_server_sys::GstSDPInfo,
) -> glib_sys::gboolean
where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
match imp.setup_sdp(
&wrap,
&mut *(sdp as *mut gst_sdp::SDPMessageRef),
&SDPInfo(ptr::NonNull::new(info).expect("NULL SDPInfo")),
) {
Ok(()) => glib_sys::GTRUE,
Err(err) => {
err.log_with_object(&wrap);
glib_sys::GFALSE
}
}
}
unsafe extern "C" fn media_new_stream<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
stream: *mut gst_rtsp_server_sys::GstRTSPStream,
) where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
imp.new_stream(&wrap, &from_glib_borrow(stream));
}
unsafe extern "C" fn media_removed_stream<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
stream: *mut gst_rtsp_server_sys::GstRTSPStream,
) where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
imp.removed_stream(&wrap, &from_glib_borrow(stream));
}
unsafe extern "C" fn media_prepared<T: ObjectSubclass>(ptr: *mut gst_rtsp_server_sys::GstRTSPMedia)
where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
imp.prepared(&wrap);
}
unsafe extern "C" fn media_unprepared<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
) where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
imp.unprepared(&wrap);
}
unsafe extern "C" fn media_target_state<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
state: gst_sys::GstState,
) where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
imp.target_state(&wrap, from_glib(state));
}
unsafe extern "C" fn media_new_state<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
state: gst_sys::GstState,
) where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
imp.new_state(&wrap, from_glib(state));
}
unsafe extern "C" fn media_handle_sdp<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMedia,
sdp: *mut gst_sdp_sys::GstSDPMessage,
) -> glib_sys::gboolean
where
T: RTSPMediaImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMedia = from_glib_borrow(ptr);
match imp.handle_sdp(&wrap, &*(sdp as *const gst_sdp::SDPMessageRef)) {
Ok(()) => glib_sys::GTRUE,
Err(err) => {
err.log_with_object(&wrap);
glib_sys::GFALSE
}
}
}

View file

@ -0,0 +1,338 @@
// Copyright (C) 2020 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 gst_rtsp_server_sys;
use glib::translate::*;
use gst_rtsp;
use glib::subclass::prelude::*;
use RTSPMediaFactory;
use RTSPMediaFactoryClass;
pub trait RTSPMediaFactoryImpl:
RTSPMediaFactoryImplExt + ObjectImpl + Send + Sync + 'static
{
fn gen_key(
&self,
factory: &RTSPMediaFactory,
url: &gst_rtsp::RTSPUrl,
) -> Option<glib::GString> {
self.parent_gen_key(factory, url)
}
fn create_element(
&self,
factory: &RTSPMediaFactory,
url: &gst_rtsp::RTSPUrl,
) -> Option<gst::Element> {
self.parent_create_element(factory, url)
}
fn construct(
&self,
factory: &RTSPMediaFactory,
url: &gst_rtsp::RTSPUrl,
) -> Option<::RTSPMedia> {
self.parent_construct(factory, url)
}
fn create_pipeline(
&self,
factory: &RTSPMediaFactory,
media: &::RTSPMedia,
) -> Option<gst::Pipeline> {
self.parent_create_pipeline(factory, media)
}
fn configure(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia) {
self.parent_configure(factory, media)
}
fn media_constructed(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia) {
self.parent_media_constructed(factory, media)
}
fn media_configure(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia) {
self.parent_media_configure(factory, media)
}
}
pub trait RTSPMediaFactoryImplExt {
fn parent_gen_key(
&self,
factory: &RTSPMediaFactory,
url: &gst_rtsp::RTSPUrl,
) -> Option<glib::GString>;
fn parent_create_element(
&self,
factory: &RTSPMediaFactory,
url: &gst_rtsp::RTSPUrl,
) -> Option<gst::Element>;
fn parent_construct(
&self,
factory: &RTSPMediaFactory,
url: &gst_rtsp::RTSPUrl,
) -> Option<::RTSPMedia>;
fn parent_create_pipeline(
&self,
factory: &RTSPMediaFactory,
media: &::RTSPMedia,
) -> Option<gst::Pipeline>;
fn parent_configure(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia);
fn parent_media_constructed(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia);
fn parent_media_configure(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia);
}
impl<T: RTSPMediaFactoryImpl + ObjectImpl> RTSPMediaFactoryImplExt for T {
fn parent_gen_key(
&self,
factory: &RTSPMediaFactory,
url: &gst_rtsp::RTSPUrl,
) -> Option<glib::GString> {
unsafe {
let data = self.get_type_data();
let parent_class = data.as_ref().get_parent_class()
as *mut gst_rtsp_server_sys::GstRTSPMediaFactoryClass;
(*parent_class)
.gen_key
.map(|f| from_glib_full(f(factory.to_glib_none().0, url.to_glib_none().0)))
.unwrap_or(None)
}
}
fn parent_create_element(
&self,
factory: &RTSPMediaFactory,
url: &gst_rtsp::RTSPUrl,
) -> Option<gst::Element> {
unsafe {
let data = self.get_type_data();
let parent_class = data.as_ref().get_parent_class()
as *mut gst_rtsp_server_sys::GstRTSPMediaFactoryClass;
(*parent_class)
.create_element
.map(|f| from_glib_none(f(factory.to_glib_none().0, url.to_glib_none().0)))
.unwrap_or(None)
}
}
fn parent_construct(
&self,
factory: &RTSPMediaFactory,
url: &gst_rtsp::RTSPUrl,
) -> Option<::RTSPMedia> {
unsafe {
let data = self.get_type_data();
let parent_class = data.as_ref().get_parent_class()
as *mut gst_rtsp_server_sys::GstRTSPMediaFactoryClass;
(*parent_class)
.construct
.map(|f| from_glib_full(f(factory.to_glib_none().0, url.to_glib_none().0)))
.unwrap_or(None)
}
}
fn parent_create_pipeline(
&self,
factory: &RTSPMediaFactory,
media: &::RTSPMedia,
) -> Option<gst::Pipeline> {
unsafe {
let data = self.get_type_data();
let parent_class = data.as_ref().get_parent_class()
as *mut gst_rtsp_server_sys::GstRTSPMediaFactoryClass;
(*parent_class)
.create_pipeline
.map(|f| {
let ptr = f(factory.to_glib_none().0, media.to_glib_none().0)
as *mut gst_sys::GstPipeline;
// See https://gitlab.freedesktop.org/gstreamer/gst-rtsp-server/merge_requests/109
if gobject_sys::g_object_is_floating(ptr as *mut _) != glib_sys::GFALSE {
gobject_sys::g_object_ref_sink(ptr as *mut _);
}
from_glib_none(ptr)
})
.unwrap_or(None)
}
}
fn parent_configure(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia) {
unsafe {
let data = self.get_type_data();
let parent_class = data.as_ref().get_parent_class()
as *mut gst_rtsp_server_sys::GstRTSPMediaFactoryClass;
if let Some(f) = (*parent_class).configure {
f(factory.to_glib_none().0, media.to_glib_none().0);
}
}
}
fn parent_media_constructed(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia) {
unsafe {
let data = self.get_type_data();
let parent_class = data.as_ref().get_parent_class()
as *mut gst_rtsp_server_sys::GstRTSPMediaFactoryClass;
if let Some(f) = (*parent_class).media_constructed {
f(factory.to_glib_none().0, media.to_glib_none().0);
}
}
}
fn parent_media_configure(&self, factory: &RTSPMediaFactory, media: &::RTSPMedia) {
unsafe {
let data = self.get_type_data();
let parent_class = data.as_ref().get_parent_class()
as *mut gst_rtsp_server_sys::GstRTSPMediaFactoryClass;
if let Some(f) = (*parent_class).media_configure {
f(factory.to_glib_none().0, media.to_glib_none().0);
}
}
}
}
unsafe impl<T: ObjectSubclass + RTSPMediaFactoryImpl> IsSubclassable<T> for RTSPMediaFactoryClass {
fn override_vfuncs(&mut self) {
<glib::ObjectClass as IsSubclassable<T>>::override_vfuncs(self);
unsafe {
let klass =
&mut *(self as *mut Self as *mut gst_rtsp_server_sys::GstRTSPMediaFactoryClass);
klass.gen_key = Some(factory_gen_key::<T>);
klass.create_element = Some(factory_create_element::<T>);
klass.construct = Some(factory_construct::<T>);
klass.create_pipeline = Some(factory_create_pipeline::<T>);
klass.configure = Some(factory_configure::<T>);
klass.media_constructed = Some(factory_media_constructed::<T>);
klass.media_configure = Some(factory_media_configure::<T>);
}
}
}
unsafe extern "C" fn factory_gen_key<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMediaFactory,
url: *const gst_rtsp_sys::GstRTSPUrl,
) -> *mut std::os::raw::c_char
where
T: RTSPMediaFactoryImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMediaFactory = from_glib_borrow(ptr);
imp.gen_key(&wrap, &from_glib_borrow(url)).to_glib_full()
}
unsafe extern "C" fn factory_create_element<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMediaFactory,
url: *const gst_rtsp_sys::GstRTSPUrl,
) -> *mut gst_sys::GstElement
where
T: RTSPMediaFactoryImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMediaFactory = from_glib_borrow(ptr);
let element = imp
.create_element(&wrap, &from_glib_borrow(url))
.to_glib_full();
gobject_sys::g_object_force_floating(element as *mut _);
element
}
unsafe extern "C" fn factory_construct<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMediaFactory,
url: *const gst_rtsp_sys::GstRTSPUrl,
) -> *mut gst_rtsp_server_sys::GstRTSPMedia
where
T: RTSPMediaFactoryImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMediaFactory = from_glib_borrow(ptr);
imp.construct(&wrap, &from_glib_borrow(url)).to_glib_full()
}
lazy_static! {
static ref PIPELINE_QUARK: glib::Quark =
glib::Quark::from_string("gstreamer-rs-rtsp-media-pipeline");
}
unsafe extern "C" fn factory_create_pipeline<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMediaFactory,
media: *mut gst_rtsp_server_sys::GstRTSPMedia,
) -> *mut gst_sys::GstElement
where
T: RTSPMediaFactoryImpl,
{
use std::mem;
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMediaFactory = from_glib_borrow(ptr);
let pipeline: *mut gst_sys::GstPipeline = imp
.create_pipeline(&wrap, &from_glib_borrow(media))
.to_glib_full();
// FIXME We somehow need to ensure the pipeline actually stays alive...
gobject_sys::g_object_set_qdata_full(
media as *mut _,
PIPELINE_QUARK.to_glib(),
pipeline as *mut _,
Some(mem::transmute(gobject_sys::g_object_unref as usize)),
);
pipeline as *mut _
}
unsafe extern "C" fn factory_configure<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMediaFactory,
media: *mut gst_rtsp_server_sys::GstRTSPMedia,
) where
T: RTSPMediaFactoryImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMediaFactory = from_glib_borrow(ptr);
imp.configure(&wrap, &from_glib_borrow(media));
}
unsafe extern "C" fn factory_media_constructed<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMediaFactory,
media: *mut gst_rtsp_server_sys::GstRTSPMedia,
) where
T: RTSPMediaFactoryImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMediaFactory = from_glib_borrow(ptr);
imp.media_constructed(&wrap, &from_glib_borrow(media));
}
unsafe extern "C" fn factory_media_configure<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPMediaFactory,
media: *mut gst_rtsp_server_sys::GstRTSPMedia,
) where
T: RTSPMediaFactoryImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPMediaFactory = from_glib_borrow(ptr);
imp.media_configure(&wrap, &from_glib_borrow(media));
}

View file

@ -0,0 +1,92 @@
// Copyright (C) 2020 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 gst_rtsp_server_sys;
use glib::subclass::prelude::*;
use glib::translate::*;
use RTSPServer;
use RTSPServerClass;
pub trait RTSPServerImpl: RTSPServerImplExt + ObjectImpl + Send + Sync + 'static {
fn create_client(&self, server: &RTSPServer) -> Option<::RTSPClient> {
self.parent_create_client(server)
}
fn client_connected(&self, server: &RTSPServer, client: &::RTSPClient) {
self.parent_client_connected(server, client);
}
}
pub trait RTSPServerImplExt {
fn parent_create_client(&self, server: &RTSPServer) -> Option<::RTSPClient>;
fn parent_client_connected(&self, server: &RTSPServer, client: &::RTSPClient);
}
impl<T: RTSPServerImpl + ObjectImpl> RTSPServerImplExt for T {
fn parent_create_client(&self, server: &RTSPServer) -> Option<::RTSPClient> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPServerClass;
let f = (*parent_class)
.create_client
.expect("No `create_client` virtual method implementation in parent class");
from_glib_full(f(server.to_glib_none().0))
}
}
fn parent_client_connected(&self, server: &RTSPServer, client: &::RTSPClient) {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_rtsp_server_sys::GstRTSPServerClass;
if let Some(f) = (*parent_class).client_connected {
f(server.to_glib_none().0, client.to_glib_none().0)
}
}
}
}
unsafe impl<T: ObjectSubclass + RTSPServerImpl> IsSubclassable<T> for RTSPServerClass {
fn override_vfuncs(&mut self) {
<glib::ObjectClass as IsSubclassable<T>>::override_vfuncs(self);
unsafe {
let klass = &mut *(self as *mut Self as *mut gst_rtsp_server_sys::GstRTSPServerClass);
klass.create_client = Some(server_create_client::<T>);
klass.client_connected = Some(server_client_connected::<T>);
}
}
}
unsafe extern "C" fn server_create_client<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPServer,
) -> *mut gst_rtsp_server_sys::GstRTSPClient
where
T: RTSPServerImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPServer = from_glib_borrow(ptr);
imp.create_client(&wrap).to_glib_full()
}
unsafe extern "C" fn server_client_connected<T: ObjectSubclass>(
ptr: *mut gst_rtsp_server_sys::GstRTSPServer,
client: *mut gst_rtsp_server_sys::GstRTSPClient,
) where
T: RTSPServerImpl,
{
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: RTSPServer = from_glib_borrow(ptr);
imp.client_connected(&wrap, &from_glib_borrow(client));
}

View file

@ -5,6 +5,75 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +685,11 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...HEAD
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-rtsp"
version = "0.15.0"
version = "0.15.4"
authors = ["Mathieu Duponchelle <mathieu@centricular.com>", "Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer Rtsp library"
@ -15,16 +15,16 @@ build = "build.rs"
[dependencies]
bitflags = "1.0"
libc = "0.2"
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gio-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-rtsp-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-sdp-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
glib = { git = "https://github.com/gtk-rs/glib" }
gio = { git = "https://github.com/gtk-rs/gio" }
gstreamer = { path = "../gstreamer" }
gstreamer-sdp = { path = "../gstreamer-sdp" }
glib-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gio-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-rtsp-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-sdp-sys = { version = "0.8", features = ["v1_8"] }
glib = { version = "0.9" }
gio = { version = "0.8" }
gstreamer = { version = "0.15", path = "../gstreamer" }
gstreamer-sdp = { version = "0.15", path = "../gstreamer-sdp" }
[build-dependencies]
rustdoc-stripper = { version = "0.1", optional = true }

View file

@ -5,6 +5,75 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +685,11 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...HEAD
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-sdp"
version = "0.15.0"
version = "0.15.4"
authors = ["Mathieu Duponchelle <mathieu@centricular.com>", "Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer Sdp library"
@ -13,12 +13,12 @@ keywords = ["gstreamer", "multimedia", "audio", "video", "gnome"]
build = "build.rs"
[dependencies]
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-sdp-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
glib = { git = "https://github.com/gtk-rs/glib" }
gstreamer = { path = "../gstreamer" }
glib-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-sdp-sys = { version = "0.8", features = ["v1_8"] }
glib = { version = "0.9" }
gstreamer = { version = "0.15", path = "../gstreamer" }
[build-dependencies]
rustdoc-stripper = { version = "0.1", optional = true }

View file

@ -5,6 +5,75 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +685,11 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...HEAD
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-video"
version = "0.15.0"
version = "0.15.4"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer Video library"
@ -15,15 +15,17 @@ build = "build.rs"
[dependencies]
bitflags = "1.0"
libc = "0.2"
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-base-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-video-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
glib = { git = "https://github.com/gtk-rs/glib" }
gstreamer = { path = "../gstreamer" }
gstreamer-base = { path = "../gstreamer-base" }
glib-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-base-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-video-sys = { version = "0.8", features = ["v1_8"] }
glib = { version = "0.9" }
gstreamer = { version = "0.15", path = "../gstreamer" }
gstreamer-base = { version = "0.15", path = "../gstreamer-base" }
lazy_static = "1.0"
futures-channel = "0.3"
futures-util = "0.3"
[build-dependencies]
rustdoc-stripper = { version = "0.1", optional = true }

View file

@ -12,6 +12,66 @@ use glib::Type;
use gobject_sys;
use gst_video_sys;
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum VideoAlphaMode {
Copy,
Set,
Mult,
#[doc(hidden)]
__Unknown(i32),
}
#[doc(hidden)]
impl ToGlib for VideoAlphaMode {
type GlibType = gst_video_sys::GstVideoAlphaMode;
fn to_glib(&self) -> gst_video_sys::GstVideoAlphaMode {
match *self {
VideoAlphaMode::Copy => gst_video_sys::GST_VIDEO_ALPHA_MODE_COPY,
VideoAlphaMode::Set => gst_video_sys::GST_VIDEO_ALPHA_MODE_SET,
VideoAlphaMode::Mult => gst_video_sys::GST_VIDEO_ALPHA_MODE_MULT,
VideoAlphaMode::__Unknown(value) => value,
}
}
}
#[doc(hidden)]
impl FromGlib<gst_video_sys::GstVideoAlphaMode> for VideoAlphaMode {
fn from_glib(value: gst_video_sys::GstVideoAlphaMode) -> Self {
skip_assert_initialized!();
match value {
0 => VideoAlphaMode::Copy,
1 => VideoAlphaMode::Set,
2 => VideoAlphaMode::Mult,
value => VideoAlphaMode::__Unknown(value),
}
}
}
impl StaticType for VideoAlphaMode {
fn static_type() -> Type {
unsafe { from_glib(gst_video_sys::gst_video_alpha_mode_get_type()) }
}
}
impl<'a> FromValueOptional<'a> for VideoAlphaMode {
unsafe fn from_value_optional(value: &Value) -> Option<Self> {
Some(FromValue::from_value(value))
}
}
impl<'a> FromValue<'a> for VideoAlphaMode {
unsafe fn from_value(value: &Value) -> Self {
from_glib(gobject_sys::g_value_get_enum(value.to_glib_none().0))
}
}
impl SetValue for VideoAlphaMode {
unsafe fn set_value(value: &mut Value, this: &Self) {
gobject_sys::g_value_set_enum(value.to_glib_none_mut().0, this.to_glib())
}
}
#[cfg(any(feature = "v1_16", feature = "dox"))]
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum VideoCaptionType {
@ -85,6 +145,69 @@ impl SetValue for VideoCaptionType {
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum VideoChromaMode {
Full,
UpsampleOnly,
DownsampleOnly,
None,
#[doc(hidden)]
__Unknown(i32),
}
#[doc(hidden)]
impl ToGlib for VideoChromaMode {
type GlibType = gst_video_sys::GstVideoChromaMode;
fn to_glib(&self) -> gst_video_sys::GstVideoChromaMode {
match *self {
VideoChromaMode::Full => gst_video_sys::GST_VIDEO_CHROMA_MODE_FULL,
VideoChromaMode::UpsampleOnly => gst_video_sys::GST_VIDEO_CHROMA_MODE_UPSAMPLE_ONLY,
VideoChromaMode::DownsampleOnly => gst_video_sys::GST_VIDEO_CHROMA_MODE_DOWNSAMPLE_ONLY,
VideoChromaMode::None => gst_video_sys::GST_VIDEO_CHROMA_MODE_NONE,
VideoChromaMode::__Unknown(value) => value,
}
}
}
#[doc(hidden)]
impl FromGlib<gst_video_sys::GstVideoChromaMode> for VideoChromaMode {
fn from_glib(value: gst_video_sys::GstVideoChromaMode) -> Self {
skip_assert_initialized!();
match value {
0 => VideoChromaMode::Full,
1 => VideoChromaMode::UpsampleOnly,
2 => VideoChromaMode::DownsampleOnly,
3 => VideoChromaMode::None,
value => VideoChromaMode::__Unknown(value),
}
}
}
impl StaticType for VideoChromaMode {
fn static_type() -> Type {
unsafe { from_glib(gst_video_sys::gst_video_chroma_mode_get_type()) }
}
}
impl<'a> FromValueOptional<'a> for VideoChromaMode {
unsafe fn from_value_optional(value: &Value) -> Option<Self> {
Some(FromValue::from_value(value))
}
}
impl<'a> FromValue<'a> for VideoChromaMode {
unsafe fn from_value(value: &Value) -> Self {
from_glib(gobject_sys::g_value_get_enum(value.to_glib_none().0))
}
}
impl SetValue for VideoChromaMode {
unsafe fn set_value(value: &mut Value, this: &Self) {
gobject_sys::g_value_set_enum(value.to_glib_none_mut().0, this.to_glib())
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum VideoColorMatrix {
Unknown,
@ -247,6 +370,72 @@ impl SetValue for VideoColorPrimaries {
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum VideoDitherMethod {
None,
Verterr,
FloydSteinberg,
SierraLite,
Bayer,
#[doc(hidden)]
__Unknown(i32),
}
#[doc(hidden)]
impl ToGlib for VideoDitherMethod {
type GlibType = gst_video_sys::GstVideoDitherMethod;
fn to_glib(&self) -> gst_video_sys::GstVideoDitherMethod {
match *self {
VideoDitherMethod::None => gst_video_sys::GST_VIDEO_DITHER_NONE,
VideoDitherMethod::Verterr => gst_video_sys::GST_VIDEO_DITHER_VERTERR,
VideoDitherMethod::FloydSteinberg => gst_video_sys::GST_VIDEO_DITHER_FLOYD_STEINBERG,
VideoDitherMethod::SierraLite => gst_video_sys::GST_VIDEO_DITHER_SIERRA_LITE,
VideoDitherMethod::Bayer => gst_video_sys::GST_VIDEO_DITHER_BAYER,
VideoDitherMethod::__Unknown(value) => value,
}
}
}
#[doc(hidden)]
impl FromGlib<gst_video_sys::GstVideoDitherMethod> for VideoDitherMethod {
fn from_glib(value: gst_video_sys::GstVideoDitherMethod) -> Self {
skip_assert_initialized!();
match value {
0 => VideoDitherMethod::None,
1 => VideoDitherMethod::Verterr,
2 => VideoDitherMethod::FloydSteinberg,
3 => VideoDitherMethod::SierraLite,
4 => VideoDitherMethod::Bayer,
value => VideoDitherMethod::__Unknown(value),
}
}
}
impl StaticType for VideoDitherMethod {
fn static_type() -> Type {
unsafe { from_glib(gst_video_sys::gst_video_dither_method_get_type()) }
}
}
impl<'a> FromValueOptional<'a> for VideoDitherMethod {
unsafe fn from_value_optional(value: &Value) -> Option<Self> {
Some(FromValue::from_value(value))
}
}
impl<'a> FromValue<'a> for VideoDitherMethod {
unsafe fn from_value(value: &Value) -> Self {
from_glib(gobject_sys::g_value_get_enum(value.to_glib_none().0))
}
}
impl SetValue for VideoDitherMethod {
unsafe fn set_value(value: &mut Value, this: &Self) {
gobject_sys::g_value_set_enum(value.to_glib_none_mut().0, this.to_glib())
}
}
#[cfg(any(feature = "v1_12", feature = "dox"))]
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum VideoFieldOrder {
@ -625,6 +814,63 @@ impl SetValue for VideoFormat {
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum VideoGammaMode {
None,
Remap,
#[doc(hidden)]
__Unknown(i32),
}
#[doc(hidden)]
impl ToGlib for VideoGammaMode {
type GlibType = gst_video_sys::GstVideoGammaMode;
fn to_glib(&self) -> gst_video_sys::GstVideoGammaMode {
match *self {
VideoGammaMode::None => gst_video_sys::GST_VIDEO_GAMMA_MODE_NONE,
VideoGammaMode::Remap => gst_video_sys::GST_VIDEO_GAMMA_MODE_REMAP,
VideoGammaMode::__Unknown(value) => value,
}
}
}
#[doc(hidden)]
impl FromGlib<gst_video_sys::GstVideoGammaMode> for VideoGammaMode {
fn from_glib(value: gst_video_sys::GstVideoGammaMode) -> Self {
skip_assert_initialized!();
match value {
0 => VideoGammaMode::None,
1 => VideoGammaMode::Remap,
value => VideoGammaMode::__Unknown(value),
}
}
}
impl StaticType for VideoGammaMode {
fn static_type() -> Type {
unsafe { from_glib(gst_video_sys::gst_video_gamma_mode_get_type()) }
}
}
impl<'a> FromValueOptional<'a> for VideoGammaMode {
unsafe fn from_value_optional(value: &Value) -> Option<Self> {
Some(FromValue::from_value(value))
}
}
impl<'a> FromValue<'a> for VideoGammaMode {
unsafe fn from_value(value: &Value) -> Self {
from_glib(gobject_sys::g_value_get_enum(value.to_glib_none().0))
}
}
impl SetValue for VideoGammaMode {
unsafe fn set_value(value: &mut Value, this: &Self) {
gobject_sys::g_value_set_enum(value.to_glib_none_mut().0, this.to_glib())
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum VideoInterlaceMode {
Progressive,
@ -691,6 +937,69 @@ impl SetValue for VideoInterlaceMode {
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum VideoMatrixMode {
Full,
InputOnly,
OutputOnly,
None,
#[doc(hidden)]
__Unknown(i32),
}
#[doc(hidden)]
impl ToGlib for VideoMatrixMode {
type GlibType = gst_video_sys::GstVideoMatrixMode;
fn to_glib(&self) -> gst_video_sys::GstVideoMatrixMode {
match *self {
VideoMatrixMode::Full => gst_video_sys::GST_VIDEO_MATRIX_MODE_FULL,
VideoMatrixMode::InputOnly => gst_video_sys::GST_VIDEO_MATRIX_MODE_INPUT_ONLY,
VideoMatrixMode::OutputOnly => gst_video_sys::GST_VIDEO_MATRIX_MODE_OUTPUT_ONLY,
VideoMatrixMode::None => gst_video_sys::GST_VIDEO_MATRIX_MODE_NONE,
VideoMatrixMode::__Unknown(value) => value,
}
}
}
#[doc(hidden)]
impl FromGlib<gst_video_sys::GstVideoMatrixMode> for VideoMatrixMode {
fn from_glib(value: gst_video_sys::GstVideoMatrixMode) -> Self {
skip_assert_initialized!();
match value {
0 => VideoMatrixMode::Full,
1 => VideoMatrixMode::InputOnly,
2 => VideoMatrixMode::OutputOnly,
3 => VideoMatrixMode::None,
value => VideoMatrixMode::__Unknown(value),
}
}
}
impl StaticType for VideoMatrixMode {
fn static_type() -> Type {
unsafe { from_glib(gst_video_sys::gst_video_matrix_mode_get_type()) }
}
}
impl<'a> FromValueOptional<'a> for VideoMatrixMode {
unsafe fn from_value_optional(value: &Value) -> Option<Self> {
Some(FromValue::from_value(value))
}
}
impl<'a> FromValue<'a> for VideoMatrixMode {
unsafe fn from_value(value: &Value) -> Self {
from_glib(gobject_sys::g_value_get_enum(value.to_glib_none().0))
}
}
impl SetValue for VideoMatrixMode {
unsafe fn set_value(value: &mut Value, this: &Self) {
gobject_sys::g_value_set_enum(value.to_glib_none_mut().0, this.to_glib())
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum VideoMultiviewFramePacking {
None,
@ -894,6 +1203,132 @@ impl SetValue for VideoMultiviewMode {
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum VideoPrimariesMode {
None,
MergeOnly,
Fast,
#[doc(hidden)]
__Unknown(i32),
}
#[doc(hidden)]
impl ToGlib for VideoPrimariesMode {
type GlibType = gst_video_sys::GstVideoPrimariesMode;
fn to_glib(&self) -> gst_video_sys::GstVideoPrimariesMode {
match *self {
VideoPrimariesMode::None => gst_video_sys::GST_VIDEO_PRIMARIES_MODE_NONE,
VideoPrimariesMode::MergeOnly => gst_video_sys::GST_VIDEO_PRIMARIES_MODE_MERGE_ONLY,
VideoPrimariesMode::Fast => gst_video_sys::GST_VIDEO_PRIMARIES_MODE_FAST,
VideoPrimariesMode::__Unknown(value) => value,
}
}
}
#[doc(hidden)]
impl FromGlib<gst_video_sys::GstVideoPrimariesMode> for VideoPrimariesMode {
fn from_glib(value: gst_video_sys::GstVideoPrimariesMode) -> Self {
skip_assert_initialized!();
match value {
0 => VideoPrimariesMode::None,
1 => VideoPrimariesMode::MergeOnly,
2 => VideoPrimariesMode::Fast,
value => VideoPrimariesMode::__Unknown(value),
}
}
}
impl StaticType for VideoPrimariesMode {
fn static_type() -> Type {
unsafe { from_glib(gst_video_sys::gst_video_primaries_mode_get_type()) }
}
}
impl<'a> FromValueOptional<'a> for VideoPrimariesMode {
unsafe fn from_value_optional(value: &Value) -> Option<Self> {
Some(FromValue::from_value(value))
}
}
impl<'a> FromValue<'a> for VideoPrimariesMode {
unsafe fn from_value(value: &Value) -> Self {
from_glib(gobject_sys::g_value_get_enum(value.to_glib_none().0))
}
}
impl SetValue for VideoPrimariesMode {
unsafe fn set_value(value: &mut Value, this: &Self) {
gobject_sys::g_value_set_enum(value.to_glib_none_mut().0, this.to_glib())
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum VideoResamplerMethod {
Nearest,
Linear,
Cubic,
Sinc,
Lanczos,
#[doc(hidden)]
__Unknown(i32),
}
#[doc(hidden)]
impl ToGlib for VideoResamplerMethod {
type GlibType = gst_video_sys::GstVideoResamplerMethod;
fn to_glib(&self) -> gst_video_sys::GstVideoResamplerMethod {
match *self {
VideoResamplerMethod::Nearest => gst_video_sys::GST_VIDEO_RESAMPLER_METHOD_NEAREST,
VideoResamplerMethod::Linear => gst_video_sys::GST_VIDEO_RESAMPLER_METHOD_LINEAR,
VideoResamplerMethod::Cubic => gst_video_sys::GST_VIDEO_RESAMPLER_METHOD_CUBIC,
VideoResamplerMethod::Sinc => gst_video_sys::GST_VIDEO_RESAMPLER_METHOD_SINC,
VideoResamplerMethod::Lanczos => gst_video_sys::GST_VIDEO_RESAMPLER_METHOD_LANCZOS,
VideoResamplerMethod::__Unknown(value) => value,
}
}
}
#[doc(hidden)]
impl FromGlib<gst_video_sys::GstVideoResamplerMethod> for VideoResamplerMethod {
fn from_glib(value: gst_video_sys::GstVideoResamplerMethod) -> Self {
skip_assert_initialized!();
match value {
0 => VideoResamplerMethod::Nearest,
1 => VideoResamplerMethod::Linear,
2 => VideoResamplerMethod::Cubic,
3 => VideoResamplerMethod::Sinc,
4 => VideoResamplerMethod::Lanczos,
value => VideoResamplerMethod::__Unknown(value),
}
}
}
impl StaticType for VideoResamplerMethod {
fn static_type() -> Type {
unsafe { from_glib(gst_video_sys::gst_video_resampler_method_get_type()) }
}
}
impl<'a> FromValueOptional<'a> for VideoResamplerMethod {
unsafe fn from_value_optional(value: &Value) -> Option<Self> {
Some(FromValue::from_value(value))
}
}
impl<'a> FromValue<'a> for VideoResamplerMethod {
unsafe fn from_value(value: &Value) -> Self {
from_glib(gobject_sys::g_value_get_enum(value.to_glib_none().0))
}
}
impl SetValue for VideoResamplerMethod {
unsafe fn set_value(value: &mut Value, this: &Self) {
gobject_sys::g_value_set_enum(value.to_glib_none_mut().0, this.to_glib())
}
}
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub enum VideoTileMode {
Unknown,

View file

@ -21,16 +21,23 @@ pub use self::video_overlay::VideoOverlayExt;
pub use self::video_overlay::{VideoOverlay, NONE_VIDEO_OVERLAY};
mod enums;
pub use self::enums::VideoAlphaMode;
#[cfg(any(feature = "v1_16", feature = "dox"))]
pub use self::enums::VideoCaptionType;
pub use self::enums::VideoChromaMode;
pub use self::enums::VideoColorMatrix;
pub use self::enums::VideoColorPrimaries;
pub use self::enums::VideoDitherMethod;
#[cfg(any(feature = "v1_12", feature = "dox"))]
pub use self::enums::VideoFieldOrder;
pub use self::enums::VideoFormat;
pub use self::enums::VideoGammaMode;
pub use self::enums::VideoInterlaceMode;
pub use self::enums::VideoMatrixMode;
pub use self::enums::VideoMultiviewFramePacking;
pub use self::enums::VideoMultiviewMode;
pub use self::enums::VideoPrimariesMode;
pub use self::enums::VideoResamplerMethod;
pub use self::enums::VideoTileMode;
pub use self::enums::VideoTransferFunction;

View file

@ -107,6 +107,35 @@ unsafe fn convert_sample_async_unsafe<F>(
);
}
pub fn convert_sample_future(
sample: &gst::Sample,
caps: &gst::Caps,
timeout: gst::ClockTime,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<gst::Sample, glib::Error>> + 'static>>
{
use futures_channel::oneshot;
use futures_util::future::lazy;
use futures_util::future::FutureExt;
let (sender, receiver) = oneshot::channel();
let sample = sample.clone();
let caps = caps.clone();
let future = lazy(move |_| {
assert!(
glib::MainContext::ref_thread_default().is_owner(),
"Spawning futures only allowed if the thread is owning the MainContext"
);
convert_sample_async(&sample, &caps, timeout, move |res| {
let _ = sender.send(res);
});
})
.then(|_| receiver.map(|res| res.expect("Sender dropped before callback was called")));
Box::pin(future)
}
#[cfg(test)]
mod tests {
use super::*;
@ -156,7 +185,6 @@ mod tests {
l.run();
let res = res_store.lock().unwrap().take().unwrap();
assert!(res.is_ok(), "Error {}", res.unwrap_err());
let res = res.unwrap();
let converted_out_caps = res.get_caps().unwrap();

View file

@ -18,6 +18,8 @@ extern crate glib_sys;
extern crate gobject_sys;
#[macro_use]
extern crate gstreamer as gst;
extern crate futures_channel;
extern crate futures_util;
extern crate gstreamer_base as gst_base;
extern crate gstreamer_base_sys as gst_base_sys;
extern crate gstreamer_sys as gst_sys;
@ -75,6 +77,8 @@ pub use video_buffer_pool::{
BUFFER_POOL_OPTION_VIDEO_ALIGNMENT, BUFFER_POOL_OPTION_VIDEO_GL_TEXTURE_UPLOAD_META,
BUFFER_POOL_OPTION_VIDEO_META,
};
pub mod video_converter;
pub use video_converter::{VideoConverter, VideoConverterConfig};
mod video_codec_frame;
mod video_decoder;

View file

@ -58,6 +58,14 @@ pub trait VideoDecoderImpl: VideoDecoderImplExt + ElementImpl + Send + Sync + 's
self.parent_set_format(element, state)
}
fn set_format_static(
&self,
element: &VideoDecoder,
state: &VideoCodecState<'static, Readable>,
) -> Result<(), gst::LoggableError> {
self.set_format(element, state)
}
fn parse(
&self,
element: &VideoDecoder,
@ -671,7 +679,7 @@ where
let wrap_state = VideoCodecState::<Readable>::new(state);
gst_panic_to_error!(&wrap, &instance.panicked(), false, {
match imp.set_format(&wrap, &wrap_state) {
match imp.set_format_static(&wrap, &wrap_state) {
Ok(()) => true,
Err(err) => {
err.log_with_object(&wrap);

View file

@ -53,6 +53,14 @@ pub trait VideoEncoderImpl: VideoEncoderImplExt + ElementImpl + Send + Sync + 's
self.parent_set_format(element, state)
}
fn set_format_static(
&self,
element: &VideoEncoder,
state: &VideoCodecState<'static, Readable>,
) -> Result<(), gst::LoggableError> {
self.set_format(element, state)
}
fn handle_frame(
&self,
element: &VideoEncoder,
@ -588,7 +596,7 @@ where
let wrap_state = VideoCodecState::<Readable>::new(state);
gst_panic_to_error!(&wrap, &instance.panicked(), false, {
match imp.set_format(&wrap, &wrap_state) {
match imp.set_format_static(&wrap, &wrap_state) {
Ok(()) => true,
Err(err) => {
err.log_with_object(&wrap);

View file

@ -0,0 +1,399 @@
// Copyright (C) 2020 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 gst_video_sys;
use glib;
use glib::translate::ToGlibPtr;
use gst;
use std::ops;
use std::ptr;
#[derive(Debug)]
pub struct VideoConverter(ptr::NonNull<gst_video_sys::GstVideoConverter>);
impl Drop for VideoConverter {
fn drop(&mut self) {
unsafe {
gst_video_sys::gst_video_converter_free(self.0.as_ptr());
}
}
}
unsafe impl Send for VideoConverter {}
unsafe impl Sync for VideoConverter {}
impl VideoConverter {
pub fn new(
in_info: &::VideoInfo,
out_info: &::VideoInfo,
config: Option<VideoConverterConfig>,
) -> Result<Self, glib::BoolError> {
if in_info.fps() != out_info.fps() {
return Err(glib_bool_error!("Can't do framerate conversion"));
}
if in_info.interlace_mode() != out_info.interlace_mode() {
return Err(glib_bool_error!("Can't do interlacing conversion"));
}
unsafe {
let ptr = gst_video_sys::gst_video_converter_new(
in_info.to_glib_none().0 as *mut _,
out_info.to_glib_none().0 as *mut _,
config.map(|s| s.0.into_ptr()).unwrap_or(ptr::null_mut()),
);
if ptr.is_null() {
Err(glib_bool_error!("Failed to create video converter"))
} else {
Ok(VideoConverter(ptr::NonNull::new_unchecked(ptr)))
}
}
}
pub fn get_config(&self) -> VideoConverterConfig {
unsafe {
VideoConverterConfig(
gst::StructureRef::from_glib_borrow(gst_video_sys::gst_video_converter_get_config(
self.0.as_ptr(),
))
.to_owned(),
)
}
}
pub fn set_config(&mut self, config: VideoConverterConfig) {
unsafe {
gst_video_sys::gst_video_converter_set_config(self.0.as_ptr(), config.0.into_ptr());
}
}
pub fn frame<T>(
&self,
src: &::VideoFrame<T>,
dest: &mut ::VideoFrame<::video_frame::Writable>,
) {
unsafe {
gst_video_sys::gst_video_converter_frame(
self.0.as_ptr(),
src.as_ptr(),
dest.as_mut_ptr(),
);
}
}
pub fn frame_ref<T>(
&self,
src: &::VideoFrameRef<T>,
dest: &mut ::VideoFrameRef<&mut gst::BufferRef>,
) {
unsafe {
gst_video_sys::gst_video_converter_frame(
self.0.as_ptr(),
src.as_ptr(),
dest.as_mut_ptr(),
);
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VideoConverterConfig(gst::Structure);
impl ops::Deref for VideoConverterConfig {
type Target = gst::StructureRef;
fn deref(&self) -> &gst::StructureRef {
self.0.deref()
}
}
impl ops::DerefMut for VideoConverterConfig {
fn deref_mut(&mut self) -> &mut gst::StructureRef {
self.0.deref_mut()
}
}
impl AsRef<gst::StructureRef> for VideoConverterConfig {
fn as_ref(&self) -> &gst::StructureRef {
self.0.as_ref()
}
}
impl AsMut<gst::StructureRef> for VideoConverterConfig {
fn as_mut(&mut self) -> &mut gst::StructureRef {
self.0.as_mut()
}
}
impl Default for VideoConverterConfig {
fn default() -> Self {
VideoConverterConfig::new()
}
}
impl VideoConverterConfig {
pub fn new() -> Self {
VideoConverterConfig(gst::Structure::new_empty("GstVideoConverter"))
}
pub fn set_resampler_method(&mut self, v: ::VideoResamplerMethod) {
self.0.set("GstVideoConverter.resampler-method", &v);
}
pub fn get_resampler_method(&self) -> ::VideoResamplerMethod {
self.0
.get_optional("GstVideoConverter.resampler-method")
.expect("Wrong type")
.unwrap_or(::VideoResamplerMethod::Cubic)
}
pub fn set_chroma_resampler_method(&mut self, v: ::VideoResamplerMethod) {
self.0.set("GstVideoConverter.chroma-resampler-method", &v);
}
pub fn get_chroma_resampler_method(&self) -> ::VideoResamplerMethod {
self.0
.get_optional("GstVideoConverter.chroma-resampler-method")
.expect("Wrong type")
.unwrap_or(::VideoResamplerMethod::Linear)
}
pub fn set_resampler_taps(&mut self, v: u32) {
self.0.set("GstVideoConverter.resampler-taps", &v);
}
pub fn get_resampler_taps(&self) -> u32 {
self.0
.get_optional("GstVideoConverter.resampler-taps")
.expect("Wrong type")
.unwrap_or(0)
}
pub fn set_dither_method(&mut self, v: ::VideoDitherMethod) {
self.0.set("GstVideoConverter.dither-method", &v);
}
pub fn get_dither_method(&self) -> ::VideoDitherMethod {
self.0
.get_optional("GstVideoConverter.dither-method")
.expect("Wrong type")
.unwrap_or(::VideoDitherMethod::Bayer)
}
pub fn set_dither_quantization(&mut self, v: u32) {
self.0.set("GstVideoConverter.dither-quantization", &v);
}
pub fn get_dither_quantization(&self) -> u32 {
self.0
.get_optional("GstVideoConverter.dither-quantization")
.expect("Wrong type")
.unwrap_or(1)
}
pub fn set_src_x(&mut self, v: i32) {
self.0.set("GstVideoConverter.src-x", &v);
}
pub fn get_src_x(&self) -> i32 {
self.0
.get_optional("GstVideoConverter.src-x")
.expect("Wrong type")
.unwrap_or(0)
}
pub fn set_src_y(&mut self, v: i32) {
self.0.set("GstVideoConverter.src-y", &v);
}
pub fn get_src_y(&self) -> i32 {
self.0
.get_optional("GstVideoConverter.src-y")
.expect("Wrong type")
.unwrap_or(0)
}
pub fn set_src_width(&mut self, v: Option<i32>) {
if let Some(v) = v {
self.0.set("GstVideoConverter.src-width", &v);
} else {
self.0.remove_field("GstVideoConverter.src-width");
}
}
pub fn get_src_width(&self) -> Option<i32> {
self.0
.get_optional("GstVideoConverter.src-width")
.expect("Wrong type")
}
pub fn set_src_height(&mut self, v: Option<i32>) {
if let Some(v) = v {
self.0.set("GstVideoConverter.src-height", &v);
} else {
self.0.remove_field("GstVideoConverter.src-height");
}
}
pub fn get_src_height(&self) -> Option<i32> {
self.0
.get_optional("GstVideoConverter.src-height")
.expect("Wrong type")
}
pub fn set_dest_x(&mut self, v: i32) {
self.0.set("GstVideoConverter.dest-x", &v);
}
pub fn get_dest_x(&self) -> i32 {
self.0
.get_optional("GstVideoConverter.dest-x")
.expect("Wrong type")
.unwrap_or(0)
}
pub fn set_dest_y(&mut self, v: i32) {
self.0.set("GstVideoConverter.dest-y", &v);
}
pub fn get_dest_y(&self) -> i32 {
self.0
.get_optional("GstVideoConverter.dest-y")
.expect("Wrong type")
.unwrap_or(0)
}
pub fn set_dest_width(&mut self, v: Option<i32>) {
if let Some(v) = v {
self.0.set("GstVideoConverter.dest-width", &v);
} else {
self.0.remove_field("GstVideoConverter.dest-width");
}
}
pub fn get_dest_width(&self) -> Option<i32> {
self.0
.get_optional("GstVideoConverter.dest-width")
.expect("Wrong type")
}
pub fn set_dest_height(&mut self, v: Option<i32>) {
if let Some(v) = v {
self.0.set("GstVideoConverter.dest-height", &v);
} else {
self.0.remove_field("GstVideoConverter.dest-height");
}
}
pub fn get_dest_height(&self) -> Option<i32> {
self.0
.get_optional("GstVideoConverter.dest-height")
.expect("Wrong type")
}
pub fn set_fill_border(&mut self, v: bool) {
self.0.set("GstVideoConverter.fill-border", &v);
}
pub fn get_fill_border(&self) -> bool {
self.0
.get_optional("GstVideoConverter.fill-border")
.expect("Wrong type")
.unwrap_or(true)
}
pub fn set_alpha_value(&mut self, v: f64) {
self.0.set("GstVideoConverter.alpha-value", &v);
}
pub fn get_alpha_value(&self) -> f64 {
self.0
.get_optional("GstVideoConverter.alpha-value")
.expect("Wrong type")
.unwrap_or(1.0)
}
pub fn set_alpha_mode(&mut self, v: ::VideoAlphaMode) {
self.0.set("GstVideoConverter.alpha-mode", &v);
}
pub fn get_alpha_mode(&self) -> ::VideoAlphaMode {
self.0
.get_optional("GstVideoConverter.alpha-mode")
.expect("Wrong type")
.unwrap_or(::VideoAlphaMode::Copy)
}
pub fn set_border_argb(&mut self, v: u32) {
self.0.set("GstVideoConverter.border-argb", &v);
}
pub fn get_border_argb(&self) -> u32 {
self.0
.get_optional("GstVideoConverter.border-argb")
.expect("Wrong type")
.unwrap_or(0xff_00_00_00)
}
pub fn set_chroma_mode(&mut self, v: ::VideoChromaMode) {
self.0.set("GstVideoConverter.chroma-mode", &v);
}
pub fn get_chroma_mode(&self) -> ::VideoChromaMode {
self.0
.get_optional("GstVideoConverter.chroma-mode")
.expect("Wrong type")
.unwrap_or(::VideoChromaMode::Full)
}
pub fn set_matrix_mode(&mut self, v: ::VideoMatrixMode) {
self.0.set("GstVideoConverter.matrix-mode", &v);
}
pub fn get_matrix_mode(&self) -> ::VideoMatrixMode {
self.0
.get_optional("GstVideoConverter.matrix-mode")
.expect("Wrong type")
.unwrap_or(::VideoMatrixMode::Full)
}
pub fn set_gamma_mode(&mut self, v: ::VideoGammaMode) {
self.0.set("GstVideoConverter.gamma-mode", &v);
}
pub fn get_gamma_mode(&self) -> ::VideoGammaMode {
self.0
.get_optional("GstVideoConverter.gamma-mode")
.expect("Wrong type")
.unwrap_or(::VideoGammaMode::None)
}
pub fn set_primaries_mode(&mut self, v: ::VideoPrimariesMode) {
self.0.set("GstVideoConverter.primaries-mode", &v);
}
pub fn get_primaries_mode(&self) -> ::VideoPrimariesMode {
self.0
.get_optional("GstVideoConverter.primaries-mode")
.expect("Wrong type")
.unwrap_or(::VideoPrimariesMode::None)
}
pub fn set_threads(&mut self, v: u32) {
self.0.set("GstVideoConverter.threads", &v);
}
pub fn get_threads(&self) -> u32 {
self.0
.get_optional("GstVideoConverter.threads")
.expect("Wrong type")
.unwrap_or(1)
}
}

View file

@ -57,7 +57,7 @@ pub trait VideoDecoderExtManual: 'static {
fn get_latency(&self) -> (gst::ClockTime, gst::ClockTime);
fn set_latency(&self, min_latency: gst::ClockTime, max_latency: gst::ClockTime);
fn get_output_state(&self) -> Option<VideoCodecState<Readable>>;
fn get_output_state(&self) -> Option<VideoCodecState<'static, Readable>>;
fn set_output_state(
&self,
fmt: VideoFormat,
@ -233,7 +233,7 @@ impl<O: IsA<VideoDecoder>> VideoDecoderExtManual for O {
}
}
fn get_output_state(&self) -> Option<VideoCodecState<Readable>> {
fn get_output_state(&self) -> Option<VideoCodecState<'static, Readable>> {
let state = unsafe {
gst_video_sys::gst_video_decoder_get_output_state(self.as_ref().to_glib_none().0)
};

View file

@ -41,7 +41,7 @@ pub trait VideoEncoderExtManual: 'static {
fn get_latency(&self) -> (gst::ClockTime, gst::ClockTime);
fn set_latency(&self, min_latency: gst::ClockTime, max_latency: gst::ClockTime);
fn get_output_state(&self) -> Option<VideoCodecState<Readable>>;
fn get_output_state(&self) -> Option<VideoCodecState<'static, Readable>>;
fn set_output_state(
&self,
caps: gst::Caps,
@ -166,7 +166,7 @@ impl<O: IsA<VideoEncoder>> VideoEncoderExtManual for O {
}
}
fn get_output_state(&self) -> Option<VideoCodecState<Readable>> {
fn get_output_state(&self) -> Option<VideoCodecState<'static, Readable>> {
let state = unsafe {
gst_video_sys::gst_video_encoder_get_output_state(self.as_ref().to_glib_none().0)
};

View file

@ -14,6 +14,7 @@ use glib::translate::{from_glib, ToGlibPtr};
use gst;
use gst::miniobject::MiniObject;
use std::fmt;
use std::marker::PhantomData;
use std::mem;
use std::ops;
@ -23,37 +24,50 @@ use std::slice;
pub enum Readable {}
pub enum Writable {}
#[derive(Debug)]
pub struct VideoFrame<T>(
gst_video_sys::GstVideoFrame,
Option<gst::Buffer>,
::VideoInfo,
PhantomData<T>,
);
pub struct VideoFrame<T> {
frame: gst_video_sys::GstVideoFrame,
buffer: Option<gst::Buffer>,
info: ::VideoInfo,
phantom: PhantomData<T>,
}
unsafe impl<T> Send for VideoFrame<T> {}
unsafe impl<T> Sync for VideoFrame<T> {}
impl<T> fmt::Debug for VideoFrame<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("VideoFrame")
.field("frame", &self.frame)
.field("buffer", &self.buffer)
.field("info", &self.info)
.field("phantom", &self.phantom)
.finish()
}
}
impl<T> VideoFrame<T> {
pub fn info(&self) -> &::VideoInfo {
&self.2
&self.info
}
pub fn flags(&self) -> ::VideoFrameFlags {
from_glib(self.0.flags)
from_glib(self.frame.flags)
}
pub fn id(&self) -> i32 {
self.0.id
self.frame.id
}
pub fn into_buffer(mut self) -> gst::Buffer {
self.1.take().unwrap()
self.buffer.take().unwrap()
}
pub fn copy(&self, dest: &mut VideoFrame<Writable>) -> Result<(), glib::BoolError> {
unsafe {
let res: bool = from_glib(gst_video_sys::gst_video_frame_copy(&mut dest.0, &self.0));
let res: bool = from_glib(gst_video_sys::gst_video_frame_copy(
&mut dest.frame,
&self.frame,
));
if res {
Ok(())
} else {
@ -71,8 +85,8 @@ impl<T> VideoFrame<T> {
unsafe {
let res: bool = from_glib(gst_video_sys::gst_video_frame_copy_plane(
&mut dest.0,
&self.0,
&mut dest.frame,
&self.frame,
plane,
));
if res {
@ -136,7 +150,7 @@ impl<T> VideoFrame<T> {
}
pub fn buffer(&self) -> &gst::BufferRef {
unsafe { gst::BufferRef::from_ptr(self.0.buffer) }
unsafe { gst::BufferRef::from_ptr(self.frame.buffer) }
}
pub fn plane_data(&self, plane: u32) -> Result<&[u8], glib::BoolError> {
@ -149,7 +163,10 @@ impl<T> VideoFrame<T> {
// Just get the palette
if format_info.has_palette() && plane == 1 {
unsafe {
return Ok(slice::from_raw_parts(self.0.data[1] as *const u8, 256 * 4));
return Ok(slice::from_raw_parts(
self.frame.data[1] as *const u8,
256 * 4,
));
}
}
@ -160,7 +177,7 @@ impl<T> VideoFrame<T> {
unsafe {
Ok(slice::from_raw_parts(
self.0.data[plane as usize] as *const u8,
self.frame.data[plane as usize] as *const u8,
(w * h) as usize,
))
}
@ -169,14 +186,34 @@ impl<T> VideoFrame<T> {
pub unsafe fn from_glib_full(frame: gst_video_sys::GstVideoFrame) -> Self {
let info = ::VideoInfo(ptr::read(&frame.info));
let buffer = gst::Buffer::from_glib_none(frame.buffer);
VideoFrame(frame, Some(buffer), info, PhantomData)
VideoFrame {
frame,
buffer: Some(buffer),
info,
phantom: PhantomData,
}
}
pub fn as_video_frame_ref(&self) -> VideoFrameRef<&gst::BufferRef> {
let frame = unsafe { ptr::read(&self.frame) };
let info = self.info.clone();
VideoFrameRef {
frame,
buffer: Some(self.buffer()),
info,
borrowed: true,
}
}
pub fn as_ptr(&self) -> *const gst_video_sys::GstVideoFrame {
&self.frame
}
}
impl<T> Drop for VideoFrame<T> {
fn drop(&mut self) {
unsafe {
gst_video_sys::gst_video_frame_unmap(&mut self.0);
gst_video_sys::gst_video_frame_unmap(&mut self.frame);
}
}
}
@ -202,7 +239,12 @@ impl VideoFrame<Readable> {
} else {
let frame = frame.assume_init();
let info = ::VideoInfo(ptr::read(&frame.info));
Ok(VideoFrame(frame, Some(buffer), info, PhantomData))
Ok(VideoFrame {
frame,
buffer: Some(buffer),
info,
phantom: PhantomData,
})
}
}
}
@ -229,20 +271,15 @@ impl VideoFrame<Readable> {
} else {
let frame = frame.assume_init();
let info = ::VideoInfo(ptr::read(&frame.info));
Ok(VideoFrame(frame, Some(buffer), info, PhantomData))
Ok(VideoFrame {
frame,
buffer: Some(buffer),
info,
phantom: PhantomData,
})
}
}
}
pub fn as_video_frame_ref(&self) -> VideoFrameRef<&gst::BufferRef> {
let vframe = unsafe { ptr::read(&self.0) };
let info = self.2.clone();
VideoFrameRef(vframe, Some(self.buffer()), info, true)
}
pub fn as_ptr(&self) -> *const gst_video_sys::GstVideoFrame {
&self.0
}
}
impl VideoFrame<Writable> {
@ -268,7 +305,12 @@ impl VideoFrame<Writable> {
} else {
let frame = frame.assume_init();
let info = ::VideoInfo(ptr::read(&frame.info));
Ok(VideoFrame(frame, Some(buffer), info, PhantomData))
Ok(VideoFrame {
frame,
buffer: Some(buffer),
info,
phantom: PhantomData,
})
}
}
}
@ -297,13 +339,18 @@ impl VideoFrame<Writable> {
} else {
let frame = frame.assume_init();
let info = ::VideoInfo(ptr::read(&frame.info));
Ok(VideoFrame(frame, Some(buffer), info, PhantomData))
Ok(VideoFrame {
frame,
buffer: Some(buffer),
info,
phantom: PhantomData,
})
}
}
}
pub fn buffer_mut(&mut self) -> &mut gst::BufferRef {
unsafe { gst::BufferRef::from_mut_ptr(self.0.buffer) }
unsafe { gst::BufferRef::from_mut_ptr(self.frame.buffer) }
}
pub fn plane_data_mut(&mut self, plane: u32) -> Result<&mut [u8], glib::BoolError> {
@ -317,7 +364,7 @@ impl VideoFrame<Writable> {
if format_info.has_palette() && plane == 1 {
unsafe {
return Ok(slice::from_raw_parts_mut(
self.0.data[1] as *mut u8,
self.frame.data[1] as *mut u8,
256 * 4,
));
}
@ -330,102 +377,47 @@ impl VideoFrame<Writable> {
unsafe {
Ok(slice::from_raw_parts_mut(
self.0.data[plane as usize] as *mut u8,
self.frame.data[plane as usize] as *mut u8,
(w * h) as usize,
))
}
}
pub fn as_mut_video_frame_ref(&mut self) -> VideoFrameRef<&mut gst::BufferRef> {
let vframe = unsafe { ptr::read(&self.0) };
let info = self.2.clone();
VideoFrameRef(vframe, Some(self.buffer_mut()), info, true)
let frame = unsafe { ptr::read(&self.frame) };
let info = self.info.clone();
VideoFrameRef {
frame,
buffer: Some(self.buffer_mut()),
info,
borrowed: true,
}
}
pub fn as_mut_ptr(&mut self) -> *mut gst_video_sys::GstVideoFrame {
&mut self.0
&mut self.frame
}
}
#[derive(Debug)]
pub struct VideoFrameRef<T>(gst_video_sys::GstVideoFrame, Option<T>, ::VideoInfo, bool);
impl<'a> VideoFrameRef<&'a gst::BufferRef> {
pub fn as_ptr(&self) -> *const gst_video_sys::GstVideoFrame {
&self.0
}
pub unsafe fn from_glib_borrow(frame: *const gst_video_sys::GstVideoFrame) -> Self {
assert!(!frame.is_null());
let frame = ptr::read(frame);
let info = ::VideoInfo(ptr::read(&frame.info));
let buffer = gst::BufferRef::from_ptr(frame.buffer);
VideoFrameRef(frame, Some(buffer), info, false)
}
pub fn from_buffer_ref_readable<'b>(
buffer: &'a gst::BufferRef,
info: &'b ::VideoInfo,
) -> Result<VideoFrameRef<&'a gst::BufferRef>, glib::BoolError> {
skip_assert_initialized!();
unsafe {
let mut frame = mem::MaybeUninit::zeroed();
let res: bool = from_glib(gst_video_sys::gst_video_frame_map(
frame.as_mut_ptr(),
info.to_glib_none().0 as *mut _,
buffer.as_mut_ptr(),
gst_video_sys::GST_VIDEO_FRAME_MAP_FLAG_NO_REF | gst_sys::GST_MAP_READ,
));
if !res {
Err(glib_bool_error!("Failed to map VideoFrame"))
} else {
let frame = frame.assume_init();
let info = ::VideoInfo(ptr::read(&frame.info));
Ok(VideoFrameRef(frame, Some(buffer), info, false))
}
}
}
pub fn from_buffer_ref_id_readable<'b>(
buffer: &'a gst::BufferRef,
id: i32,
info: &'b ::VideoInfo,
) -> Result<VideoFrameRef<&'a gst::BufferRef>, glib::BoolError> {
skip_assert_initialized!();
unsafe {
let mut frame = mem::MaybeUninit::zeroed();
let res: bool = from_glib(gst_video_sys::gst_video_frame_map_id(
frame.as_mut_ptr(),
info.to_glib_none().0 as *mut _,
buffer.as_mut_ptr(),
id,
gst_video_sys::GST_VIDEO_FRAME_MAP_FLAG_NO_REF | gst_sys::GST_MAP_READ,
));
if !res {
Err(glib_bool_error!("Failed to map VideoFrame"))
} else {
let frame = frame.assume_init();
let info = ::VideoInfo(ptr::read(&frame.info));
Ok(VideoFrameRef(frame, Some(buffer), info, false))
}
}
}
pub struct VideoFrameRef<T> {
frame: gst_video_sys::GstVideoFrame,
buffer: Option<T>,
info: ::VideoInfo,
borrowed: bool,
}
impl<T> VideoFrameRef<T> {
pub fn info(&self) -> &::VideoInfo {
&self.2
&self.info
}
pub fn flags(&self) -> ::VideoFrameFlags {
from_glib(self.0.flags)
from_glib(self.frame.flags)
}
pub fn id(&self) -> i32 {
self.0.id
self.frame.id
}
pub fn copy(
@ -433,7 +425,10 @@ impl<'a> VideoFrameRef<&'a gst::BufferRef> {
dest: &mut VideoFrameRef<&mut gst::BufferRef>,
) -> Result<(), glib::BoolError> {
unsafe {
let res: bool = from_glib(gst_video_sys::gst_video_frame_copy(&mut dest.0, &self.0));
let res: bool = from_glib(gst_video_sys::gst_video_frame_copy(
&mut dest.frame,
&self.frame,
));
if res {
Ok(())
} else {
@ -451,8 +446,8 @@ impl<'a> VideoFrameRef<&'a gst::BufferRef> {
unsafe {
let res: bool = from_glib(gst_video_sys::gst_video_frame_copy_plane(
&mut dest.0,
&self.0,
&mut dest.frame,
&self.frame,
plane,
));
if res {
@ -515,10 +510,6 @@ impl<'a> VideoFrameRef<&'a gst::BufferRef> {
self.info().offset()
}
pub fn buffer(&self) -> &gst::BufferRef {
self.1.as_ref().unwrap()
}
pub fn plane_data(&self, plane: u32) -> Result<&[u8], glib::BoolError> {
if plane >= self.n_planes() {
return Err(glib_bool_error!("Plane index higher than number of planes"));
@ -529,7 +520,10 @@ impl<'a> VideoFrameRef<&'a gst::BufferRef> {
// Just get the palette
if format_info.has_palette() && plane == 1 {
unsafe {
return Ok(slice::from_raw_parts(self.0.data[1] as *const u8, 256 * 4));
return Ok(slice::from_raw_parts(
self.frame.data[1] as *const u8,
256 * 4,
));
}
}
@ -540,11 +534,97 @@ impl<'a> VideoFrameRef<&'a gst::BufferRef> {
unsafe {
Ok(slice::from_raw_parts(
self.0.data[plane as usize] as *const u8,
self.frame.data[plane as usize] as *const u8,
(w * h) as usize,
))
}
}
pub fn as_ptr(&self) -> *const gst_video_sys::GstVideoFrame {
&self.frame
}
}
impl<'a> VideoFrameRef<&'a gst::BufferRef> {
pub unsafe fn from_glib_borrow(frame: *const gst_video_sys::GstVideoFrame) -> Self {
assert!(!frame.is_null());
let frame = ptr::read(frame);
let info = ::VideoInfo(ptr::read(&frame.info));
let buffer = gst::BufferRef::from_ptr(frame.buffer);
VideoFrameRef {
frame,
buffer: Some(buffer),
info,
borrowed: false,
}
}
pub fn from_buffer_ref_readable<'b>(
buffer: &'a gst::BufferRef,
info: &'b ::VideoInfo,
) -> Result<VideoFrameRef<&'a gst::BufferRef>, glib::BoolError> {
skip_assert_initialized!();
unsafe {
let mut frame = mem::MaybeUninit::zeroed();
let res: bool = from_glib(gst_video_sys::gst_video_frame_map(
frame.as_mut_ptr(),
info.to_glib_none().0 as *mut _,
buffer.as_mut_ptr(),
gst_video_sys::GST_VIDEO_FRAME_MAP_FLAG_NO_REF | gst_sys::GST_MAP_READ,
));
if !res {
Err(glib_bool_error!("Failed to map VideoFrame"))
} else {
let frame = frame.assume_init();
let info = ::VideoInfo(ptr::read(&frame.info));
Ok(VideoFrameRef {
frame,
buffer: Some(buffer),
info,
borrowed: false,
})
}
}
}
pub fn from_buffer_ref_id_readable<'b>(
buffer: &'a gst::BufferRef,
id: i32,
info: &'b ::VideoInfo,
) -> Result<VideoFrameRef<&'a gst::BufferRef>, glib::BoolError> {
skip_assert_initialized!();
unsafe {
let mut frame = mem::MaybeUninit::zeroed();
let res: bool = from_glib(gst_video_sys::gst_video_frame_map_id(
frame.as_mut_ptr(),
info.to_glib_none().0 as *mut _,
buffer.as_mut_ptr(),
id,
gst_video_sys::GST_VIDEO_FRAME_MAP_FLAG_NO_REF | gst_sys::GST_MAP_READ,
));
if !res {
Err(glib_bool_error!("Failed to map VideoFrame"))
} else {
let frame = frame.assume_init();
let info = ::VideoInfo(ptr::read(&frame.info));
Ok(VideoFrameRef {
frame,
buffer: Some(buffer),
info,
borrowed: false,
})
}
}
}
pub fn buffer(&self) -> &gst::BufferRef {
self.buffer.as_ref().unwrap()
}
}
impl<'a> VideoFrameRef<&'a mut gst::BufferRef> {
@ -554,7 +634,12 @@ impl<'a> VideoFrameRef<&'a mut gst::BufferRef> {
let frame = ptr::read(frame);
let info = ::VideoInfo(ptr::read(&frame.info));
let buffer = gst::BufferRef::from_mut_ptr(frame.buffer);
VideoFrameRef(frame, Some(buffer), info, false)
VideoFrameRef {
frame,
buffer: Some(buffer),
info,
borrowed: false,
}
}
pub fn from_buffer_ref_writable<'b>(
@ -579,7 +664,12 @@ impl<'a> VideoFrameRef<&'a mut gst::BufferRef> {
} else {
let frame = frame.assume_init();
let info = ::VideoInfo(ptr::read(&frame.info));
Ok(VideoFrameRef(frame, Some(buffer), info, false))
Ok(VideoFrameRef {
frame,
buffer: Some(buffer),
info,
borrowed: false,
})
}
}
}
@ -608,13 +698,18 @@ impl<'a> VideoFrameRef<&'a mut gst::BufferRef> {
} else {
let frame = frame.assume_init();
let info = ::VideoInfo(ptr::read(&frame.info));
Ok(VideoFrameRef(frame, Some(buffer), info, false))
Ok(VideoFrameRef {
frame,
buffer: Some(buffer),
info,
borrowed: false,
})
}
}
}
pub fn buffer_mut(&mut self) -> &mut gst::BufferRef {
self.1.as_mut().unwrap()
self.buffer.as_mut().unwrap()
}
pub fn plane_data_mut(&mut self, plane: u32) -> Result<&mut [u8], glib::BoolError> {
@ -628,7 +723,7 @@ impl<'a> VideoFrameRef<&'a mut gst::BufferRef> {
if format_info.has_palette() && plane == 1 {
unsafe {
return Ok(slice::from_raw_parts_mut(
self.0.data[1] as *mut u8,
self.frame.data[1] as *mut u8,
256 * 4,
));
}
@ -641,14 +736,14 @@ impl<'a> VideoFrameRef<&'a mut gst::BufferRef> {
unsafe {
Ok(slice::from_raw_parts_mut(
self.0.data[plane as usize] as *mut u8,
self.frame.data[plane as usize] as *mut u8,
(w * h) as usize,
))
}
}
pub fn as_mut_ptr(&mut self) -> *mut gst_video_sys::GstVideoFrame {
&mut self.0
&mut self.frame
}
}
@ -668,9 +763,9 @@ unsafe impl<T> Sync for VideoFrameRef<T> {}
impl<T> Drop for VideoFrameRef<T> {
fn drop(&mut self) {
if !self.3 {
if !self.borrowed {
unsafe {
gst_video_sys::gst_video_frame_unmap(&mut self.0);
gst_video_sys::gst_video_frame_unmap(&mut self.frame);
}
}
}

View file

@ -267,7 +267,38 @@ impl<'a> VideoInfoBuilder<'a> {
unsafe {
let mut info = mem::MaybeUninit::uninit();
#[cfg(not(feature = "v1_16"))]
#[cfg(not(feature = "v1_12"))]
let res: bool = {
// The bool return value is new with 1.11.1, see
// https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/commit/17cdd369e6f2f73329d27dfceb50011f40f1ceb0
let res = if gst::version() < (1, 11, 1, 0) {
gst_video_sys::gst_video_info_set_format(
info.as_mut_ptr(),
self.format.to_glib(),
self.width,
self.height,
);
true
} else {
from_glib(gst_video_sys::gst_video_info_set_format(
info.as_mut_ptr(),
self.format.to_glib(),
self.width,
self.height,
))
};
if res {
if let Some(interlace_mode) = self.interlace_mode {
let info = info.as_mut_ptr();
(*info).interlace_mode = interlace_mode.to_glib();
}
}
res
};
#[cfg(all(feature = "v1_12", not(feature = "v1_16")))]
let res: bool = {
let res = from_glib(gst_video_sys::gst_video_info_set_format(
info.as_mut_ptr(),
@ -710,8 +741,23 @@ impl VideoInfo {
}
}
#[cfg(any(feature = "v1_12", feature = "dox"))]
pub fn align(&mut self, align: &mut ::VideoAlignment) -> bool {
#[cfg(not(feature = "v1_12"))]
unsafe {
// The bool return value is new with 1.11.1, see
// https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/commit/17cdd369e6f2f73329d27dfceb50011f40f1ceb0
if gst::version() < (1, 11, 1, 0) {
gst_video_sys::gst_video_info_align(&mut self.0, &mut align.0);
true
} else {
from_glib(gst_video_sys::gst_video_info_align(
&mut self.0,
&mut align.0,
))
}
}
#[cfg(feature = "v1_12")]
unsafe {
from_glib(gst_video_sys::gst_video_info_align(
&mut self.0,
@ -973,13 +1019,13 @@ mod tests {
.expect("Failed to create VideoInfo");
assert_eq!(info.stride(), [1920, 1920]);
assert_eq!(info.offset(), [0, 2073600]);
assert_eq!(info.offset(), [0, 2_073_600]);
let mut align = ::VideoAlignment::new(0, 0, 0, 8, &[0; VIDEO_MAX_PLANES]);
assert!(info.align(&mut align));
assert_eq!(info.stride(), [1928, 1928]);
assert_eq!(info.offset(), [0, 2082240]);
assert_eq!(info.offset(), [0, 2_082_240]);
}
#[cfg(any(feature = "v1_12", feature = "dox"))]

View file

@ -5,6 +5,75 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +685,11 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...HEAD
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer-webrtc"
version = "0.15.0"
version = "0.15.4"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer WebRTC library"
@ -14,13 +14,13 @@ build = "build.rs"
[dependencies]
libc = "0.2"
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
gstreamer-webrtc-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys" }
glib = { git = "https://github.com/gtk-rs/glib" }
gstreamer = { path = "../gstreamer", features = ["v1_14"] }
gstreamer-sdp = { path = "../gstreamer-sdp", features = ["v1_14"] }
glib-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_8"] }
gstreamer-webrtc-sys = { version = "0.8" }
glib = { version = "0.9" }
gstreamer = { version = "0.15", path = "../gstreamer", features = ["v1_14"] }
gstreamer-sdp = { version = "0.15", path = "../gstreamer-sdp", features = ["v1_14"] }
[build-dependencies]
rustdoc-stripper = { version = "0.1", optional = true }

View file

@ -5,6 +5,75 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.15.4] - 2020-03-09
### Fixed
- Allow logging any `glib::Object` and not just `gst::Object`
- Fix floating reference handling in `RTSPMedia::take_pipeline()`
- Hold `GMutex` guards for the remainder of the function and warn if they're
directly dropped
- Work around empty/any caps handling bugs in `Caps::fixate()`
### Added
- Add `BaseTransform::prepare_output_buffer()` subclassing support
- `RTSPServer`, `RTSPClient`, `RTSPMedia` and `RTSPMediaFactory` subclassing
support
- Handle panicking in `appsrc`/`appsink` callbacks by posting an error message
instead of killing the process
## [0.15.3] - 2020-02-15
### Fixed
- `UniqueFlowCombiner::clear()` should take a mutable reference.
- `AudioStreamAlign` doesn't require mutable references for getters anymore.
- Don't use bool return value of `gst_video_info_set_format()` and
`gst_video_info_align()` with GStreamer < 1.11.1 as it returned void back
then. We'd otherwise use some random value.
- Make `VideoInfo::align()` is available since 1.8.
- Fix changing/clearing of `AppSrc`, `AppSink` callbacks and `Bus` sync
handler. Before 1.16.3 this was not thread-safe and caused crashes. When
running with older versions changing them causes a panic now and unsetting
the bus sync handler has not effect. With newer versions it works correctly.
### Added
- Add `Clone` impls for `BufferPoolConfig` and `PlayerConfig`.
- Add `VideoConverter` bindings.
- Add `Future`s variant for `gst::Promise` constructor.
- Add `Future`s variant for `gst_video::convert_sample_async()`.
- Add `submit_input_buffer()`, `generate_output()`, `before_transform()`,
`copy_metadata()` and `transform_meta()` virtual method support for
`BaseTransform`.
- Add `AppSink` `Stream` adapter and `AppSrc` `Sink` adapter for integrating
both into Rust async contexts.
### Changed
- More generic implementations of `VideoFrame` / `VideoFrameRef` functions to
allow usage in more generic contexts.
## [0.15.2] - 2020-01-30
### Fixed
- Fix another race condition in the `gst::Bus` `Stream` that could cause it to
not wake up although a message is available.
## [0.15.1] - 2020-01-23
### Added
- Use static inner lifetime for `VideoCodecState<Readable>` so that it can be
stored safely on the heap.
- Getters/setters for `BinFlags` on `gst::Bin`.
- `gst::Caps::builder_full()` for building caps with multiple structures
conveniently.
- `gst::Element::call_async_future()` for asynchronously spawning a closure
and returning a `Future` for awaiting its return value.
### Fixed
- Various clippy warnings.
- Getters/setters for `PadFlags` on `gst::Pad` now provide the correct
behaviour.
- Take mutex before popping messages in the `gst::Bus` `Stream` to close a
small race condition that could cause it to not be woken up.
- `gst::ChildProxy` implementers do not have to provide `child_added()` and
`child_removed()` functions anymore but these are optional now.
- Manually implement `Debug` impls for various generic types where to `Debug`
impl should not depend on their type parameters also implementing `Debug`.
## [0.15.0] - 2019-12-18
### Added
- `StructureRef::get_optional()` for returning `None` if the field does not
@ -616,7 +685,11 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
(< 0.8.0) of the bindings can be found [here](https://github.com/arturoc/gstreamer1.0-rs).
The API of the two is incompatible.
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.4...HEAD
[0.15.4]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.3...0.15.4
[0.15.3]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.2...0.15.3
[0.15.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.1...0.15.2
[0.15.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.15.0...0.15.1
[0.15.0]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.2...0.15.0
[0.14.2]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.1...0.14.2
[0.14.1]: https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/compare/0.14.0...0.14.1

View file

@ -1,6 +1,6 @@
[package]
name = "gstreamer"
version = "0.15.0"
version = "0.15.4"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer"
@ -16,13 +16,15 @@ build = "build.rs"
bitflags = "1.0"
cfg-if = "0.1"
libc = "0.2"
glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"] }
glib = { git = "https://github.com/gtk-rs/glib" }
glib-sys = { version = "0.9" }
gobject-sys = { version = "0.9" }
gstreamer-sys = { version = "0.8", features = ["v1_8"] }
glib = { version = "0.9" }
num-rational = { version = "0.2", default-features = false, features = [] }
lazy_static = "1.0"
futures-core = "0.3"
futures-util = "0.3"
futures-channel = "0.3"
muldiv = "0.2"
serde = { version = "1.0", optional = true }
serde_bytes = { version = "0.11", optional = true }
@ -35,6 +37,7 @@ rustdoc-stripper = { version = "0.1", optional = true }
[dev-dependencies]
ron = "0.5"
serde_json = "1.0"
futures-executor = "0.3.1"
[features]
default = []

View file

@ -7,6 +7,7 @@
// except according to those terms.
use Bin;
use BinFlags;
use Element;
use LoggableError;
@ -48,6 +49,12 @@ pub trait GstBinExtManual: 'static {
details: ::DebugGraphDetails,
file_name: Q,
);
fn set_bin_flags(&self, flags: BinFlags);
fn unset_bin_flags(&self, flags: BinFlags);
fn get_bin_flags(&self) -> BinFlags;
}
impl<O: IsA<Bin>> GstBinExtManual for O {
@ -150,7 +157,7 @@ impl<O: IsA<Bin>> GstBinExtManual for O {
fn get_children(&self) -> Vec<Element> {
unsafe {
let bin: &gst_sys::GstBin = &*(self.as_ptr() as *const _);
::utils::MutexGuard::lock(&bin.element.object.lock);
let _guard = ::utils::MutexGuard::lock(&bin.element.object.lock);
FromGlibPtrContainer::from_glib_none(bin.children)
}
}
@ -170,6 +177,30 @@ impl<O: IsA<Bin>> GstBinExtManual for O {
) {
::debug_bin_to_dot_file_with_ts(self, details, file_name)
}
fn set_bin_flags(&self, flags: BinFlags) {
unsafe {
let ptr: *mut gst_sys::GstObject = self.as_ptr() as *mut _;
let _guard = ::utils::MutexGuard::lock(&(*ptr).lock);
(*ptr).flags |= flags.to_glib();
}
}
fn unset_bin_flags(&self, flags: BinFlags) {
unsafe {
let ptr: *mut gst_sys::GstObject = self.as_ptr() as *mut _;
let _guard = ::utils::MutexGuard::lock(&(*ptr).lock);
(*ptr).flags &= !flags.to_glib();
}
}
fn get_bin_flags(&self) -> BinFlags {
unsafe {
let ptr: *mut gst_sys::GstObject = self.as_ptr() as *mut _;
let _guard = ::utils::MutexGuard::lock(&(*ptr).lock);
from_glib((*ptr).flags)
}
}
}
unsafe extern "C" fn do_latency_trampoline<

View file

@ -596,7 +596,6 @@ impl BufferRef {
macro_rules! define_iter(
($name:ident, $typ:ty, $mtyp:ty, $prepare_buffer:expr, $from_ptr:expr) => {
#[derive(Debug)]
pub struct $name<'a, T: MetaAPI + 'a> {
buffer: $typ,
state: glib_sys::gpointer,
@ -607,6 +606,17 @@ macro_rules! define_iter(
unsafe impl<'a, T: MetaAPI> Send for $name<'a, T> { }
unsafe impl<'a, T: MetaAPI> Sync for $name<'a, T> { }
impl<'a, T: MetaAPI> fmt::Debug for $name<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct(stringify!($name))
.field("buffer", &self.buffer)
.field("state", &self.state)
.field("meta_api", &self.meta_api)
.field("items", &self.items)
.finish()
}
}
impl<'a, T: MetaAPI> $name<'a, T> {
fn new(buffer: $typ) -> $name<'a, T> {
skip_assert_initialized!();

View file

@ -21,7 +21,7 @@ use std::mem;
use std::ops;
use std::ptr;
#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BufferPoolConfig(Structure);
impl ops::Deref for BufferPoolConfig {

View file

@ -6,8 +6,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use futures_core::stream::Stream;
use futures_core::task::{Context, Poll, Waker};
use futures_channel::mpsc::{self, UnboundedReceiver};
use futures_core::Stream;
use futures_util::{future, StreamExt};
use glib;
use glib::source::{Continue, Priority, SourceId};
use glib::translate::*;
@ -17,14 +18,17 @@ use gst_sys;
use std::cell::RefCell;
use std::mem::transmute;
use std::pin::Pin;
use std::ptr;
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll};
use Bus;
use BusSyncReply;
use Message;
use MessageType;
lazy_static! {
static ref SET_ONCE_QUARK: glib::Quark = glib::Quark::from_string("gstreamer-rs-sync-handler");
}
unsafe extern "C" fn trampoline_watch<F: FnMut(&Bus, &Message) -> Continue + 'static>(
bus: *mut gst_sys::GstBus,
msg: *mut gst_sys::GstMessage,
@ -157,8 +161,26 @@ impl Bus {
F: Fn(&Bus, &Message) -> BusSyncReply + Send + Sync + 'static,
{
unsafe {
let bus = self.to_glib_none().0;
// This is not thread-safe before 1.16.3, see
// https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/merge_requests/416
if ::version() < (1, 16, 3, 0) {
if !gobject_sys::g_object_get_qdata(bus as *mut _, SET_ONCE_QUARK.to_glib())
.is_null()
{
panic!("Bus sync handler can only be set once");
}
gobject_sys::g_object_set_qdata(
bus as *mut _,
SET_ONCE_QUARK.to_glib(),
1 as *mut _,
);
}
gst_sys::gst_bus_set_sync_handler(
self.to_glib_none().0,
bus,
Some(trampoline_sync::<F>),
into_raw_sync(func),
Some(destroy_closure_sync::<F>),
@ -167,7 +189,15 @@ impl Bus {
}
pub fn unset_sync_handler(&self) {
// This is not thread-safe before 1.16.3, see
// https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/merge_requests/416
if ::version() < (1, 16, 3, 0) {
return;
}
unsafe {
use std::ptr;
gst_sys::gst_bus_set_sync_handler(self.to_glib_none().0, None, ptr::null_mut(), None)
}
}
@ -217,6 +247,21 @@ impl Bus {
}
}
}
pub fn stream(&self) -> BusStream {
BusStream::new(self)
}
pub fn stream_filtered<'a>(
&self,
message_types: &'a [MessageType],
) -> impl Stream<Item = Message> + Unpin + Send + 'a {
self.stream().filter(move |message| {
let message_type = message.get_type();
future::ready(message_types.contains(&message_type))
})
}
}
#[derive(Debug)]
@ -234,49 +279,39 @@ impl<'a> Iterator for Iter<'a> {
}
#[derive(Debug)]
pub struct BusStream(Bus, Arc<Mutex<Option<Waker>>>);
pub struct BusStream {
bus: Bus,
receiver: UnboundedReceiver<Message>,
}
impl BusStream {
pub fn new(bus: &Bus) -> Self {
skip_assert_initialized!();
let waker = Arc::new(Mutex::new(None));
let waker_clone = Arc::clone(&waker);
bus.set_sync_handler(move |_, _| {
let mut waker = waker_clone.lock().unwrap();
if let Some(waker) = waker.take() {
// FIXME: Force type...
let waker: Waker = waker;
waker.wake();
}
let bus = bus.clone();
let (sender, receiver) = mpsc::unbounded();
BusSyncReply::Pass
bus.set_sync_handler(move |_, message| {
let _ = sender.unbounded_send(message.to_owned());
BusSyncReply::Drop
});
BusStream(bus.clone(), waker)
Self { bus, receiver }
}
}
impl Drop for BusStream {
fn drop(&mut self) {
self.0.unset_sync_handler();
self.bus.unset_sync_handler();
}
}
impl Stream for BusStream {
type Item = Message;
fn poll_next(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Option<Self::Item>> {
let BusStream(ref bus, ref waker) = *self;
let msg = bus.pop();
if let Some(msg) = msg {
Poll::Ready(Some(msg))
} else {
let mut waker = waker.lock().unwrap();
*waker = Some(ctx.waker().clone());
Poll::Pending
}
fn poll_next(mut self: Pin<&mut Self>, context: &mut Context) -> Poll<Option<Self::Item>> {
self.receiver.poll_next_unpin(context)
}
}
@ -306,4 +341,23 @@ mod tests {
_ => unreachable!(),
}
}
#[test]
fn test_bus_stream() {
::init().unwrap();
let bus = Bus::new();
let bus_stream = bus.stream();
let eos_message = ::Message::new_eos().build();
bus.post(&eos_message).unwrap();
let bus_future = bus_stream.into_future();
let (message, _) = futures_executor::block_on(bus_future);
match message.unwrap().view() {
::MessageView::Eos(_) => (),
_ => unreachable!(),
}
}
}

View file

@ -9,6 +9,7 @@
use caps_features::*;
use miniobject::*;
use std::fmt;
use std::marker::PhantomData;
use std::ptr;
use std::str;
use structure::*;
@ -34,6 +35,21 @@ impl Caps {
Builder::new(name)
}
pub fn builder_full() -> BuilderFull<SomeFeatures> {
assert_initialized_main_thread!();
BuilderFull::new()
}
pub fn builder_full_with_features(features: CapsFeatures) -> BuilderFull<SomeFeatures> {
assert_initialized_main_thread!();
BuilderFull::new_with_features(features)
}
pub fn builder_full_with_any_features() -> BuilderFull<AnyFeatures> {
assert_initialized_main_thread!();
BuilderFull::new_with_any_features()
}
pub fn new_empty() -> Self {
assert_initialized_main_thread!();
unsafe { from_glib_full(gst_sys::gst_caps_new_empty()) }
@ -56,7 +72,16 @@ impl Caps {
pub fn fixate(caps: Self) -> Self {
skip_assert_initialized!();
unsafe { from_glib_full(gst_sys::gst_caps_fixate(caps.into_ptr())) }
unsafe {
// See https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/388
assert!(!caps.is_any());
let ptr = if caps.is_empty() {
gst_sys::gst_caps_new_empty()
} else {
gst_sys::gst_caps_fixate(caps.into_ptr())
};
from_glib_full(ptr)
}
}
pub fn merge(caps: Self, other: Self) -> Self {
@ -563,6 +588,95 @@ impl<'a> Builder<'a> {
}
}
pub enum AnyFeatures {}
pub enum SomeFeatures {}
pub struct BuilderFull<T> {
caps: ::Caps,
features: Option<CapsFeatures>,
phantom: PhantomData<T>,
}
impl<T> fmt::Debug for BuilderFull<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Builder")
.field("caps", &self.caps)
.field("features", &self.features)
.field("phantom", &self.phantom)
.finish()
}
}
impl BuilderFull<SomeFeatures> {
fn new() -> Self {
BuilderFull {
caps: Caps::new_empty(),
features: None,
phantom: PhantomData,
}
}
fn new_with_features(features: CapsFeatures) -> Self {
BuilderFull {
caps: Caps::new_empty(),
features: Some(features),
phantom: PhantomData,
}
}
pub fn structure_with_features(self, structure: Structure, features: CapsFeatures) -> Self {
self.append_structure(structure, Some(features))
}
pub fn structure_with_any_features(self, structure: Structure) -> Self {
self.append_structure(structure, Some(CapsFeatures::new_any()))
}
}
impl BuilderFull<AnyFeatures> {
fn new_with_any_features() -> Self {
BuilderFull {
caps: Caps::new_empty(),
features: Some(CapsFeatures::new_any()),
phantom: PhantomData,
}
}
}
impl<T> BuilderFull<T> {
fn append_structure(mut self, structure: Structure, features: Option<CapsFeatures>) -> Self {
let features = {
match self.features {
None => features,
Some(ref result) => {
let mut result = result.clone();
match features {
None => Some(result),
Some(features) => {
features.iter().for_each(|feat| result.add(feat));
Some(result)
}
}
}
}
};
self.caps
.get_mut()
.unwrap()
.append_structure_full(structure, features);
self
}
pub fn structure(self, structure: Structure) -> Self {
self.append_structure(structure, None)
}
pub fn build(self) -> Caps {
self.caps
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -656,4 +770,73 @@ mod tests {
let caps = Caps::new_simple("foo/bar", &[]);
format!("{}", caps);
}
#[test]
fn test_builder_full() {
::init().unwrap();
let caps = Caps::builder_full()
.structure(Structure::builder("audio/x-raw").build())
.structure(Structure::builder("video/x-raw").build())
.build();
assert_eq!(caps.to_string(), "audio/x-raw; video/x-raw");
let caps = Caps::builder_full()
.structure(
Structure::builder("audio/x-raw")
.field("format", &"S16LE")
.build(),
)
.structure(Structure::builder("video/x-raw").build())
.build();
assert_eq!(
caps.to_string(),
"audio/x-raw, format=(string)S16LE; video/x-raw"
);
let caps = Caps::builder_full()
.structure_with_any_features(Structure::builder("audio/x-raw").build())
.structure_with_features(
Structure::builder("video/x-raw").build(),
CapsFeatures::new(&["foo:bla", "foo:baz"]),
)
.build();
assert_eq!(
caps.to_string(),
"audio/x-raw(ANY); video/x-raw(foo:bla, foo:baz)"
);
}
#[test]
fn test_builder_full_with_features() {
::init().unwrap();
let caps = Caps::builder_full_with_features(CapsFeatures::new(&["foo:bla"]))
.structure(Structure::builder("audio/x-raw").build())
.structure_with_features(
Structure::builder("video/x-raw").build(),
CapsFeatures::new(&["foo:baz"]),
)
.build();
assert_eq!(
caps.to_string(),
"audio/x-raw(foo:bla); video/x-raw(foo:bla, foo:baz)"
);
}
#[test]
fn test_builder_full_with_any_features() {
::init().unwrap();
let caps = Caps::builder_full_with_any_features()
.structure(Structure::builder("audio/x-raw").build())
.structure(Structure::builder("video/x-raw").build())
.build();
assert_eq!(caps.to_string(), "audio/x-raw(ANY); video/x-raw(ANY)");
let caps = Caps::builder_full_with_any_features()
.structure(Structure::builder("audio/x-raw").build())
.build();
assert_eq!(caps.to_string(), "audio/x-raw(ANY)");
}
}

View file

@ -247,7 +247,7 @@ mod tests {
let mut pretty_config = ron::ser::PrettyConfig::default();
pretty_config.new_line = "".to_string();
let res = ron::ser::to_string_pretty(&caps, pretty_config.clone());
let res = ron::ser::to_string_pretty(&caps, pretty_config);
assert_eq!(
Ok(concat!(
"Some([",
@ -279,7 +279,7 @@ mod tests {
let mut pretty_config = ron::ser::PrettyConfig::default();
pretty_config.new_line = "".to_string();
let res = ron::ser::to_string_pretty(&caps, pretty_config.clone());
let res = ron::ser::to_string_pretty(&caps, pretty_config);
assert_eq!(
Ok(concat!(
"Some([",
@ -339,7 +339,7 @@ mod tests {
assert_eq!(Ok("Any".to_owned()), res);
let caps_empty = Caps::new_empty();
let res = ron::ser::to_string_pretty(&caps_empty, pretty_config.clone());
let res = ron::ser::to_string_pretty(&caps_empty, pretty_config);
assert_eq!(Ok("Empty".to_owned()), res);
}

View file

@ -106,13 +106,7 @@ impl ClockId {
unsafe {
let res =
gst_sys::gst_clock_id_compare_func(self.to_glib_none().0, other.to_glib_none().0);
if res < 0 {
cmp::Ordering::Less
} else if res > 0 {
cmp::Ordering::Greater
} else {
cmp::Ordering::Equal
}
res.cmp(&0)
}
}

View file

@ -87,13 +87,7 @@ impl cmp::PartialOrd for DateTime {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
#[inline]
fn get_cmp(delta: i32) -> Option<cmp::Ordering> {
Some(if delta < 0 {
cmp::Ordering::Less
} else if delta == 0 {
cmp::Ordering::Equal
} else {
cmp::Ordering::Greater
})
Some(delta.cmp(&0))
}
if !(self.has_year() && other.has_year()) {
@ -239,6 +233,7 @@ impl fmt::Display for DateTime {
mod tests {
use super::*;
#[allow(clippy::cognitive_complexity)]
#[test]
fn test_to_utc() {
::init().unwrap();

View file

@ -197,7 +197,7 @@ mod tests {
assert_eq!(Ok("YM(2018, 5)".to_owned()), res);
let datetime = DateTime::new_y(2018);
let res = ron::ser::to_string_pretty(&datetime, pretty_config.clone());
let res = ron::ser::to_string_pretty(&datetime, pretty_config);
assert_eq!(Ok("Y(2018)".to_owned()), res);
}

View file

@ -38,7 +38,13 @@ use StateChangeReturn;
use StateChangeSuccess;
use std::ffi::CStr;
#[cfg(any(feature = "v1_10", feature = "dox"))]
use std::future::Future;
#[cfg(any(feature = "v1_10", feature = "dox"))]
use std::marker::Unpin;
use std::mem;
#[cfg(any(feature = "v1_10", feature = "dox"))]
use std::pin::Pin;
use libc;
@ -247,6 +253,15 @@ pub trait ElementExtManual: 'static {
fn call_async<F>(&self, func: F)
where
F: FnOnce(&Self) + Send + 'static;
#[cfg(any(feature = "v1_10", feature = "dox"))]
fn call_async_future<F, T>(
&self,
func: F,
) -> Pin<Box<dyn Future<Output = T> + Unpin + Send + 'static>>
where
F: FnOnce(&Self) -> T + Send + 'static,
T: Send + 'static;
}
impl<O: IsA<Element>> ElementExtManual for O {
@ -487,7 +502,7 @@ impl<O: IsA<Element>> ElementExtManual for O {
fn get_pads(&self) -> Vec<Pad> {
unsafe {
let elt: &gst_sys::GstElement = &*(self.as_ptr() as *const _);
::utils::MutexGuard::lock(&elt.object.lock);
let _guard = ::utils::MutexGuard::lock(&elt.object.lock);
FromGlibPtrContainer::from_glib_none(elt.pads)
}
}
@ -495,7 +510,7 @@ impl<O: IsA<Element>> ElementExtManual for O {
fn get_sink_pads(&self) -> Vec<Pad> {
unsafe {
let elt: &gst_sys::GstElement = &*(self.as_ptr() as *const _);
::utils::MutexGuard::lock(&elt.object.lock);
let _guard = ::utils::MutexGuard::lock(&elt.object.lock);
FromGlibPtrContainer::from_glib_none(elt.sinkpads)
}
}
@ -503,7 +518,7 @@ impl<O: IsA<Element>> ElementExtManual for O {
fn get_src_pads(&self) -> Vec<Pad> {
unsafe {
let elt: &gst_sys::GstElement = &*(self.as_ptr() as *const _);
::utils::MutexGuard::lock(&elt.object.lock);
let _guard = ::utils::MutexGuard::lock(&elt.object.lock);
FromGlibPtrContainer::from_glib_none(elt.srcpads)
}
}
@ -511,7 +526,7 @@ impl<O: IsA<Element>> ElementExtManual for O {
fn num_pads(&self) -> u16 {
unsafe {
let elt: &gst_sys::GstElement = &*(self.as_ptr() as *const _);
::utils::MutexGuard::lock(&elt.object.lock);
let _guard = ::utils::MutexGuard::lock(&elt.object.lock);
elt.numpads
}
}
@ -519,7 +534,7 @@ impl<O: IsA<Element>> ElementExtManual for O {
fn num_sink_pads(&self) -> u16 {
unsafe {
let elt: &gst_sys::GstElement = &*(self.as_ptr() as *const _);
::utils::MutexGuard::lock(&elt.object.lock);
let _guard = ::utils::MutexGuard::lock(&elt.object.lock);
elt.numsinkpads
}
}
@ -527,7 +542,7 @@ impl<O: IsA<Element>> ElementExtManual for O {
fn num_src_pads(&self) -> u16 {
unsafe {
let elt: &gst_sys::GstElement = &*(self.as_ptr() as *const _);
::utils::MutexGuard::lock(&elt.object.lock);
let _guard = ::utils::MutexGuard::lock(&elt.object.lock);
elt.numsrcpads
}
}
@ -768,6 +783,27 @@ impl<O: IsA<Element>> ElementExtManual for O {
);
}
}
#[cfg(any(feature = "v1_10", feature = "dox"))]
fn call_async_future<F, T>(
&self,
func: F,
) -> Pin<Box<dyn Future<Output = T> + Unpin + Send + 'static>>
where
F: FnOnce(&Self) -> T + Send + 'static,
T: Send + 'static,
{
use futures_channel::oneshot;
use futures_util::future::FutureExt;
let (sender, receiver) = oneshot::channel();
self.call_async(move |element| {
let _ = sender.send(func(element));
});
Box::pin(receiver.map(|res| res.expect("sender dropped")))
}
}
impl ElementClass {

View file

@ -145,7 +145,7 @@ impl LoggableError {
pub fn log(&self) {
self.category.log(
None as Option<&::Object>,
None as Option<&glib::Object>,
::DebugLevel::Error,
self.bool_error.filename,
self.bool_error.function,
@ -154,7 +154,7 @@ impl LoggableError {
);
}
pub fn log_with_object<O: IsA<::Object>>(&self, obj: &O) {
pub fn log_with_object<O: IsA<glib::Object>>(&self, obj: &O) {
self.category.log(
Some(obj),
::DebugLevel::Error,

View file

@ -68,13 +68,7 @@ impl cmp::Ord for Seqnum {
fn cmp(&self, other: &Seqnum) -> cmp::Ordering {
unsafe {
let ret = gst_sys::gst_util_seqnum_compare(self.0, other.0);
if ret < 0 {
cmp::Ordering::Less
} else if ret > 0 {
cmp::Ordering::Greater
} else {
cmp::Ordering::Equal
}
ret.cmp(&0)
}
}
}
@ -1451,11 +1445,8 @@ impl<'a> NavigationBuilder<'a> {
}
event_builder_generic_impl!(|s: &mut Self| {
let structure = s.structure.take();
let ev = gst_sys::gst_event_new_navigation(structure.to_glib_none().0);
mem::forget(structure);
ev
let structure = s.structure.take().unwrap();
gst_sys::gst_event_new_navigation(structure.into_ptr())
});
}
@ -1569,14 +1560,8 @@ impl<'a> CustomUpstreamBuilder<'a> {
}
event_builder_generic_impl!(|s: &mut Self| {
let structure = s.structure.take();
let ev = gst_sys::gst_event_new_custom(
gst_sys::GST_EVENT_CUSTOM_UPSTREAM,
structure.to_glib_none().0,
);
mem::forget(structure);
ev
let structure = s.structure.take().unwrap();
gst_sys::gst_event_new_custom(gst_sys::GST_EVENT_CUSTOM_UPSTREAM, structure.into_ptr())
});
}
@ -1594,14 +1579,8 @@ impl<'a> CustomDownstreamBuilder<'a> {
}
event_builder_generic_impl!(|s: &mut Self| {
let structure = s.structure.take();
let ev = gst_sys::gst_event_new_custom(
gst_sys::GST_EVENT_CUSTOM_DOWNSTREAM,
structure.to_glib_none().0,
);
mem::forget(structure);
ev
let structure = s.structure.take().unwrap();
gst_sys::gst_event_new_custom(gst_sys::GST_EVENT_CUSTOM_DOWNSTREAM, structure.into_ptr())
});
}
@ -1619,14 +1598,11 @@ impl<'a> CustomDownstreamOobBuilder<'a> {
}
event_builder_generic_impl!(|s: &mut Self| {
let structure = s.structure.take();
let ev = gst_sys::gst_event_new_custom(
let structure = s.structure.take().unwrap();
gst_sys::gst_event_new_custom(
gst_sys::GST_EVENT_CUSTOM_DOWNSTREAM_OOB,
structure.to_glib_none().0,
);
mem::forget(structure);
ev
structure.into_ptr(),
)
});
}
@ -1644,14 +1620,11 @@ impl<'a> CustomDownstreamStickyBuilder<'a> {
}
event_builder_generic_impl!(|s: &mut Self| {
let structure = s.structure.take();
let ev = gst_sys::gst_event_new_custom(
let structure = s.structure.take().unwrap();
gst_sys::gst_event_new_custom(
gst_sys::GST_EVENT_CUSTOM_DOWNSTREAM_STICKY,
structure.to_glib_none().0,
);
mem::forget(structure);
ev
structure.into_ptr(),
)
});
}
@ -1669,14 +1642,8 @@ impl<'a> CustomBothBuilder<'a> {
}
event_builder_generic_impl!(|s: &mut Self| {
let structure = s.structure.take();
let ev = gst_sys::gst_event_new_custom(
gst_sys::GST_EVENT_CUSTOM_BOTH,
structure.to_glib_none().0,
);
mem::forget(structure);
ev
let structure = s.structure.take().unwrap();
gst_sys::gst_event_new_custom(gst_sys::GST_EVENT_CUSTOM_BOTH, structure.into_ptr())
});
}
@ -1694,14 +1661,8 @@ impl<'a> CustomBothOobBuilder<'a> {
}
event_builder_generic_impl!(|s: &mut Self| {
let structure = s.structure.take();
let ev = gst_sys::gst_event_new_custom(
gst_sys::GST_EVENT_CUSTOM_BOTH_OOB,
structure.to_glib_none().0,
);
mem::forget(structure);
ev
let structure = s.structure.take().unwrap();
gst_sys::gst_event_new_custom(gst_sys::GST_EVENT_CUSTOM_BOTH_OOB, structure.into_ptr())
});
}

Some files were not shown because too many files have changed in this diff Show more