mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2024-06-20 15:10:34 +00:00
Merge branch 'xdpscreencastbin' into 'main'
Video source bin wrapping pipewiresrc using xdg-desktop-portal See merge request gstreamer/gst-plugins-rs!1405
This commit is contained in:
commit
59d5a7b954
|
@ -45,6 +45,8 @@ members = [
|
||||||
"utils/togglerecord",
|
"utils/togglerecord",
|
||||||
"utils/tracers",
|
"utils/tracers",
|
||||||
"utils/uriplaylistbin",
|
"utils/uriplaylistbin",
|
||||||
|
"utils/xdgscreencapsrc",
|
||||||
|
|
||||||
|
|
||||||
"video/cdg",
|
"video/cdg",
|
||||||
"video/closedcaption",
|
"video/closedcaption",
|
||||||
|
|
|
@ -145,6 +145,8 @@ You will find the following plugins in this repository:
|
||||||
|
|
||||||
- `uriplaylistbin`: Helper bin to gaplessly play a list of URIs.
|
- `uriplaylistbin`: Helper bin to gaplessly play a list of URIs.
|
||||||
|
|
||||||
|
- `xdgscreencapsrc`: GStreamer xdg-desktop-portal screen capture plugin
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
gst-plugins-rs relies on [cargo-c](https://github.com/lu-zero/cargo-c/) to
|
gst-plugins-rs relies on [cargo-c](https://github.com/lu-zero/cargo-c/) to
|
||||||
|
|
|
@ -205,6 +205,7 @@ plugins = {
|
||||||
'extra-deps': {'cairo-gobject': []},
|
'extra-deps': {'cairo-gobject': []},
|
||||||
},
|
},
|
||||||
'gopbuffer': {'library': 'libgstgopbuffer'},
|
'gopbuffer': {'library': 'libgstgopbuffer'},
|
||||||
|
'xdgscreencapsrc': {'library': 'libgstxdgscreencapsrc'},
|
||||||
}
|
}
|
||||||
|
|
||||||
if get_option('examples').allowed()
|
if get_option('examples').allowed()
|
||||||
|
|
|
@ -47,6 +47,7 @@ option('livesync', type: 'feature', value: 'auto', description: 'Build livesync
|
||||||
option('togglerecord', type: 'feature', value: 'auto', description: 'Build togglerecord plugin')
|
option('togglerecord', type: 'feature', value: 'auto', description: 'Build togglerecord plugin')
|
||||||
option('tracers', type: 'feature', value: 'auto', description: 'Build tracers plugin')
|
option('tracers', type: 'feature', value: 'auto', description: 'Build tracers plugin')
|
||||||
option('uriplaylistbin', type: 'feature', value: 'auto', description: 'Build uriplaylistbin plugin')
|
option('uriplaylistbin', type: 'feature', value: 'auto', description: 'Build uriplaylistbin plugin')
|
||||||
|
option('xdgscreencapsrc', type: 'feature', value: 'auto', description: 'Build xdgscreencapsrc plugin')
|
||||||
|
|
||||||
# video
|
# video
|
||||||
option('cdg', type: 'feature', value: 'auto', description: 'Build cdg plugin')
|
option('cdg', type: 'feature', value: 'auto', description: 'Build cdg plugin')
|
||||||
|
|
47
utils/xdgscreencapsrc/Cargo.toml
Normal file
47
utils/xdgscreencapsrc/Cargo.toml
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
[package]
|
||||||
|
name = "gst-plugin-xdgscreencapsrc"
|
||||||
|
version = "0.12.0-alpha.1"
|
||||||
|
authors = ["Ruben Gonzalez <rgonzalez@fluendo.com"]
|
||||||
|
license = "MPL-2.0"
|
||||||
|
description = "GStreamer xdg-desktop-portal screen capture plugin"
|
||||||
|
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
|
||||||
|
edition = "2021"
|
||||||
|
rust-version = "1.70"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
gst.workspace = true
|
||||||
|
parking_lot = "0.12"
|
||||||
|
ashpd = {version = "0.6", default-features = false, features = ["tokio"]}
|
||||||
|
tokio = { version = "1", features = ["fs", "macros", "rt-multi-thread", "time"] }
|
||||||
|
once_cell.workspace = true
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
either = "1.0"
|
||||||
|
gst-check.workspace = true
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "gstxdgscreencapsrc"
|
||||||
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
gst-plugin-version-helper.workspace = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
static = []
|
||||||
|
capi = []
|
||||||
|
doc = ["gst/v1_18"]
|
||||||
|
|
||||||
|
[package.metadata.capi]
|
||||||
|
min_version = "0.9.21"
|
||||||
|
|
||||||
|
[package.metadata.capi.header]
|
||||||
|
enabled = false
|
||||||
|
|
||||||
|
[package.metadata.capi.library]
|
||||||
|
install_subdir = "gstreamer-1.0"
|
||||||
|
versioning = false
|
||||||
|
import_library = false
|
||||||
|
|
||||||
|
[package.metadata.capi.pkg_config]
|
||||||
|
requires_private = "gstreamer-1.0, gstreamer-audio-1.0, gstreamer-video-1.0, gobject-2.0, glib-2.0, gmodule-2.0"
|
3
utils/xdgscreencapsrc/build.rs
Normal file
3
utils/xdgscreencapsrc/build.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
fn main() {
|
||||||
|
gst_plugin_version_helper::info()
|
||||||
|
}
|
34
utils/xdgscreencapsrc/src/lib.rs
Normal file
34
utils/xdgscreencapsrc/src/lib.rs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright (C) 2023 Ruben Gonzalez <rgonzalez@fluendo.com
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
|
||||||
|
// If a copy of the MPL was not distributed with this file, You can obtain one at
|
||||||
|
// <https://mozilla.org/MPL/2.0/>.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
#![allow(clippy::non_send_fields_in_send_ty, unused_doc_comments)]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* plugin-xdgscreencapsrc:
|
||||||
|
*
|
||||||
|
* Since: plugins-rs-0.12.0
|
||||||
|
*/
|
||||||
|
use gst::glib;
|
||||||
|
|
||||||
|
mod xdgscreencapsrc;
|
||||||
|
|
||||||
|
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
|
xdgscreencapsrc::register(plugin)
|
||||||
|
}
|
||||||
|
|
||||||
|
gst::plugin_define!(
|
||||||
|
xdgscreencapsrc,
|
||||||
|
env!("CARGO_PKG_DESCRIPTION"),
|
||||||
|
plugin_init,
|
||||||
|
concat!(env!("CARGO_PKG_VERSION"), "-", "COMMIT_ID"),
|
||||||
|
// FIXME: MPL-2.0 is only allowed since 1.18.3 (as unknown) and 1.20 (as known)
|
||||||
|
"MPL",
|
||||||
|
env!("CARGO_PKG_NAME"),
|
||||||
|
env!("CARGO_PKG_NAME"),
|
||||||
|
env!("CARGO_PKG_REPOSITORY"),
|
||||||
|
env!("BUILD_REL_DATE")
|
||||||
|
);
|
333
utils/xdgscreencapsrc/src/xdgscreencapsrc/imp.rs
Normal file
333
utils/xdgscreencapsrc/src/xdgscreencapsrc/imp.rs
Normal file
|
@ -0,0 +1,333 @@
|
||||||
|
// Copyright (C) 2023 Ruben Gonzalez <rgonzalez@fluendo.com
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
|
||||||
|
// If a copy of the MPL was not distributed with this file, You can obtain one at
|
||||||
|
// <https://mozilla.org/MPL/2.0/>.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
/**
|
||||||
|
* element-xdgscreencapsrc:
|
||||||
|
* @short_description: Source element wrapping pipewiresrc using xdg-desktop-portal to start a screencast session.
|
||||||
|
*
|
||||||
|
* Based on https://gitlab.gnome.org/-/snippets/19 using https://crates.io/crates/ashpd
|
||||||
|
*
|
||||||
|
* ## Example pipeline
|
||||||
|
* ```bash
|
||||||
|
* gst-launch-1.0 -v xdgscreencapsrc ! videoconvert ! identity silent=false ! gtkwaylandsink
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Since: plugins-rs-0.12.0
|
||||||
|
*/
|
||||||
|
use gst::glib;
|
||||||
|
use gst::prelude::*;
|
||||||
|
use gst::subclass::prelude::*;
|
||||||
|
|
||||||
|
use ashpd::{
|
||||||
|
desktop::screencast::{PersistMode, Screencast},
|
||||||
|
WindowIdentifier,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, glib::Enum)]
|
||||||
|
#[repr(u32)]
|
||||||
|
#[enum_type(name = "SourceType")]
|
||||||
|
pub enum SourceType {
|
||||||
|
#[enum_value(name = "A monitor", nick = "monitor")]
|
||||||
|
Monitor,
|
||||||
|
#[enum_value(name = "A specific window", nick = "window")]
|
||||||
|
Window,
|
||||||
|
#[enum_value(name = "Virtual", nick = "virtual")]
|
||||||
|
Virtual,
|
||||||
|
#[enum_value(name = "monitor+window+virtual", nick = "all")]
|
||||||
|
All,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SourceType> for ashpd::enumflags2::BitFlags<ashpd::desktop::screencast::SourceType, u32> {
|
||||||
|
fn from(v: SourceType) -> Self {
|
||||||
|
use ashpd::desktop::screencast;
|
||||||
|
|
||||||
|
match v {
|
||||||
|
SourceType::Monitor => screencast::SourceType::Monitor.into(),
|
||||||
|
SourceType::Window => screencast::SourceType::Window.into(),
|
||||||
|
SourceType::Virtual => screencast::SourceType::Virtual.into(),
|
||||||
|
SourceType::All => {
|
||||||
|
screencast::SourceType::Monitor
|
||||||
|
| screencast::SourceType::Window
|
||||||
|
| screencast::SourceType::Virtual
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, glib::Enum)]
|
||||||
|
#[repr(u32)]
|
||||||
|
#[enum_type(name = "CursorMode")]
|
||||||
|
pub enum CursorMode {
|
||||||
|
#[enum_value(
|
||||||
|
name = "The cursor is not part of the screen cast stream",
|
||||||
|
nick = "hidden"
|
||||||
|
)]
|
||||||
|
Hidden,
|
||||||
|
#[enum_value(
|
||||||
|
name = "The cursor is embedded as part of the stream buffers",
|
||||||
|
nick = "embedded"
|
||||||
|
)]
|
||||||
|
Embedded,
|
||||||
|
#[enum_value(
|
||||||
|
name = "The cursor is not part of the screen cast stream, but sent as PipeWire stream metadata. Not implemented",
|
||||||
|
nick = "metadata"
|
||||||
|
)]
|
||||||
|
Metadata,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CursorMode> for ashpd::desktop::screencast::CursorMode {
|
||||||
|
fn from(v: CursorMode) -> Self {
|
||||||
|
use ashpd::desktop::screencast;
|
||||||
|
|
||||||
|
match v {
|
||||||
|
CursorMode::Hidden => screencast::CursorMode::Hidden,
|
||||||
|
CursorMode::Embedded => screencast::CursorMode::Embedded,
|
||||||
|
CursorMode::Metadata => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
|
pub fn block_on<F: std::future::Future>(future: F) -> F::Output {
|
||||||
|
static TOKIO_RT: once_cell::sync::Lazy<tokio::runtime::Runtime> =
|
||||||
|
once_cell::sync::Lazy::new(|| {
|
||||||
|
tokio::runtime::Builder::new_current_thread()
|
||||||
|
.enable_io()
|
||||||
|
.enable_time()
|
||||||
|
.build()
|
||||||
|
.expect("launch of single-threaded tokio runtime")
|
||||||
|
});
|
||||||
|
TOKIO_RT.block_on(future)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn portal_main(cursor_mode: CursorMode, source_type: SourceType) -> ashpd::Result<u32> {
|
||||||
|
let proxy = Screencast::new().await?;
|
||||||
|
let session = proxy.create_session().await?;
|
||||||
|
proxy
|
||||||
|
.select_sources(
|
||||||
|
&session,
|
||||||
|
cursor_mode.into(),
|
||||||
|
source_type.into(),
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
PersistMode::DoNot,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let response = proxy
|
||||||
|
.start(&session, &WindowIdentifier::default())
|
||||||
|
.await?
|
||||||
|
.response()?;
|
||||||
|
|
||||||
|
if let Some(first_value) = response.streams().iter().next() {
|
||||||
|
let id = first_value.pipe_wire_node_id();
|
||||||
|
Ok(id)
|
||||||
|
} else {
|
||||||
|
Err(ashpd::Error::NoResponse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_CURSOR_MODE: CursorMode = CursorMode::Hidden;
|
||||||
|
const DEFAULT_SOURCE_TYPE: SourceType = SourceType::All;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
struct Settings {
|
||||||
|
cursor_mode: CursorMode,
|
||||||
|
source_type: SourceType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Settings {
|
||||||
|
fn default() -> Self {
|
||||||
|
Settings {
|
||||||
|
cursor_mode: DEFAULT_CURSOR_MODE,
|
||||||
|
source_type: DEFAULT_SOURCE_TYPE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct XdpScreenCast {
|
||||||
|
settings: Mutex<Settings>,
|
||||||
|
src: gst::Element,
|
||||||
|
srcpad: gst::GhostPad,
|
||||||
|
}
|
||||||
|
|
||||||
|
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||||
|
gst::DebugCategory::new(
|
||||||
|
"xdgscreencapsrc",
|
||||||
|
gst::DebugColorFlags::empty(),
|
||||||
|
Some("XDP Screen Cast Bin"),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
impl XdpScreenCast {}
|
||||||
|
|
||||||
|
#[glib::object_subclass]
|
||||||
|
impl ObjectSubclass for XdpScreenCast {
|
||||||
|
const NAME: &'static str = "GstXdpScreenCast";
|
||||||
|
type Type = super::XdpScreenCast;
|
||||||
|
type ParentType = gst::Bin;
|
||||||
|
|
||||||
|
fn with_class(klass: &Self::Class) -> Self {
|
||||||
|
let settings = Mutex::new(Settings::default());
|
||||||
|
let src = gst::ElementFactory::make("pipewiresrc").build().unwrap();
|
||||||
|
let templ = klass.pad_template("src").unwrap();
|
||||||
|
let srcpad = gst::GhostPad::from_template(&templ);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
settings,
|
||||||
|
src,
|
||||||
|
srcpad,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectImpl for XdpScreenCast {
|
||||||
|
// based on https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.ScreenCast.html#org-freedesktop-portal-screencast-selectsources
|
||||||
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
|
vec![
|
||||||
|
glib::ParamSpecEnum::builder_with_default::<CursorMode>(
|
||||||
|
"cursor-mode",
|
||||||
|
DEFAULT_CURSOR_MODE,
|
||||||
|
)
|
||||||
|
.nick("cursor mode")
|
||||||
|
.blurb("Determines how the cursor will be drawn in the screen cast stream")
|
||||||
|
.mutable_ready()
|
||||||
|
.build(),
|
||||||
|
glib::ParamSpecEnum::builder_with_default::<SourceType>(
|
||||||
|
"source-type",
|
||||||
|
DEFAULT_SOURCE_TYPE,
|
||||||
|
)
|
||||||
|
.nick("source type")
|
||||||
|
.blurb("Sets the types of content to record")
|
||||||
|
.mutable_ready()
|
||||||
|
.build(),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
PROPERTIES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||||
|
match pspec.name() {
|
||||||
|
"cursor-mode" => {
|
||||||
|
let mut settings = self.settings.lock();
|
||||||
|
let cursor_mode = value.get().expect("type checked upstream");
|
||||||
|
gst::debug!(
|
||||||
|
CAT,
|
||||||
|
imp: self,
|
||||||
|
"Setting cursor-mode from {:?} to {:?}",
|
||||||
|
settings.cursor_mode,
|
||||||
|
cursor_mode
|
||||||
|
);
|
||||||
|
|
||||||
|
settings.cursor_mode = cursor_mode;
|
||||||
|
}
|
||||||
|
"source-type" => {
|
||||||
|
let mut settings = self.settings.lock();
|
||||||
|
let source_type = value.get().expect("type checked upstream");
|
||||||
|
gst::debug!(
|
||||||
|
CAT,
|
||||||
|
imp: self,
|
||||||
|
"Setting source-type from {:?} to {:?}",
|
||||||
|
settings.source_type,
|
||||||
|
source_type
|
||||||
|
);
|
||||||
|
|
||||||
|
settings.source_type = source_type;
|
||||||
|
}
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
|
match pspec.name() {
|
||||||
|
"cursor-mode" => {
|
||||||
|
let settings = self.settings.lock();
|
||||||
|
settings.cursor_mode.to_value()
|
||||||
|
}
|
||||||
|
"source-type" => {
|
||||||
|
let settings = self.settings.lock();
|
||||||
|
settings.source_type.to_value()
|
||||||
|
}
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn constructed(&self) {
|
||||||
|
self.parent_constructed();
|
||||||
|
|
||||||
|
let obj = self.obj();
|
||||||
|
|
||||||
|
obj.add(&self.src).unwrap();
|
||||||
|
self.srcpad
|
||||||
|
.set_target(Some(&self.src.static_pad("src").unwrap()))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
obj.add_pad(&self.srcpad).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GstObjectImpl for XdpScreenCast {}
|
||||||
|
|
||||||
|
impl ElementImpl for XdpScreenCast {
|
||||||
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||||
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||||
|
gst::subclass::ElementMetadata::new(
|
||||||
|
"xdg-desktop-portal screen capture",
|
||||||
|
"Generic",
|
||||||
|
"Source element wrapping pipewiresrc using \
|
||||||
|
xdg-desktop-portal to start a screencast session.",
|
||||||
|
"Ruben Gonzalez <rgonzalez@fluendo.com>",
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(&*ELEMENT_METADATA)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||||
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||||
|
let caps = gst::Caps::new_any();
|
||||||
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
|
"src",
|
||||||
|
gst::PadDirection::Src,
|
||||||
|
gst::PadPresence::Always,
|
||||||
|
&caps,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
vec![src_pad_template]
|
||||||
|
});
|
||||||
|
|
||||||
|
PAD_TEMPLATES.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change_state(
|
||||||
|
&self,
|
||||||
|
transition: gst::StateChange,
|
||||||
|
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
||||||
|
gst::debug!(CAT, imp: self, "Changing state {:?}", transition);
|
||||||
|
|
||||||
|
let settings = self.settings.lock();
|
||||||
|
|
||||||
|
let success = self.parent_change_state(transition)?;
|
||||||
|
|
||||||
|
if transition == gst::StateChange::NullToReady {
|
||||||
|
if let Ok(fd) = block_on(portal_main(settings.cursor_mode, settings.source_type)) {
|
||||||
|
self.src.set_property("fd", fd as i32);
|
||||||
|
} else {
|
||||||
|
return Err(gst::StateChangeError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(success)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BinImpl for XdpScreenCast {}
|
25
utils/xdgscreencapsrc/src/xdgscreencapsrc/mod.rs
Normal file
25
utils/xdgscreencapsrc/src/xdgscreencapsrc/mod.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright (C) 2023 Ruben Gonzalez <rgonzalez@fluendo.com
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
|
||||||
|
// If a copy of the MPL was not distributed with this file, You can obtain one at
|
||||||
|
// <https://mozilla.org/MPL/2.0/>.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use gst::glib;
|
||||||
|
use gst::prelude::*;
|
||||||
|
|
||||||
|
mod imp;
|
||||||
|
|
||||||
|
glib::wrapper! {
|
||||||
|
pub struct XdpScreenCast(ObjectSubclass<imp::XdpScreenCast>) @extends gst::Bin, gst::Element, gst::Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
|
gst::Element::register(
|
||||||
|
Some(plugin),
|
||||||
|
"xdgscreencapsrc",
|
||||||
|
gst::Rank::NONE,
|
||||||
|
XdpScreenCast::static_type(),
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in a new issue