forked from mirrors/gstreamer-rs
rtsp server: allow custom authentication
Enables subclassing gst_rtsp_server::RTSPAuth and overriding its authenticate/check/generate_authenticate_header methods Also add new methods in RTSPContext to retrieve RTSP request/response, and to get/replace tokens. Additionally, added RTSPMessage with methods to add an authentication header to a request / retrieve authentication parameters from a response. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/merge_requests/1359>
This commit is contained in:
parent
08fa853c7e
commit
60e8c44abb
14 changed files with 588 additions and 4 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -388,6 +388,12 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991"
|
checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "data-encoding"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_more"
|
name = "derive_more"
|
||||||
version = "0.99.17"
|
version = "0.99.17"
|
||||||
|
@ -474,6 +480,7 @@ dependencies = [
|
||||||
"byte-slice-cast",
|
"byte-slice-cast",
|
||||||
"cairo-rs",
|
"cairo-rs",
|
||||||
"cocoa",
|
"cocoa",
|
||||||
|
"data-encoding",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"futures",
|
"futures",
|
||||||
"gio",
|
"gio",
|
||||||
|
|
|
@ -41,6 +41,7 @@ raw-window-handle = { version = "0.5", optional = true }
|
||||||
uds = { version = "0.4", optional = true }
|
uds = { version = "0.4", optional = true }
|
||||||
winit = { version = "0.29", optional = true, default-features = false, features = ["rwh_05"] }
|
winit = { version = "0.29", optional = true, default-features = false, features = ["rwh_05"] }
|
||||||
atomic_refcell = "0.1"
|
atomic_refcell = "0.1"
|
||||||
|
data-encoding = "2.0"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
windows = { version = "0.52", features=["Win32_Graphics_Direct3D11",
|
windows = { version = "0.52", features=["Win32_Graphics_Direct3D11",
|
||||||
|
@ -133,6 +134,10 @@ required-features = ["rtsp-server"]
|
||||||
name = "rtsp-server-subclass"
|
name = "rtsp-server-subclass"
|
||||||
required-features = ["rtsp-server"]
|
required-features = ["rtsp-server"]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "rtsp-server-custom-auth"
|
||||||
|
required-features = ["rtsp-server", "gst-rtsp-server/v1_22"]
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "tagsetter"
|
name = "tagsetter"
|
||||||
|
|
||||||
|
|
219
examples/src/bin/rtsp-server-custom-auth.rs
Normal file
219
examples/src/bin/rtsp-server-custom-auth.rs
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
// This example demonstrates how to set up a rtsp server using GStreamer
|
||||||
|
// and extending the default auth module behaviour by subclassing RTSPAuth
|
||||||
|
// For this, the example creates a videotestsrc pipeline manually to be used
|
||||||
|
// by the RTSP server for providing data
|
||||||
|
#![allow(clippy::non_send_fields_in_send_ty)]
|
||||||
|
|
||||||
|
use anyhow::Error;
|
||||||
|
use derive_more::{Display, Error};
|
||||||
|
use gst_rtsp_server::prelude::*;
|
||||||
|
|
||||||
|
#[path = "../examples-common.rs"]
|
||||||
|
mod examples_common;
|
||||||
|
|
||||||
|
#[derive(Debug, Display, Error)]
|
||||||
|
#[display(fmt = "Could not get mount points")]
|
||||||
|
struct NoMountPoints;
|
||||||
|
|
||||||
|
fn main_loop() -> Result<(), Error> {
|
||||||
|
let main_loop = glib::MainLoop::new(None, false);
|
||||||
|
let server = gst_rtsp_server::RTSPServer::new();
|
||||||
|
|
||||||
|
// We create our custom auth module.
|
||||||
|
// The job of the auth module is to authenticate users and authorize
|
||||||
|
// factories access/construction.
|
||||||
|
let auth = auth::Auth::default();
|
||||||
|
server.set_auth(Some(&auth));
|
||||||
|
|
||||||
|
// 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.mount_points().ok_or(NoMountPoints)?;
|
||||||
|
|
||||||
|
// Next, we create a 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 = gst_rtsp_server::RTSPMediaFactory::new();
|
||||||
|
// Here we tell the media factory the media we want to serve.
|
||||||
|
// This is done in the launch syntax. When the first client connects,
|
||||||
|
// the factory will use this syntax to create a new pipeline instance.
|
||||||
|
factory.set_launch("( videotestsrc ! vp8enc ! rtpvp8pay name=pay0 )");
|
||||||
|
// 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.bound_port()
|
||||||
|
);
|
||||||
|
println!("user admin/password can access stream");
|
||||||
|
println!("user demo/demo passes authentication but receives 404");
|
||||||
|
println!("other users do not pass pass authentication and receive 401");
|
||||||
|
|
||||||
|
// Start the mainloop. From this point on, the server will start to serve
|
||||||
|
// our quality content to connecting clients.
|
||||||
|
main_loop.run();
|
||||||
|
|
||||||
|
id.remove();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Our custom auth module
|
||||||
|
mod auth {
|
||||||
|
// In the imp submodule we include the actual implementation
|
||||||
|
mod imp {
|
||||||
|
use gst_rtsp::{RTSPHeaderField, RTSPStatusCode};
|
||||||
|
use gst_rtsp_server::{prelude::*, subclass::prelude::*, RTSPContext};
|
||||||
|
|
||||||
|
// This is the private data of our auth
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Auth;
|
||||||
|
|
||||||
|
impl Auth {
|
||||||
|
// Simulate external auth validation and user extraction
|
||||||
|
// authorized users are admin/password and demo/demo
|
||||||
|
fn external_auth(&self, auth: &str) -> Option<String> {
|
||||||
|
if let Ok(decoded) = data_encoding::BASE64.decode(auth.as_bytes()) {
|
||||||
|
if let Ok(decoded) = std::str::from_utf8(&decoded) {
|
||||||
|
let tokens = decoded.split(':').collect::<Vec<_>>();
|
||||||
|
if tokens == vec!["admin", "password"] || tokens == vec!["demo", "demo"] {
|
||||||
|
return Some(tokens[0].into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simulate external role check
|
||||||
|
// admin user can construct and access media factory
|
||||||
|
fn external_access_check(&self, user: &str) -> bool {
|
||||||
|
user == "admin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
#[glib::object_subclass]
|
||||||
|
impl ObjectSubclass for Auth {
|
||||||
|
const NAME: &'static str = "RsRTSPAuth";
|
||||||
|
type Type = super::Auth;
|
||||||
|
type ParentType = gst_rtsp_server::RTSPAuth;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementation of glib::Object virtual methods
|
||||||
|
impl ObjectImpl for Auth {}
|
||||||
|
|
||||||
|
// Implementation of gst_rtsp_server::RTSPAuth virtual methods
|
||||||
|
impl RTSPAuthImpl for Auth {
|
||||||
|
fn authenticate(&self, ctx: &RTSPContext) -> bool {
|
||||||
|
// authenticate should always be called with a valid context request
|
||||||
|
let req = ctx
|
||||||
|
.request()
|
||||||
|
.expect("Context without request. Should not happen !");
|
||||||
|
|
||||||
|
if let Some(auth_credentials) = req.parse_auth_credentials().get(0) {
|
||||||
|
if let Some(authorization) = auth_credentials.authorization() {
|
||||||
|
if let Some(user) = self.external_auth(authorization) {
|
||||||
|
// Update context token with authenticated username
|
||||||
|
ctx.set_token(gst_rtsp_server::RTSPToken::new(&[("user", &user)]));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check(&self, ctx: &RTSPContext, role: &glib::GString) -> bool {
|
||||||
|
// We only check media factory access
|
||||||
|
if !role.starts_with("auth.check.media.factory") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.token().is_none() {
|
||||||
|
// If we do not have a context token yet, check if there are any auth credentials in request
|
||||||
|
if !self.authenticate(ctx) {
|
||||||
|
// If there were no credentials, send a "401 Unauthorized" response
|
||||||
|
if let Some(resp) = ctx.response() {
|
||||||
|
resp.init_response(RTSPStatusCode::Unauthorized, ctx.request());
|
||||||
|
resp.add_header(
|
||||||
|
RTSPHeaderField::WwwAuthenticate,
|
||||||
|
"Basic realm=\"CustomRealm\"",
|
||||||
|
);
|
||||||
|
if let Some(client) = ctx.client() {
|
||||||
|
client.send_message(resp, ctx.session());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(token) = ctx.token() {
|
||||||
|
// If we already have a user token...
|
||||||
|
if self.external_access_check(&token.string("user").unwrap_or_default()) {
|
||||||
|
// grant access if user may access factory
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// send a "404 Not Found" response if user may not access factory
|
||||||
|
if let Some(resp) = ctx.response() {
|
||||||
|
resp.init_response(RTSPStatusCode::NotFound, ctx.request());
|
||||||
|
if let Some(client) = ctx.client() {
|
||||||
|
client.send_message(resp, ctx.session());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This here defines the public interface of our auth and implements
|
||||||
|
// the corresponding traits so that it behaves like any other RTSPAuth
|
||||||
|
glib::wrapper! {
|
||||||
|
pub struct Auth(ObjectSubclass<imp::Auth>) @extends gst_rtsp_server::RTSPAuth;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Auth {
|
||||||
|
// Creates a new instance of our auth
|
||||||
|
fn default() -> Self {
|
||||||
|
glib::Object::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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}"),
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||||
|
|
||||||
|
use crate::{RTSPClient, RTSPSession};
|
||||||
use glib::{prelude::*, source::SourceId, translate::*};
|
use glib::{prelude::*, source::SourceId, translate::*};
|
||||||
|
use gst_rtsp::rtsp_message::RTSPMessage;
|
||||||
use crate::RTSPClient;
|
|
||||||
|
|
||||||
mod sealed {
|
mod sealed {
|
||||||
pub trait Sealed {}
|
pub trait Sealed {}
|
||||||
|
@ -19,6 +19,21 @@ pub trait RTSPClientExtManual: sealed::Sealed + IsA<RTSPClient> + 'static {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(alias = "gst_rtsp_client_send_message")]
|
||||||
|
fn send_message(
|
||||||
|
&self,
|
||||||
|
message: &RTSPMessage,
|
||||||
|
session: Option<&RTSPSession>,
|
||||||
|
) -> gst_rtsp::RTSPResult {
|
||||||
|
unsafe {
|
||||||
|
from_glib(ffi::gst_rtsp_client_send_message(
|
||||||
|
self.as_ref().to_glib_none().0,
|
||||||
|
session.to_glib_none().0,
|
||||||
|
message.to_glib_none().0,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<O: IsA<RTSPClient>> RTSPClientExtManual for O {}
|
impl<O: IsA<RTSPClient>> RTSPClientExtManual for O {}
|
||||||
|
|
|
@ -5,8 +5,10 @@ use std::{
|
||||||
ptr::{self, addr_of},
|
ptr::{self, addr_of},
|
||||||
};
|
};
|
||||||
|
|
||||||
use glib::translate::*;
|
use glib::{translate::*, ObjectType};
|
||||||
use gst_rtsp::RTSPUrl;
|
use gst_rtsp::{rtsp_message::RTSPMessage, RTSPUrl};
|
||||||
|
|
||||||
|
use crate::{RTSPClient, RTSPSession, RTSPToken};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[doc(alias = "GstRTSPContext")]
|
#[doc(alias = "GstRTSPContext")]
|
||||||
|
@ -42,6 +44,87 @@ impl RTSPContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn client(&self) -> Option<&RTSPClient> {
|
||||||
|
unsafe {
|
||||||
|
let ptr = self.0.as_ptr();
|
||||||
|
if (*ptr).client.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let client = RTSPClient::from_glib_ptr_borrow(
|
||||||
|
addr_of!((*ptr).client) as *const *const ffi::GstRTSPClient
|
||||||
|
);
|
||||||
|
Some(client)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn request(&self) -> Option<&RTSPMessage> {
|
||||||
|
unsafe {
|
||||||
|
let ptr = self.0.as_ptr();
|
||||||
|
if (*ptr).request.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let msg = RTSPMessage::from_glib_ptr_borrow(
|
||||||
|
addr_of!((*ptr).request) as *const *const gst_rtsp::ffi::GstRTSPMessage
|
||||||
|
);
|
||||||
|
Some(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn response(&self) -> Option<&RTSPMessage> {
|
||||||
|
unsafe {
|
||||||
|
let ptr = self.0.as_ptr();
|
||||||
|
if (*ptr).response.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let msg = RTSPMessage::from_glib_ptr_borrow(
|
||||||
|
addr_of!((*ptr).response) as *const *const gst_rtsp::ffi::GstRTSPMessage
|
||||||
|
);
|
||||||
|
Some(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn session(&self) -> Option<&RTSPSession> {
|
||||||
|
unsafe {
|
||||||
|
let ptr = self.0.as_ptr();
|
||||||
|
if (*ptr).session.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let sess = RTSPSession::from_glib_ptr_borrow(
|
||||||
|
addr_of!((*ptr).session) as *const *const ffi::GstRTSPSession
|
||||||
|
);
|
||||||
|
Some(sess)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn token(&self) -> Option<RTSPToken> {
|
||||||
|
unsafe {
|
||||||
|
let ptr = self.0.as_ptr();
|
||||||
|
if (*ptr).token.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let token = RTSPToken::from_glib_none((*ptr).token as *const ffi::GstRTSPToken);
|
||||||
|
Some(token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "v1_22")]
|
||||||
|
#[doc(alias = "gst_rtsp_context_set_token")]
|
||||||
|
pub fn set_token(&self, token: RTSPToken) {
|
||||||
|
unsafe {
|
||||||
|
ffi::gst_rtsp_context_set_token(self.0.as_ptr(), token.into_glib_ptr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Add additional getters for all the contained fields as needed
|
// TODO: Add additional getters for all the contained fields as needed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#![allow(clippy::cast_ptr_alignment)]
|
#![allow(clippy::cast_ptr_alignment)]
|
||||||
|
|
||||||
|
mod rtsp_auth;
|
||||||
mod rtsp_client;
|
mod rtsp_client;
|
||||||
mod rtsp_media;
|
mod rtsp_media;
|
||||||
mod rtsp_media_factory;
|
mod rtsp_media_factory;
|
||||||
|
@ -20,6 +21,7 @@ pub mod prelude {
|
||||||
pub use gst::subclass::prelude::*;
|
pub use gst::subclass::prelude::*;
|
||||||
|
|
||||||
pub use super::{
|
pub use super::{
|
||||||
|
rtsp_auth::{RTSPAuthImpl, RTSPAuthImplExt},
|
||||||
rtsp_client::{RTSPClientImpl, RTSPClientImplExt},
|
rtsp_client::{RTSPClientImpl, RTSPClientImplExt},
|
||||||
rtsp_media::{RTSPMediaImpl, RTSPMediaImplExt},
|
rtsp_media::{RTSPMediaImpl, RTSPMediaImplExt},
|
||||||
rtsp_media_factory::{RTSPMediaFactoryImpl, RTSPMediaFactoryImplExt},
|
rtsp_media_factory::{RTSPMediaFactoryImpl, RTSPMediaFactoryImplExt},
|
||||||
|
|
116
gstreamer-rtsp-server/src/subclass/rtsp_auth.rs
Normal file
116
gstreamer-rtsp-server/src/subclass/rtsp_auth.rs
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||||
|
|
||||||
|
use crate::{RTSPAuth, RTSPContext};
|
||||||
|
use glib::{prelude::*, subclass::prelude::*, translate::*};
|
||||||
|
use libc::c_char;
|
||||||
|
|
||||||
|
pub trait RTSPAuthImpl: RTSPAuthImplExt + ObjectImpl + Send + Sync {
|
||||||
|
fn authenticate(&self, ctx: &RTSPContext) -> bool {
|
||||||
|
self.parent_authenticate(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check(&self, ctx: &RTSPContext, check: &glib::GString) -> bool {
|
||||||
|
self.parent_check(ctx, check)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_authenticate_header(&self, ctx: &RTSPContext) {
|
||||||
|
self.parent_generate_authenticate_header(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod sealed {
|
||||||
|
pub trait Sealed {}
|
||||||
|
impl<T: super::RTSPAuthImplExt> Sealed for T {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait RTSPAuthImplExt: sealed::Sealed + ObjectSubclass {
|
||||||
|
fn parent_authenticate(&self, ctx: &RTSPContext) -> bool {
|
||||||
|
unsafe {
|
||||||
|
let data = Self::type_data();
|
||||||
|
let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPAuthClass;
|
||||||
|
(*parent_class)
|
||||||
|
.authenticate
|
||||||
|
.map(|f| {
|
||||||
|
from_glib(f(
|
||||||
|
self.obj().unsafe_cast_ref::<RTSPAuth>().to_glib_none().0,
|
||||||
|
ctx.to_glib_none().0,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parent_check(&self, ctx: &RTSPContext, check: &glib::GString) -> bool {
|
||||||
|
unsafe {
|
||||||
|
let data = Self::type_data();
|
||||||
|
let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPAuthClass;
|
||||||
|
(*parent_class)
|
||||||
|
.check
|
||||||
|
.map(|f| {
|
||||||
|
from_glib(f(
|
||||||
|
self.obj().unsafe_cast_ref::<RTSPAuth>().to_glib_none().0,
|
||||||
|
ctx.to_glib_none().0,
|
||||||
|
check.to_glib_none().0,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parent_generate_authenticate_header(&self, ctx: &RTSPContext) {
|
||||||
|
unsafe {
|
||||||
|
let data = Self::type_data();
|
||||||
|
let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPAuthClass;
|
||||||
|
if let Some(f) = (*parent_class).generate_authenticate_header {
|
||||||
|
f(
|
||||||
|
self.obj().unsafe_cast_ref::<RTSPAuth>().to_glib_none().0,
|
||||||
|
ctx.to_glib_none().0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: RTSPAuthImpl> RTSPAuthImplExt for T {}
|
||||||
|
|
||||||
|
unsafe impl<T: RTSPAuthImpl> IsSubclassable<T> for RTSPAuth {
|
||||||
|
fn class_init(klass: &mut glib::Class<Self>) {
|
||||||
|
Self::parent_class_init::<T>(klass);
|
||||||
|
let klass = klass.as_mut();
|
||||||
|
klass.authenticate = Some(rtsp_auth_authenticate::<T>);
|
||||||
|
klass.check = Some(rtsp_auth_check::<T>);
|
||||||
|
klass.generate_authenticate_header = Some(rtsp_auth_generate_authenticate_header::<T>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn rtsp_auth_authenticate<T: RTSPAuthImpl>(
|
||||||
|
ptr: *mut ffi::GstRTSPAuth,
|
||||||
|
ctx: *mut ffi::GstRTSPContext,
|
||||||
|
) -> glib::ffi::gboolean {
|
||||||
|
let instance = &*(ptr as *mut T::Instance);
|
||||||
|
let imp = instance.imp();
|
||||||
|
|
||||||
|
imp.authenticate(&from_glib_borrow(ctx)).into_glib()
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn rtsp_auth_check<T: RTSPAuthImpl>(
|
||||||
|
ptr: *mut ffi::GstRTSPAuth,
|
||||||
|
ctx: *mut ffi::GstRTSPContext,
|
||||||
|
check: *const c_char,
|
||||||
|
) -> glib::ffi::gboolean {
|
||||||
|
let instance = &*(ptr as *mut T::Instance);
|
||||||
|
let imp = instance.imp();
|
||||||
|
|
||||||
|
imp.check(&from_glib_borrow(ctx), &from_glib_borrow(check))
|
||||||
|
.into_glib()
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn rtsp_auth_generate_authenticate_header<T: RTSPAuthImpl>(
|
||||||
|
ptr: *mut ffi::GstRTSPAuth,
|
||||||
|
ctx: *mut ffi::GstRTSPContext,
|
||||||
|
) {
|
||||||
|
let instance = &*(ptr as *mut T::Instance);
|
||||||
|
let imp = instance.imp();
|
||||||
|
|
||||||
|
imp.generate_authenticate_header(&from_glib_borrow(ctx));
|
||||||
|
}
|
|
@ -49,6 +49,11 @@ name = "Gst.Structure"
|
||||||
status = "manual"
|
status = "manual"
|
||||||
ref_mode = "ref"
|
ref_mode = "ref"
|
||||||
|
|
||||||
|
[[object]]
|
||||||
|
name = "GstRtsp.RTSPAuthCredential"
|
||||||
|
status = "generate"
|
||||||
|
concurrency = "send"
|
||||||
|
|
||||||
[[object]]
|
[[object]]
|
||||||
name = "GstRtsp.RTSPAuthParam"
|
name = "GstRtsp.RTSPAuthParam"
|
||||||
status = "generate"
|
status = "generate"
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
// from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git)
|
// from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git)
|
||||||
// DO NOT EDIT
|
// DO NOT EDIT
|
||||||
|
|
||||||
|
mod rtsp_auth_credential;
|
||||||
|
pub use self::rtsp_auth_credential::RTSPAuthCredential;
|
||||||
|
|
||||||
mod rtsp_auth_param;
|
mod rtsp_auth_param;
|
||||||
pub use self::rtsp_auth_param::RTSPAuthParam;
|
pub use self::rtsp_auth_param::RTSPAuthParam;
|
||||||
|
|
||||||
|
|
17
gstreamer-rtsp/src/auto/rtsp_auth_credential.rs
Normal file
17
gstreamer-rtsp/src/auto/rtsp_auth_credential.rs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||||
|
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||||
|
// from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git)
|
||||||
|
// DO NOT EDIT
|
||||||
|
|
||||||
|
glib::wrapper! {
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct RTSPAuthCredential(Boxed<ffi::GstRTSPAuthCredential>);
|
||||||
|
|
||||||
|
match fn {
|
||||||
|
copy => |ptr| glib::gobject_ffi::g_boxed_copy(ffi::gst_rtsp_auth_credential_get_type(), ptr as *mut _) as *mut ffi::GstRTSPAuthCredential,
|
||||||
|
free => |ptr| glib::gobject_ffi::g_boxed_free(ffi::gst_rtsp_auth_credential_get_type(), ptr as *mut _),
|
||||||
|
type_ => || ffi::gst_rtsp_auth_credential_get_type(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for RTSPAuthCredential {}
|
|
@ -27,6 +27,9 @@ pub use crate::auto::*;
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
mod flag_serde;
|
mod flag_serde;
|
||||||
|
|
||||||
|
pub mod rtsp_auth_credential;
|
||||||
|
pub mod rtsp_message;
|
||||||
|
|
||||||
// Re-export all the traits in a prelude module, so that applications
|
// Re-export all the traits in a prelude module, so that applications
|
||||||
// can always "use gst_rtsp::prelude::*" without getting conflicts
|
// can always "use gst_rtsp::prelude::*" without getting conflicts
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
|
|
26
gstreamer-rtsp/src/rtsp_auth_credential.rs
Normal file
26
gstreamer-rtsp/src/rtsp_auth_credential.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
use crate::{RTSPAuthCredential, RTSPAuthMethod, RTSPAuthParam};
|
||||||
|
use ffi::GstRTSPAuthCredential;
|
||||||
|
use glib::translate::*;
|
||||||
|
|
||||||
|
impl RTSPAuthCredential {
|
||||||
|
pub fn scheme(&self) -> RTSPAuthMethod {
|
||||||
|
let ptr: *mut GstRTSPAuthCredential = self.to_glib_none().0;
|
||||||
|
unsafe { from_glib((*ptr).scheme) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn authorization(&self) -> Option<&str> {
|
||||||
|
let ptr: *mut GstRTSPAuthCredential = self.to_glib_none().0;
|
||||||
|
unsafe {
|
||||||
|
if (*ptr).authorization.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
std::ffi::CStr::from_ptr((*ptr).authorization).to_str().ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn params(&self) -> glib::collections::PtrSlice<RTSPAuthParam> {
|
||||||
|
let ptr: *mut GstRTSPAuthCredential = self.to_glib_none().0;
|
||||||
|
unsafe { FromGlibPtrContainer::from_glib_none((*ptr).params) }
|
||||||
|
}
|
||||||
|
}
|
25
gstreamer-rtsp/src/rtsp_auth_param.rs
Normal file
25
gstreamer-rtsp/src/rtsp_auth_param.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
use glib::translate::*;
|
||||||
|
|
||||||
|
impl RTSPAuthParam {
|
||||||
|
pub fn name(&self) -> Option<&str> {
|
||||||
|
let ptr: *mut GstRTSPAuthParam = self.to_glib_none().0;
|
||||||
|
unsafe {
|
||||||
|
if (*ptr).name.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
std::ffi::CStr::from_ptr((*ptr).name).to_str().ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn value(&self) -> Option<&str> {
|
||||||
|
let ptr: *mut GstRTSPAuthParam = self.to_glib_none().0;
|
||||||
|
unsafe {
|
||||||
|
if (*ptr).value.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
std::ffi::CStr::from_ptr((*ptr).value).to_str().ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
58
gstreamer-rtsp/src/rtsp_message.rs
Normal file
58
gstreamer-rtsp/src/rtsp_message.rs
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
use crate::{RTSPAuthCredential, RTSPHeaderField, RTSPStatusCode};
|
||||||
|
use glib::translate::*;
|
||||||
|
|
||||||
|
glib::wrapper! {
|
||||||
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
#[doc(alias = "GstRTSPMessage")]
|
||||||
|
pub struct RTSPMessage(Boxed<ffi::GstRTSPMessage>);
|
||||||
|
|
||||||
|
match fn {
|
||||||
|
copy => |ptr| {
|
||||||
|
let mut copy = std::ptr::null_mut();
|
||||||
|
let res = ffi::gst_rtsp_message_copy(ptr, &mut copy);
|
||||||
|
debug_assert_eq!(res, ffi::GST_RTSP_OK);
|
||||||
|
copy
|
||||||
|
},
|
||||||
|
free => |ptr| {
|
||||||
|
let res = ffi::gst_rtsp_message_free(ptr);
|
||||||
|
debug_assert_eq!(res, ffi::GST_RTSP_OK);
|
||||||
|
},
|
||||||
|
type_ => || ffi::gst_rtsp_msg_get_type(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RTSPMessage {
|
||||||
|
pub const NONE: Option<&'static RTSPMessage> = None;
|
||||||
|
|
||||||
|
#[doc(alias = "gst_rtsp_message_add_header")]
|
||||||
|
pub fn add_header(&self, header: RTSPHeaderField, value: &str) {
|
||||||
|
let ptr = self.to_glib_none().0;
|
||||||
|
unsafe {
|
||||||
|
ffi::gst_rtsp_message_add_header(ptr, header.into_glib(), value.to_glib_none().0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(alias = "gst_rtsp_message_init_response")]
|
||||||
|
pub fn init_response(&self, code: RTSPStatusCode, request: Option<&RTSPMessage>) {
|
||||||
|
let ptr = self.to_glib_none().0;
|
||||||
|
unsafe {
|
||||||
|
ffi::gst_rtsp_message_init_response(
|
||||||
|
ptr,
|
||||||
|
code.into_glib(),
|
||||||
|
ffi::gst_rtsp_status_as_text(code.into_glib()),
|
||||||
|
request.to_glib_none().0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(alias = "gst_rtsp_message_parse_auth_credentials")]
|
||||||
|
pub fn parse_auth_credentials(&self) -> glib::collections::PtrSlice<RTSPAuthCredential> {
|
||||||
|
unsafe {
|
||||||
|
let credentials = ffi::gst_rtsp_message_parse_auth_credentials(
|
||||||
|
self.to_glib_none().0,
|
||||||
|
ffi::GST_RTSP_HDR_AUTHORIZATION,
|
||||||
|
);
|
||||||
|
FromGlibPtrContainer::from_glib_full(credentials)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue