mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer-rs.git
synced 2024-11-29 13:01:05 +00:00
rtsp_server: Add example making use of subclassing RTSPMediaFactory and RTSPMedia
This commit is contained in:
parent
51545b90f0
commit
7e989631f8
2 changed files with 487 additions and 1 deletions
|
@ -15,6 +15,7 @@ gstreamer-video = { path = "../gstreamer-video" }
|
||||||
gstreamer-pbutils = { path = "../gstreamer-pbutils" }
|
gstreamer-pbutils = { path = "../gstreamer-pbutils" }
|
||||||
gstreamer-player = { path = "../gstreamer-player", optional = true }
|
gstreamer-player = { path = "../gstreamer-player", optional = true }
|
||||||
gstreamer-editing-services = { path = "../gstreamer-editing-services", optional = true }
|
gstreamer-editing-services = { path = "../gstreamer-editing-services", optional = true }
|
||||||
|
gstreamer-sdp = { path = "../gstreamer-sdp", optional = true }
|
||||||
gstreamer-rtsp = { path = "../gstreamer-rtsp", optional = true }
|
gstreamer-rtsp = { path = "../gstreamer-rtsp", optional = true }
|
||||||
gstreamer-rtsp-server = { path = "../gstreamer-rtsp-server", 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 }
|
gstreamer-rtsp-server-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys", features = ["v1_8"], optional = true }
|
||||||
|
@ -43,7 +44,7 @@ gtksink = ["gtk", "gio"]
|
||||||
gtkvideooverlay = ["gtk", "gdk", "gio"]
|
gtkvideooverlay = ["gtk", "gdk", "gio"]
|
||||||
gtkvideooverlay-x11 = ["gtkvideooverlay"]
|
gtkvideooverlay-x11 = ["gtkvideooverlay"]
|
||||||
gtkvideooverlay-quartz = ["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"]
|
gst-rtsp-server-record = ["gstreamer-rtsp-server-sys", "gstreamer-rtsp-server", "gstreamer-rtsp", "gio"]
|
||||||
v1_10 = ["gstreamer/v1_10"]
|
v1_10 = ["gstreamer/v1_10"]
|
||||||
pango-cairo = ["pango", "pangocairo", "cairo-rs"]
|
pango-cairo = ["pango", "pangocairo", "cairo-rs"]
|
||||||
|
@ -110,6 +111,10 @@ name = "rtpfecserver"
|
||||||
name = "rtsp-server"
|
name = "rtsp-server"
|
||||||
required-features = ["gst-rtsp-server"]
|
required-features = ["gst-rtsp-server"]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "rtsp-server-subclass"
|
||||||
|
required-features = ["gst-rtsp-server"]
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "tagsetter"
|
name = "tagsetter"
|
||||||
|
|
||||||
|
|
481
examples/src/bin/rtsp-server-subclass.rs
Normal file
481
examples/src/bin/rtsp-server-subclass.rs
Normal 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),
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue