Skip to content
Commits on Source (14)
......@@ -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"
......
......@@ -15,6 +15,7 @@ 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 }
......@@ -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"
......
// 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),
}
}
......@@ -5,6 +5,21 @@ 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.
......@@ -670,7 +685,9 @@ 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.2...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
......
[package]
name = "gstreamer-app"
version = "0.15.3"
version = "0.15.4"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer App library"
......
......@@ -12,12 +12,15 @@ 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"))]
......@@ -48,6 +51,7 @@ pub struct AppSinkCallbacks {
Box<dyn FnMut(&AppSink) -> Result<gst::FlowSuccess, gst::FlowError> + Send + 'static>,
>,
>,
panicked: AtomicBool,
callbacks: gst_app_sys::GstAppSinkCallbacks,
}
......@@ -122,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 {
......@@ -145,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);
}
}
}
}
......@@ -158,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
};
......@@ -173,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
};
......
......@@ -10,11 +10,15 @@ 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;
......@@ -29,6 +33,7 @@ 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,
}
......@@ -89,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)
......@@ -116,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);
}
}
}
}
......@@ -133,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);
}
}
}
}
......@@ -145,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
};
......
......@@ -5,6 +5,21 @@ 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.
......@@ -670,7 +685,9 @@ 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.2...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
......
[package]
name = "gstreamer-audio"
version = "0.15.3"
version = "0.15.4"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer Audio library"
......
......@@ -5,6 +5,21 @@ 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.
......@@ -670,7 +685,9 @@ 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.2...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
......
[package]
name = "gstreamer-base"
version = "0.15.3"
version = "0.15.4"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer Base library"
......
......@@ -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)
}
}
......
......@@ -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 _)
}
}
......
......@@ -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 _)
}
}
......
......@@ -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 _)
}
}
......
......@@ -102,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,
......@@ -221,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,
......@@ -534,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,
......@@ -792,6 +841,7 @@ 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>);
......@@ -849,6 +899,12 @@ pub enum GeneratedOutput {
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
......@@ -1079,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,
......
......@@ -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> {
......
......@@ -5,6 +5,21 @@ 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.
......@@ -670,7 +685,9 @@ 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.2...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
......
[package]
name = "gstreamer-check"
version = "0.15.3"
version = "0.15.4"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
categories = ["api-bindings", "multimedia"]
description = "Rust bindings for GStreamer Check library"
......
......@@ -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
}
}
......