mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2024-11-24 04:20:59 +00:00
Add a GTK4 paintable sink plugin
Co-authored-by: Sebastian Dröge <sebastian@centricular.com> Co-authored-by: Jordan Petridis <jordan@centricular.com>
This commit is contained in:
parent
1a830c7c78
commit
30796fbe07
17 changed files with 1201 additions and 3 deletions
|
@ -43,19 +43,19 @@ stages:
|
|||
.debian:11-stable:
|
||||
extends: .debian:11
|
||||
variables:
|
||||
FDO_DISTRIBUTION_TAG: '$GST_RS_STABLE-${GST_RS_IMG_TAG}_2021-09-09.0'
|
||||
FDO_DISTRIBUTION_TAG: '$GST_RS_STABLE-${GST_RS_IMG_TAG}_2021-10-12.0'
|
||||
FDO_BASE_IMAGE: "registry.freedesktop.org/gstreamer/gstreamer-rs/debian/bullseye-slim:$GST_RS_STABLE-$GST_RS_IMG_TAG"
|
||||
|
||||
.debian:11-msrv:
|
||||
extends: .debian:11
|
||||
variables:
|
||||
FDO_DISTRIBUTION_TAG: '$GST_RS_MSRV-${GST_RS_IMG_TAG}_2021-09-09.0'
|
||||
FDO_DISTRIBUTION_TAG: '$GST_RS_MSRV-${GST_RS_IMG_TAG}_2021-10-12.0'
|
||||
FDO_BASE_IMAGE: "registry.freedesktop.org/gstreamer/gstreamer-rs/debian/bullseye-slim:$GST_RS_MSRV-$GST_RS_IMG_TAG"
|
||||
|
||||
.debian:11-nightly:
|
||||
extends: .debian:11
|
||||
variables:
|
||||
FDO_DISTRIBUTION_TAG: 'nightly-${GST_RS_IMG_TAG}_2021-09-09.0'
|
||||
FDO_DISTRIBUTION_TAG: 'nightly-${GST_RS_IMG_TAG}_2021-10-12.0'
|
||||
FDO_BASE_IMAGE: "registry.freedesktop.org/gstreamer/gstreamer-rs/debian/bullseye-slim:nightly-$GST_RS_IMG_TAG"
|
||||
|
||||
.build-debian-container:
|
||||
|
@ -66,6 +66,7 @@ stages:
|
|||
FDO_DISTRIBUTION_PACKAGES: "libcsound64-dev llvm clang nasm libsodium-dev"
|
||||
FDO_DISTRIBUTION_EXEC: >-
|
||||
bash ci/install-dav1d.sh &&
|
||||
bash ci/install-gtk4.sh &&
|
||||
apt clean &&
|
||||
bash ./ci/install-rust-ext.sh
|
||||
rules:
|
||||
|
|
|
@ -20,6 +20,7 @@ members = [
|
|||
"video/ffv1",
|
||||
"video/flavors",
|
||||
"video/gif",
|
||||
"video/gtk4",
|
||||
"video/rav1e",
|
||||
"video/rspng",
|
||||
"video/hsv",
|
||||
|
|
373
LICENSE-MPL-2.0
Normal file
373
LICENSE-MPL-2.0
Normal file
|
@ -0,0 +1,373 @@
|
|||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
10
ci/install-gtk4.sh
Normal file
10
ci/install-gtk4.sh
Normal file
|
@ -0,0 +1,10 @@
|
|||
set -eux
|
||||
|
||||
BRANCH=gtk-4-4
|
||||
|
||||
git clone https://gitlab.gnome.org/GNOME/gtk.git --branch $BRANCH --depth=1
|
||||
cd gtk
|
||||
meson build -D prefix=/usr/local
|
||||
ninja -C build
|
||||
ninja -C build install
|
||||
cd ..
|
|
@ -108,6 +108,12 @@ else
|
|||
exclude += ['audio/csound']
|
||||
endif
|
||||
|
||||
if dependency('gtk4', required : get_option('gtk4')).found()
|
||||
plugins_rep += {'video/gtk4' : 'libgstgtk4',}
|
||||
else
|
||||
exclude += ['video/gtk4']
|
||||
endif
|
||||
|
||||
output = []
|
||||
|
||||
extensions = []
|
||||
|
|
|
@ -4,3 +4,4 @@ option('sodium', type : 'combo',
|
|||
choices : ['system', 'built-in', 'disabled'], value : 'built-in',
|
||||
description : 'Weither to use libsodium from the system or the built-in version from the sodiumoxide crate')
|
||||
option('csound', type : 'feature', value : 'auto', description : 'Build csound plugin')
|
||||
option('gtk4', type : 'feature', value : 'auto', description : 'Build GTK4 plugin')
|
||||
|
|
44
video/gtk4/Cargo.toml
Normal file
44
video/gtk4/Cargo.toml
Normal file
|
@ -0,0 +1,44 @@
|
|||
[package]
|
||||
name = "gst-plugin-gtk4"
|
||||
version = "0.1.0"
|
||||
authors = ["Bilal Elmoussaoui <bil.elmoussaoui@gmail.com>", "Jordan Petridis <jordan@centricular.com>", "Sebastian Dröge <sebastian@centricular.com>"]
|
||||
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
|
||||
license = "MPL-2.0"
|
||||
edition = "2018"
|
||||
description = "GTK 4 Sink element and Paintable widget"
|
||||
|
||||
[dependencies]
|
||||
gtk = { package = "gtk4", git = "https://github.com/gtk-rs/gtk4-rs" }
|
||||
|
||||
gst_video = {package="gstreamer-video", git="https://gitlab.freedesktop.org/gstreamer/gstreamer-rs"}
|
||||
gst_base = {package="gstreamer-base", git="https://gitlab.freedesktop.org/gstreamer/gstreamer-rs"}
|
||||
gst = {package="gstreamer", git="https://gitlab.freedesktop.org/gstreamer/gstreamer-rs"}
|
||||
|
||||
once_cell = "1.0"
|
||||
fragile = "1.0.0"
|
||||
|
||||
[lib]
|
||||
name = "gstgtk4"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
path = "src/lib.rs"
|
||||
|
||||
[build-dependencies]
|
||||
gst-plugin-version-helper = { path="../../version-helper" }
|
||||
|
||||
[features]
|
||||
# GStreamer 1.14 is required for static linking
|
||||
static = ["gst/v1_14"]
|
||||
capi = []
|
||||
|
||||
[package.metadata.capi]
|
||||
min_version = "0.7.0"
|
||||
|
||||
[package.metadata.capi.header]
|
||||
enabled = false
|
||||
|
||||
[package.metadata.capi.library]
|
||||
install_subdir = "gstreamer-1.0"
|
||||
versioning = false
|
||||
|
||||
[package.metadata.capi.pkg_config]
|
||||
requires_private = "gstreamer-1.0, gstreamer-base-1.0, gstreamer-video-1.0, gtk4, gobject-2.0, glib-2.0, gmodule-2.0"
|
1
video/gtk4/LICENSE-MPL-2.0
Symbolic link
1
video/gtk4/LICENSE-MPL-2.0
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../LICENSE-MPL-2.0
|
4
video/gtk4/README.md
Normal file
4
video/gtk4/README.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Gtk 4 Sink & Paintable
|
||||
|
||||
GTK 4 provides `gtk::Video` & `gtk::Picture` for rendering media such as videos. As the default `gtk::Video` widget doesn't
|
||||
offer the possibility to use a custom `gst::Pipeline`. The plugin provides a `gst_video::VideoSink` along with a `gdk::Paintable` that's capable of rendering the sink's frames.
|
3
video/gtk4/build.rs
Normal file
3
video/gtk4/build.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
gst_plugin_version_helper::info()
|
||||
}
|
117
video/gtk4/examples/gtksink.rs
Normal file
117
video/gtk4/examples/gtksink.rs
Normal file
|
@ -0,0 +1,117 @@
|
|||
use gst::prelude::*;
|
||||
|
||||
use gtk::prelude::*;
|
||||
use gtk::{gdk, gio, glib};
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
fn create_ui(app: >k::Application) {
|
||||
let pipeline = gst::Pipeline::new(None);
|
||||
let src = gst::ElementFactory::make("videotestsrc", None).unwrap();
|
||||
|
||||
let sink = gst::ElementFactory::make("gtk4paintablesink", None).unwrap();
|
||||
let paintable = sink
|
||||
.property("paintable")
|
||||
.unwrap()
|
||||
.get::<gdk::Paintable>()
|
||||
.unwrap();
|
||||
|
||||
pipeline.add_many(&[&src, &sink]).unwrap();
|
||||
src.link(&sink).unwrap();
|
||||
|
||||
let window = gtk::ApplicationWindow::new(app);
|
||||
window.set_default_size(640, 480);
|
||||
|
||||
let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0);
|
||||
let picture = gtk::Picture::new();
|
||||
let label = gtk::Label::new(Some("Position: 00:00:00"));
|
||||
|
||||
picture.set_paintable(Some(&paintable));
|
||||
vbox.append(&picture);
|
||||
vbox.append(&label);
|
||||
|
||||
window.set_child(Some(&vbox));
|
||||
window.show();
|
||||
|
||||
app.add_window(&window);
|
||||
|
||||
let pipeline_weak = pipeline.downgrade();
|
||||
let timeout_id = glib::timeout_add_local(std::time::Duration::from_millis(500), move || {
|
||||
let pipeline = match pipeline_weak.upgrade() {
|
||||
Some(pipeline) => pipeline,
|
||||
None => return glib::Continue(true),
|
||||
};
|
||||
|
||||
let position = pipeline.query_position::<gst::ClockTime>();
|
||||
label.set_text(&format!("Position: {:.0}", position.display()));
|
||||
glib::Continue(true)
|
||||
});
|
||||
|
||||
let bus = pipeline.bus().unwrap();
|
||||
|
||||
pipeline
|
||||
.set_state(gst::State::Playing)
|
||||
.expect("Unable to set the pipeline to the `Playing` state");
|
||||
|
||||
let app_weak = app.downgrade();
|
||||
bus.add_watch_local(move |_, msg| {
|
||||
use gst::MessageView;
|
||||
|
||||
let app = match app_weak.upgrade() {
|
||||
Some(app) => app,
|
||||
None => return glib::Continue(false),
|
||||
};
|
||||
|
||||
match msg.view() {
|
||||
MessageView::Eos(..) => app.quit(),
|
||||
MessageView::Error(err) => {
|
||||
println!(
|
||||
"Error from {:?}: {} ({:?})",
|
||||
err.src().map(|s| s.path_string()),
|
||||
err.error(),
|
||||
err.debug()
|
||||
);
|
||||
app.quit();
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
|
||||
glib::Continue(true)
|
||||
})
|
||||
.expect("Failed to add bus watch");
|
||||
|
||||
let timeout_id = RefCell::new(Some(timeout_id));
|
||||
let pipeline = RefCell::new(Some(pipeline));
|
||||
app.connect_shutdown(move |_| {
|
||||
window.close();
|
||||
|
||||
if let Some(pipeline) = pipeline.borrow_mut().take() {
|
||||
pipeline
|
||||
.set_state(gst::State::Null)
|
||||
.expect("Unable to set the pipeline to the `Null` state");
|
||||
pipeline.bus().unwrap().remove_watch().unwrap();
|
||||
}
|
||||
|
||||
if let Some(timeout_id) = timeout_id.borrow_mut().take() {
|
||||
timeout_id.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn main() {
|
||||
gst::init().unwrap();
|
||||
gtk::init().unwrap();
|
||||
|
||||
gstgtk4::plugin_register_static().expect("Failed to register gstgtk4 plugin");
|
||||
|
||||
{
|
||||
let app = gtk::Application::new(None, gio::ApplicationFlags::FLAGS_NONE);
|
||||
|
||||
app.connect_activate(create_ui);
|
||||
app.run();
|
||||
}
|
||||
|
||||
unsafe {
|
||||
gst::deinit();
|
||||
}
|
||||
}
|
31
video/gtk4/src/lib.rs
Normal file
31
video/gtk4/src/lib.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
//
|
||||
// Copyright (C) 2021 Bilal Elmoussaoui <bil.elmoussaoui@gmail.com>
|
||||
// Copyright (C) 2021 Jordan Petridis <jordan@centricular.com>
|
||||
// Copyright (C) 2021 Sebastian Dröge <sebastian@centricular.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;
|
||||
|
||||
mod sink;
|
||||
pub use sink::PaintableSink;
|
||||
|
||||
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
sink::register(plugin)
|
||||
}
|
||||
|
||||
gst::plugin_define!(
|
||||
gtk4,
|
||||
env!("CARGO_PKG_DESCRIPTION"),
|
||||
plugin_init,
|
||||
concat!(env!("CARGO_PKG_VERSION"), "-", env!("COMMIT_ID")),
|
||||
"MIT/X11",
|
||||
env!("CARGO_PKG_NAME"),
|
||||
env!("CARGO_PKG_NAME"),
|
||||
env!("CARGO_PKG_REPOSITORY"),
|
||||
env!("BUILD_REL_DATE")
|
||||
);
|
87
video/gtk4/src/sink/frame.rs
Normal file
87
video/gtk4/src/sink/frame.rs
Normal file
|
@ -0,0 +1,87 @@
|
|||
//
|
||||
// Copyright (C) 2021 Bilal Elmoussaoui <bil.elmoussaoui@gmail.com>
|
||||
// Copyright (C) 2021 Jordan Petridis <jordan@centricular.com>
|
||||
// Copyright (C) 2021 Sebastian Dröge <sebastian@centricular.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 gtk::prelude::*;
|
||||
use gtk::{gdk, glib};
|
||||
use std::convert::AsRef;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Frame(pub gst_video::VideoFrame<gst_video::video_frame::Readable>);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Paintable {
|
||||
pub paintable: gdk::Paintable,
|
||||
pub pixel_aspect_ratio: f64,
|
||||
}
|
||||
|
||||
impl Paintable {
|
||||
pub fn width(&self) -> i32 {
|
||||
f64::round(self.paintable.intrinsic_width() as f64 * self.pixel_aspect_ratio) as i32
|
||||
}
|
||||
|
||||
pub fn height(&self) -> i32 {
|
||||
self.paintable.intrinsic_height()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for Frame {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.0.plane_data(0).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Frame> for Paintable {
|
||||
fn from(f: Frame) -> Paintable {
|
||||
let format = match f.0.format() {
|
||||
gst_video::VideoFormat::Bgra => gdk::MemoryFormat::B8g8r8a8,
|
||||
gst_video::VideoFormat::Argb => gdk::MemoryFormat::A8r8g8b8,
|
||||
gst_video::VideoFormat::Rgba => gdk::MemoryFormat::R8g8b8a8,
|
||||
gst_video::VideoFormat::Abgr => gdk::MemoryFormat::A8b8g8r8,
|
||||
gst_video::VideoFormat::Rgb => gdk::MemoryFormat::R8g8b8,
|
||||
gst_video::VideoFormat::Bgr => gdk::MemoryFormat::B8g8r8,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let width = f.0.width() as i32;
|
||||
let height = f.0.height() as i32;
|
||||
let rowstride = f.0.plane_stride()[0] as usize;
|
||||
|
||||
let pixel_aspect_ratio =
|
||||
(*f.0.info().par().numer() as f64) / (*f.0.info().par().denom() as f64);
|
||||
|
||||
Paintable {
|
||||
paintable: gdk::MemoryTexture::new(
|
||||
width,
|
||||
height,
|
||||
format,
|
||||
&glib::Bytes::from_owned(f),
|
||||
rowstride,
|
||||
)
|
||||
.upcast(),
|
||||
pixel_aspect_ratio,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
pub fn new(buffer: &gst::Buffer, info: &gst_video::VideoInfo) -> Self {
|
||||
let video_frame =
|
||||
gst_video::VideoFrame::from_buffer_readable(buffer.clone(), info).unwrap();
|
||||
Self(video_frame)
|
||||
}
|
||||
|
||||
pub fn width(&self) -> u32 {
|
||||
self.0.width()
|
||||
}
|
||||
|
||||
pub fn height(&self) -> u32 {
|
||||
self.0.height()
|
||||
}
|
||||
}
|
236
video/gtk4/src/sink/imp.rs
Normal file
236
video/gtk4/src/sink/imp.rs
Normal file
|
@ -0,0 +1,236 @@
|
|||
//
|
||||
// Copyright (C) 2021 Bilal Elmoussaoui <bil.elmoussaoui@gmail.com>
|
||||
// Copyright (C) 2021 Jordan Petridis <jordan@centricular.com>
|
||||
// Copyright (C) 2021 Sebastian Dröge <sebastian@centricular.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 super::SinkEvent;
|
||||
use crate::sink::frame::Frame;
|
||||
use crate::sink::paintable::SinkPaintable;
|
||||
|
||||
use glib::prelude::*;
|
||||
use glib::Sender;
|
||||
|
||||
use gst::subclass::prelude::*;
|
||||
use gst::{gst_debug, gst_error, gst_trace};
|
||||
use gst_base::subclass::prelude::*;
|
||||
use gst_video::subclass::prelude::*;
|
||||
|
||||
use gtk::glib;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use fragile::Fragile;
|
||||
|
||||
pub(super) static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||
gst::DebugCategory::new(
|
||||
"gtk4paintablesink",
|
||||
gst::DebugColorFlags::empty(),
|
||||
Some("GTK4 Paintable sink"),
|
||||
)
|
||||
});
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PaintableSink {
|
||||
pub(super) paintable: Mutex<Option<Fragile<SinkPaintable>>>,
|
||||
info: Mutex<Option<gst_video::VideoInfo>>,
|
||||
pub(super) sender: Mutex<Option<Sender<SinkEvent>>>,
|
||||
pub(super) pending_frame: Mutex<Option<Frame>>,
|
||||
}
|
||||
|
||||
impl Drop for PaintableSink {
|
||||
fn drop(&mut self) {
|
||||
let mut paintable = self.paintable.lock().unwrap();
|
||||
|
||||
// Drop the paintable from the main thread
|
||||
if let Some(paintable) = paintable.take() {
|
||||
let context = glib::MainContext::default();
|
||||
|
||||
context.invoke(move || {
|
||||
drop(paintable);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for PaintableSink {
|
||||
const NAME: &'static str = "Gtk4PaintableSink";
|
||||
type Type = super::PaintableSink;
|
||||
type ParentType = gst_video::VideoSink;
|
||||
}
|
||||
|
||||
impl ObjectImpl for PaintableSink {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![glib::ParamSpec::new_object(
|
||||
"paintable",
|
||||
"Paintable",
|
||||
"The Paintable the sink renders to",
|
||||
gtk::gdk::Paintable::static_type(),
|
||||
glib::ParamFlags::READABLE,
|
||||
)]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn property(&self, obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
match pspec.name() {
|
||||
"paintable" => {
|
||||
let mut paintable = self.paintable.lock().unwrap();
|
||||
if paintable.is_none() {
|
||||
obj.initialize_paintable(&mut paintable);
|
||||
}
|
||||
|
||||
let paintable = match &*paintable {
|
||||
Some(ref paintable) => paintable,
|
||||
None => {
|
||||
gst_error!(CAT, obj: obj, "Failed to create paintable");
|
||||
return None::<>k::gdk::Paintable>.to_value();
|
||||
}
|
||||
};
|
||||
|
||||
// Getter must be called from the main thread
|
||||
match paintable.try_get() {
|
||||
Ok(paintable) => paintable.to_value(),
|
||||
Err(_) => {
|
||||
gst_error!(
|
||||
CAT,
|
||||
obj: obj,
|
||||
"Can't retrieve Paintable from non-main thread"
|
||||
);
|
||||
None::<>k::gdk::Paintable>.to_value()
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ElementImpl for PaintableSink {
|
||||
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||
gst::subclass::ElementMetadata::new(
|
||||
"GTK 4 Paintable Sink",
|
||||
"Sink/Video",
|
||||
"A GTK 4 Paintable sink",
|
||||
"Bilal Elmoussaoui <bil.elmoussaoui@gmail.com>, Jordan Petridis <jordan@centricular.com>, Sebastian Dröge <sebastian@centricular.com>",
|
||||
)
|
||||
});
|
||||
|
||||
Some(&*ELEMENT_METADATA)
|
||||
}
|
||||
|
||||
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||
// Those are the supported formats by a gdk::Texture
|
||||
let caps = gst_video::video_make_raw_caps(&[
|
||||
gst_video::VideoFormat::Bgra,
|
||||
gst_video::VideoFormat::Argb,
|
||||
gst_video::VideoFormat::Rgba,
|
||||
gst_video::VideoFormat::Abgr,
|
||||
gst_video::VideoFormat::Rgb,
|
||||
gst_video::VideoFormat::Bgr,
|
||||
])
|
||||
.any_features()
|
||||
.build();
|
||||
|
||||
vec![gst::PadTemplate::new(
|
||||
"sink",
|
||||
gst::PadDirection::Sink,
|
||||
gst::PadPresence::Always,
|
||||
&caps,
|
||||
)
|
||||
.unwrap()]
|
||||
});
|
||||
|
||||
PAD_TEMPLATES.as_ref()
|
||||
}
|
||||
|
||||
#[allow(clippy::single_match)]
|
||||
fn change_state(
|
||||
&self,
|
||||
element: &Self::Type,
|
||||
transition: gst::StateChange,
|
||||
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
||||
match transition {
|
||||
gst::StateChange::NullToReady => {
|
||||
let mut paintable = self.paintable.lock().unwrap();
|
||||
if paintable.is_none() {
|
||||
element.initialize_paintable(&mut paintable);
|
||||
}
|
||||
|
||||
if paintable.is_none() {
|
||||
gst_error!(CAT, obj: element, "Failed to create paintable");
|
||||
return Err(gst::StateChangeError);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let res = self.parent_change_state(element, transition);
|
||||
|
||||
match transition {
|
||||
gst::StateChange::PausedToReady => {
|
||||
let _ = self.info.lock().unwrap().take();
|
||||
let _ = self.pending_frame.lock().unwrap().take();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl BaseSinkImpl for PaintableSink {
|
||||
fn set_caps(&self, element: &Self::Type, caps: &gst::Caps) -> Result<(), gst::LoggableError> {
|
||||
gst_debug!(CAT, obj: element, "Setting caps {:?}", caps);
|
||||
|
||||
let video_info = gst_video::VideoInfo::from_caps(caps)
|
||||
.map_err(|_| gst::loggable_error!(CAT, "Invalid caps"))?;
|
||||
|
||||
self.info.lock().unwrap().replace(video_info);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl VideoSinkImpl for PaintableSink {
|
||||
fn show_frame(
|
||||
&self,
|
||||
element: &Self::Type,
|
||||
buffer: &gst::Buffer,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
gst_trace!(CAT, obj: element, "Rendering buffer {:?}", buffer);
|
||||
|
||||
let info = self.info.lock().unwrap();
|
||||
let info = info.as_ref().ok_or_else(|| {
|
||||
gst_error!(CAT, obj: element, "Received no caps yet");
|
||||
gst::FlowError::NotNegotiated
|
||||
})?;
|
||||
|
||||
let frame = Frame::new(buffer, info);
|
||||
self.pending_frame.lock().unwrap().replace(frame);
|
||||
|
||||
let sender = self.sender.lock().unwrap();
|
||||
let sender = sender.as_ref().ok_or_else(|| {
|
||||
gst_error!(CAT, obj: element, "Have no main thread sender");
|
||||
gst::FlowError::Error
|
||||
})?;
|
||||
|
||||
sender.send(SinkEvent::FrameChanged).map_err(|_| {
|
||||
gst_error!(CAT, obj: element, "Have main thread receiver shut down");
|
||||
gst::FlowError::Error
|
||||
})?;
|
||||
|
||||
Ok(gst::FlowSuccess::Ok)
|
||||
}
|
||||
}
|
117
video/gtk4/src/sink/mod.rs
Normal file
117
video/gtk4/src/sink/mod.rs
Normal file
|
@ -0,0 +1,117 @@
|
|||
//
|
||||
// Copyright (C) 2021 Bilal Elmoussaoui <bil.elmoussaoui@gmail.com>
|
||||
// Copyright (C) 2021 Jordan Petridis <jordan@centricular.com>
|
||||
// Copyright (C) 2021 Sebastian Dröge <sebastian@centricular.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 gtk::glib;
|
||||
use gtk::glib::prelude::*;
|
||||
use gtk::subclass::prelude::*;
|
||||
|
||||
use gst::{gst_debug, gst_trace};
|
||||
|
||||
use fragile::Fragile;
|
||||
|
||||
use std::sync::{mpsc, MutexGuard};
|
||||
|
||||
mod frame;
|
||||
mod imp;
|
||||
mod paintable;
|
||||
|
||||
use frame::Frame;
|
||||
use paintable::SinkPaintable;
|
||||
|
||||
enum SinkEvent {
|
||||
FrameChanged,
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct PaintableSink(ObjectSubclass<imp::PaintableSink>)
|
||||
@extends gst_video::VideoSink, gst_base::BaseSink, gst::Element, gst::Object;
|
||||
}
|
||||
|
||||
// GStreamer elements need to be thread-safe. For the private implementation this is automatically
|
||||
// enforced but for the public wrapper type we need to specify this manually.
|
||||
unsafe impl Send for PaintableSink {}
|
||||
unsafe impl Sync for PaintableSink {}
|
||||
|
||||
impl PaintableSink {
|
||||
pub fn new(name: Option<&str>) -> Self {
|
||||
glib::Object::new(&[("name", &name)]).expect("Failed to create a GTK4Sink")
|
||||
}
|
||||
|
||||
fn pending_frame(&self) -> Option<Frame> {
|
||||
let self_ = imp::PaintableSink::from_instance(self);
|
||||
self_.pending_frame.lock().unwrap().take()
|
||||
}
|
||||
|
||||
fn initialize_paintable(
|
||||
&self,
|
||||
paintable_storage: &mut MutexGuard<Option<Fragile<SinkPaintable>>>,
|
||||
) {
|
||||
gst_debug!(imp::CAT, obj: self, "Initializing paintable");
|
||||
|
||||
let context = glib::MainContext::default();
|
||||
|
||||
// The channel for the SinkEvents
|
||||
let (sender, receiver) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
|
||||
|
||||
// This is an one time channel we send into the closure, so we can block until the paintable has been
|
||||
// created.
|
||||
let (send, recv) = mpsc::channel();
|
||||
context.invoke(glib::clone!(
|
||||
@weak self as s =>
|
||||
move || {
|
||||
let paintable = Fragile::new(SinkPaintable::new());
|
||||
send.send(paintable).expect("Somehow we dropped the receiver");
|
||||
}
|
||||
));
|
||||
|
||||
let paintable = recv.recv().expect("Somehow we dropped the sender");
|
||||
|
||||
receiver.attach(
|
||||
None,
|
||||
glib::clone!(
|
||||
@weak self as sink => @default-return glib::Continue(false),
|
||||
move |action| sink.do_action(action)
|
||||
),
|
||||
);
|
||||
|
||||
**paintable_storage = Some(paintable);
|
||||
|
||||
let self_ = imp::PaintableSink::from_instance(self);
|
||||
*self_.sender.lock().unwrap() = Some(sender);
|
||||
}
|
||||
|
||||
fn do_action(&self, action: SinkEvent) -> glib::Continue {
|
||||
let self_ = imp::PaintableSink::from_instance(self);
|
||||
let paintable = self_.paintable.lock().unwrap().clone();
|
||||
let paintable = match paintable {
|
||||
Some(paintable) => paintable,
|
||||
None => return glib::Continue(false),
|
||||
};
|
||||
|
||||
match action {
|
||||
SinkEvent::FrameChanged => {
|
||||
gst_trace!(imp::CAT, obj: self, "Frame changed");
|
||||
paintable.get().handle_frame_changed(self.pending_frame())
|
||||
}
|
||||
}
|
||||
|
||||
glib::Continue(true)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
gst::Element::register(
|
||||
Some(plugin),
|
||||
"gtk4paintablesink",
|
||||
gst::Rank::None,
|
||||
PaintableSink::static_type(),
|
||||
)
|
||||
}
|
101
video/gtk4/src/sink/paintable/imp.rs
Normal file
101
video/gtk4/src/sink/paintable/imp.rs
Normal file
|
@ -0,0 +1,101 @@
|
|||
//
|
||||
// Copyright (C) 2021 Bilal Elmoussaoui <bil.elmoussaoui@gmail.com>
|
||||
// Copyright (C) 2021 Jordan Petridis <jordan@centricular.com>
|
||||
// Copyright (C) 2021 Sebastian Dröge <sebastian@centricular.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 gtk::prelude::*;
|
||||
use gtk::subclass::prelude::*;
|
||||
use gtk::{gdk, glib, graphene};
|
||||
|
||||
use gst::gst_trace;
|
||||
|
||||
use crate::sink::frame::Paintable;
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
pub(super) static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||
gst::DebugCategory::new(
|
||||
"gtk4paintablesink-paintable",
|
||||
gst::DebugColorFlags::empty(),
|
||||
Some("GTK4 Paintable Sink Paintable"),
|
||||
)
|
||||
});
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SinkPaintable {
|
||||
pub paintable: RefCell<Option<Paintable>>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for SinkPaintable {
|
||||
const NAME: &'static str = "Gtk4PaintableSinkPaintable";
|
||||
type Type = super::SinkPaintable;
|
||||
type ParentType = glib::Object;
|
||||
type Interfaces = (gdk::Paintable,);
|
||||
}
|
||||
|
||||
impl ObjectImpl for SinkPaintable {}
|
||||
|
||||
impl PaintableImpl for SinkPaintable {
|
||||
fn intrinsic_height(&self, _paintable: &Self::Type) -> i32 {
|
||||
if let Some(Paintable { ref paintable, .. }) = *self.paintable.borrow() {
|
||||
paintable.intrinsic_height()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
fn intrinsic_width(&self, _paintable: &Self::Type) -> i32 {
|
||||
if let Some(Paintable {
|
||||
ref paintable,
|
||||
pixel_aspect_ratio,
|
||||
}) = *self.paintable.borrow()
|
||||
{
|
||||
f64::round(paintable.intrinsic_width() as f64 * pixel_aspect_ratio) as i32
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
fn intrinsic_aspect_ratio(&self, _paintable: &Self::Type) -> f64 {
|
||||
if let Some(Paintable {
|
||||
ref paintable,
|
||||
pixel_aspect_ratio,
|
||||
}) = *self.paintable.borrow()
|
||||
{
|
||||
paintable.intrinsic_aspect_ratio() * pixel_aspect_ratio
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
fn current_image(&self, _paintable: &Self::Type) -> gdk::Paintable {
|
||||
if let Some(Paintable { ref paintable, .. }) = *self.paintable.borrow() {
|
||||
paintable.clone()
|
||||
} else {
|
||||
gdk::Paintable::new_empty(0, 0).expect("Couldn't create empty paintable")
|
||||
}
|
||||
}
|
||||
|
||||
fn snapshot(&self, paintable: &Self::Type, snapshot: &gdk::Snapshot, width: f64, height: f64) {
|
||||
if let Some(Paintable { ref paintable, .. }) = *self.paintable.borrow() {
|
||||
gst_trace!(CAT, obj: paintable, "Snapshotting frame");
|
||||
paintable.snapshot(snapshot, width, height);
|
||||
} else {
|
||||
gst_trace!(CAT, obj: paintable, "Snapshotting black frame");
|
||||
let snapshot = snapshot.downcast_ref::<gtk::Snapshot>().unwrap();
|
||||
snapshot.append_color(
|
||||
&gdk::RGBA::BLACK,
|
||||
&graphene::Rect::new(0f32, 0f32, width as f32, height as f32),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
65
video/gtk4/src/sink/paintable/mod.rs
Normal file
65
video/gtk4/src/sink/paintable/mod.rs
Normal file
|
@ -0,0 +1,65 @@
|
|||
//
|
||||
// Copyright (C) 2021 Bilal Elmoussaoui <bil.elmoussaoui@gmail.com>
|
||||
// Copyright (C) 2021 Jordan Petridis <jordan@centricular.com>
|
||||
// Copyright (C) 2021 Sebastian Dröge <sebastian@centricular.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 crate::sink::frame::{Frame, Paintable};
|
||||
|
||||
use gtk::prelude::*;
|
||||
use gtk::subclass::prelude::*;
|
||||
use gtk::{gdk, glib};
|
||||
|
||||
use gst::{gst_debug, gst_trace};
|
||||
|
||||
mod imp;
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct SinkPaintable(ObjectSubclass<imp::SinkPaintable>)
|
||||
@implements gdk::Paintable;
|
||||
}
|
||||
|
||||
impl SinkPaintable {
|
||||
pub fn new() -> Self {
|
||||
glib::Object::new(&[]).expect("Failed to create a SinkPaintable")
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SinkPaintable {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl SinkPaintable {
|
||||
pub(crate) fn handle_frame_changed(&self, frame: Option<Frame>) {
|
||||
let self_ = imp::SinkPaintable::from_instance(self);
|
||||
if let Some(frame) = frame {
|
||||
gst_trace!(imp::CAT, obj: self, "Received new frame");
|
||||
|
||||
let paintable: Paintable = frame.into();
|
||||
let new_size = (paintable.width(), paintable.height());
|
||||
|
||||
let old_paintable = self_.paintable.replace(Some(paintable));
|
||||
let old_size = old_paintable.map(|p| (p.width(), p.height()));
|
||||
|
||||
if Some(new_size) != old_size {
|
||||
gst_debug!(
|
||||
imp::CAT,
|
||||
obj: self,
|
||||
"Size changed from {:?} to {:?}",
|
||||
old_size,
|
||||
new_size,
|
||||
);
|
||||
self.invalidate_size();
|
||||
}
|
||||
|
||||
self.invalidate_contents();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue