2021-04-09 12:38:02 +00:00
|
|
|
//
|
|
|
|
// 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;
|
2022-10-27 17:50:40 +00:00
|
|
|
use crate::sink::paintable::Paintable;
|
2021-04-09 12:38:02 +00:00
|
|
|
|
2022-12-22 19:34:08 +00:00
|
|
|
use glib::{thread_guard::ThreadGuard, Sender};
|
2021-10-19 06:45:07 +00:00
|
|
|
use gtk::prelude::*;
|
|
|
|
use gtk::{gdk, glib};
|
2021-04-09 12:38:02 +00:00
|
|
|
|
|
|
|
use gst::subclass::prelude::*;
|
|
|
|
use gst_base::subclass::prelude::*;
|
|
|
|
use gst_video::subclass::prelude::*;
|
|
|
|
|
|
|
|
use once_cell::sync::Lazy;
|
2021-10-19 06:45:07 +00:00
|
|
|
use std::sync::{Mutex, MutexGuard};
|
2021-04-09 12:38:02 +00:00
|
|
|
|
2021-10-19 06:45:07 +00:00
|
|
|
use crate::utils;
|
2021-04-09 12:38:02 +00:00
|
|
|
|
2022-12-28 17:57:54 +00:00
|
|
|
#[cfg(any(target_os = "macos", feature = "gst_gl"))]
|
2022-11-30 09:55:05 +00:00
|
|
|
use gst_gl::prelude::GLContextExt as GstGLContextExt;
|
2022-12-28 17:57:54 +00:00
|
|
|
#[cfg(any(target_os = "macos", feature = "gst_gl"))]
|
2022-11-30 09:55:05 +00:00
|
|
|
use gst_gl::prelude::*;
|
2023-02-21 19:15:49 +00:00
|
|
|
|
|
|
|
// Global GL context that is created by the first sink and kept around until the end of the
|
|
|
|
// process. This is provided to other elements in the pipeline to make sure they create GL contexts
|
|
|
|
// that are sharing with the GTK GL context.
|
2022-12-28 17:57:54 +00:00
|
|
|
#[cfg(any(target_os = "macos", feature = "gst_gl"))]
|
2023-02-21 19:15:49 +00:00
|
|
|
enum GLContext {
|
|
|
|
Uninitialized,
|
|
|
|
Unsupported,
|
|
|
|
Initialized {
|
|
|
|
display: gst_gl::GLDisplay,
|
|
|
|
wrapped_context: gst_gl::GLContext,
|
|
|
|
gdk_context: ThreadGuard<gdk::GLContext>,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(any(target_os = "macos", feature = "gst_gl"))]
|
|
|
|
static GL_CONTEXT: Mutex<GLContext> = Mutex::new(GLContext::Uninitialized);
|
2022-11-30 09:55:05 +00:00
|
|
|
|
2021-10-19 06:45:07 +00:00
|
|
|
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
2021-04-09 12:38:02 +00:00
|
|
|
gst::DebugCategory::new(
|
2022-12-28 14:04:33 +00:00
|
|
|
"gtk4paintablesink",
|
2021-04-09 12:38:02 +00:00
|
|
|
gst::DebugColorFlags::empty(),
|
|
|
|
Some("GTK4 Paintable sink"),
|
|
|
|
)
|
|
|
|
});
|
|
|
|
|
2022-12-22 19:34:08 +00:00
|
|
|
#[derive(Default)]
|
2021-04-09 12:38:02 +00:00
|
|
|
pub struct PaintableSink {
|
2022-12-22 19:34:08 +00:00
|
|
|
paintable: Mutex<Option<ThreadGuard<Paintable>>>,
|
2021-04-09 12:38:02 +00:00
|
|
|
info: Mutex<Option<gst_video::VideoInfo>>,
|
2021-10-19 06:45:07 +00:00
|
|
|
sender: Mutex<Option<Sender<SinkEvent>>>,
|
|
|
|
pending_frame: Mutex<Option<Frame>>,
|
|
|
|
cached_caps: Mutex<Option<gst::Caps>>,
|
2021-04-09 12:38:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Drop for PaintableSink {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
let mut paintable = self.paintable.lock().unwrap();
|
|
|
|
if let Some(paintable) = paintable.take() {
|
2021-10-19 06:45:07 +00:00
|
|
|
glib::MainContext::default().invoke(|| drop(paintable))
|
2021-04-09 12:38:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[glib::object_subclass]
|
|
|
|
impl ObjectSubclass for PaintableSink {
|
2022-10-23 15:42:58 +00:00
|
|
|
const NAME: &'static str = "GstGtk4PaintableSink";
|
2021-04-09 12:38:02 +00:00
|
|
|
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(|| {
|
2022-08-18 12:04:15 +00:00
|
|
|
vec![
|
2021-10-19 06:45:07 +00:00
|
|
|
glib::ParamSpecObject::builder::<gdk::Paintable>("paintable")
|
2022-08-18 12:04:15 +00:00
|
|
|
.nick("Paintable")
|
|
|
|
.blurb("The Paintable the sink renders to")
|
|
|
|
.read_only()
|
|
|
|
.build(),
|
|
|
|
]
|
2021-04-09 12:38:02 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
PROPERTIES.as_ref()
|
|
|
|
}
|
|
|
|
|
2022-10-09 13:06:59 +00:00
|
|
|
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
2021-04-09 12:38:02 +00:00
|
|
|
match pspec.name() {
|
|
|
|
"paintable" => {
|
|
|
|
let mut paintable = self.paintable.lock().unwrap();
|
|
|
|
if paintable.is_none() {
|
2021-10-19 06:45:07 +00:00
|
|
|
self.create_paintable(&mut paintable);
|
2021-04-09 12:38:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let paintable = match &*paintable {
|
|
|
|
Some(ref paintable) => paintable,
|
|
|
|
None => {
|
2022-10-09 13:06:59 +00:00
|
|
|
gst::error!(CAT, imp: self, "Failed to create paintable");
|
2021-10-19 06:45:07 +00:00
|
|
|
return None::<&gdk::Paintable>.to_value();
|
2021-04-09 12:38:02 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Getter must be called from the main thread
|
2022-12-22 19:34:08 +00:00
|
|
|
if paintable.is_owner() {
|
|
|
|
paintable.get_ref().to_value()
|
|
|
|
} else {
|
|
|
|
gst::error!(
|
|
|
|
CAT,
|
|
|
|
imp: self,
|
|
|
|
"Can't retrieve Paintable from non-main thread"
|
|
|
|
);
|
|
|
|
None::<&gdk::Paintable>.to_value()
|
2021-04-09 12:38:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => unimplemented!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-23 08:57:31 +00:00
|
|
|
impl GstObjectImpl for PaintableSink {}
|
|
|
|
|
2021-04-09 12:38:02 +00:00
|
|
|
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
|
2021-10-17 17:23:43 +00:00
|
|
|
let mut caps = gst::Caps::new_empty();
|
|
|
|
{
|
|
|
|
let caps = caps.get_mut().unwrap();
|
|
|
|
|
|
|
|
for features in [
|
|
|
|
None,
|
2023-02-21 19:15:49 +00:00
|
|
|
#[cfg(any(target_os = "macos", feature = "gst_gl"))]
|
2023-01-15 20:57:14 +00:00
|
|
|
Some(gst::CapsFeatures::new([
|
|
|
|
"memory:GLMemory",
|
|
|
|
"meta:GstVideoOverlayComposition",
|
|
|
|
])),
|
2023-02-21 19:15:49 +00:00
|
|
|
#[cfg(any(target_os = "macos", feature = "gst_gl"))]
|
2023-01-15 20:57:14 +00:00
|
|
|
Some(gst::CapsFeatures::new(["memory:GLMemory"])),
|
|
|
|
Some(gst::CapsFeatures::new([
|
|
|
|
"memory:SystemMemory",
|
|
|
|
"meta:GstVideoOverlayComposition",
|
|
|
|
])),
|
|
|
|
Some(gst::CapsFeatures::new(["meta:GstVideoOverlayComposition"])),
|
2021-10-17 17:23:43 +00:00
|
|
|
] {
|
|
|
|
let mut c = 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,
|
|
|
|
])
|
|
|
|
.build();
|
|
|
|
|
|
|
|
if let Some(features) = features {
|
2023-01-15 20:57:14 +00:00
|
|
|
let c = c.get_mut().unwrap();
|
|
|
|
|
|
|
|
if features.contains("memory:GLMemory") {
|
|
|
|
c.set("texture-target", "2D")
|
2021-10-19 06:45:07 +00:00
|
|
|
}
|
2023-01-15 20:57:14 +00:00
|
|
|
c.set_features_simple(Some(features));
|
2021-10-17 17:23:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
caps.append(c);
|
|
|
|
}
|
|
|
|
}
|
2021-04-09 12:38:02 +00:00
|
|
|
|
|
|
|
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,
|
|
|
|
transition: gst::StateChange,
|
|
|
|
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
|
|
|
match transition {
|
|
|
|
gst::StateChange::NullToReady => {
|
|
|
|
let mut paintable = self.paintable.lock().unwrap();
|
2021-10-19 06:45:07 +00:00
|
|
|
|
2021-04-09 12:38:02 +00:00
|
|
|
if paintable.is_none() {
|
2021-10-19 06:45:07 +00:00
|
|
|
self.create_paintable(&mut paintable);
|
2021-04-09 12:38:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if paintable.is_none() {
|
2022-10-09 13:06:59 +00:00
|
|
|
gst::error!(CAT, imp: self, "Failed to create paintable");
|
2021-04-09 12:38:02 +00:00
|
|
|
return Err(gst::StateChangeError);
|
|
|
|
}
|
2022-12-22 20:29:22 +00:00
|
|
|
|
|
|
|
drop(paintable);
|
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
// Notify the pipeline about the GL display and wrapped context so that any other
|
|
|
|
// elements in the pipeline ideally use the same / create GL contexts that are
|
|
|
|
// sharing with this one.
|
2022-12-28 17:57:54 +00:00
|
|
|
#[cfg(any(target_os = "macos", feature = "gst_gl"))]
|
2022-12-22 20:29:22 +00:00
|
|
|
{
|
2023-02-21 19:15:49 +00:00
|
|
|
let gl_context = GL_CONTEXT.lock().unwrap();
|
|
|
|
if let GLContext::Initialized {
|
|
|
|
display,
|
|
|
|
wrapped_context,
|
|
|
|
..
|
|
|
|
} = &*gl_context
|
|
|
|
{
|
|
|
|
let display = display.clone();
|
|
|
|
let wrapped_context = wrapped_context.clone();
|
|
|
|
drop(gl_context);
|
|
|
|
|
|
|
|
gst_gl::gl_element_propagate_display_context(&*self.obj(), &display);
|
|
|
|
let mut ctx = gst::Context::new("gst.gl.app_context", true);
|
|
|
|
{
|
|
|
|
let ctx = ctx.get_mut().unwrap();
|
|
|
|
ctx.structure_mut().set("context", &wrapped_context);
|
2023-01-07 10:42:42 +00:00
|
|
|
}
|
2023-02-21 19:15:49 +00:00
|
|
|
let _ = self.obj().post_message(
|
|
|
|
gst::message::HaveContext::builder(ctx)
|
|
|
|
.src(&*self.obj())
|
|
|
|
.build(),
|
|
|
|
);
|
2022-12-22 20:29:22 +00:00
|
|
|
}
|
|
|
|
}
|
2021-04-09 12:38:02 +00:00
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
|
2022-10-09 13:06:59 +00:00
|
|
|
let res = self.parent_change_state(transition);
|
2021-04-09 12:38:02 +00:00
|
|
|
|
|
|
|
match transition {
|
|
|
|
gst::StateChange::PausedToReady => {
|
|
|
|
let _ = self.info.lock().unwrap().take();
|
|
|
|
let _ = self.pending_frame.lock().unwrap().take();
|
2022-12-22 18:44:25 +00:00
|
|
|
|
2023-01-19 11:53:21 +00:00
|
|
|
// Flush frames from the GDK paintable but don't wait
|
|
|
|
// for this to finish as this can other deadlock.
|
2022-12-22 18:44:25 +00:00
|
|
|
let self_ = self.to_owned();
|
2023-01-19 11:53:21 +00:00
|
|
|
glib::MainContext::default().invoke(move || {
|
2022-12-22 18:44:25 +00:00
|
|
|
let paintable = self_.paintable.lock().unwrap();
|
|
|
|
if let Some(paintable) = &*paintable {
|
2022-12-22 19:34:08 +00:00
|
|
|
paintable.get_ref().handle_flush_frames();
|
2022-12-22 18:44:25 +00:00
|
|
|
}
|
|
|
|
});
|
2021-04-09 12:38:02 +00:00
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
|
|
|
|
res
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl BaseSinkImpl for PaintableSink {
|
2021-10-19 06:45:07 +00:00
|
|
|
fn caps(&self, filter: Option<&gst::Caps>) -> Option<gst::Caps> {
|
|
|
|
let cached_caps = self
|
|
|
|
.cached_caps
|
|
|
|
.lock()
|
|
|
|
.expect("Failed to lock cached caps mutex")
|
|
|
|
.clone();
|
|
|
|
|
|
|
|
let mut tmp_caps = cached_caps.unwrap_or_else(|| {
|
|
|
|
let templ = Self::pad_templates();
|
|
|
|
templ[0].caps().clone()
|
|
|
|
});
|
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
gst::debug!(CAT, imp: self, "Advertising our own caps: {tmp_caps:?}");
|
2021-10-19 06:45:07 +00:00
|
|
|
|
|
|
|
if let Some(filter_caps) = filter {
|
|
|
|
gst::debug!(
|
|
|
|
CAT,
|
|
|
|
imp: self,
|
2023-02-21 19:15:49 +00:00
|
|
|
"Intersecting with filter caps: {filter_caps:?}",
|
2021-10-19 06:45:07 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
tmp_caps = filter_caps.intersect_with_mode(&tmp_caps, gst::CapsIntersectMode::First);
|
|
|
|
};
|
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
gst::debug!(CAT, imp: self, "Returning caps: {tmp_caps:?}");
|
2021-10-19 06:45:07 +00:00
|
|
|
Some(tmp_caps)
|
|
|
|
}
|
|
|
|
|
2022-10-09 13:06:59 +00:00
|
|
|
fn set_caps(&self, caps: &gst::Caps) -> Result<(), gst::LoggableError> {
|
2023-02-21 19:15:49 +00:00
|
|
|
gst::debug!(CAT, imp: self, "Setting caps {caps:?}");
|
2021-04-09 12:38:02 +00:00
|
|
|
|
|
|
|
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(())
|
|
|
|
}
|
2021-10-16 12:02:23 +00:00
|
|
|
|
|
|
|
fn propose_allocation(
|
|
|
|
&self,
|
2022-01-22 10:18:02 +00:00
|
|
|
query: &mut gst::query::Allocation,
|
2021-12-06 17:11:06 +00:00
|
|
|
) -> Result<(), gst::LoggableError> {
|
2021-10-19 06:45:07 +00:00
|
|
|
gst::debug!(CAT, imp: self, "Proposing Allocation query");
|
|
|
|
|
|
|
|
self.parent_propose_allocation(query)?;
|
2021-10-16 12:02:23 +00:00
|
|
|
|
2021-10-19 06:45:07 +00:00
|
|
|
query.add_allocation_meta::<gst_video::VideoMeta>(None);
|
2021-10-17 17:23:43 +00:00
|
|
|
// TODO: Provide a preferred "window size" here for higher-resolution rendering
|
|
|
|
query.add_allocation_meta::<gst_video::VideoOverlayCompositionMeta>(None);
|
|
|
|
|
2022-12-28 17:57:54 +00:00
|
|
|
#[cfg(any(target_os = "macos", feature = "gst_gl"))]
|
2022-11-30 09:55:05 +00:00
|
|
|
{
|
2023-02-21 19:15:49 +00:00
|
|
|
if let GLContext::Initialized {
|
|
|
|
wrapped_context, ..
|
|
|
|
} = &*GL_CONTEXT.lock().unwrap()
|
2022-12-29 08:55:13 +00:00
|
|
|
{
|
2023-02-21 19:15:49 +00:00
|
|
|
if wrapped_context.check_feature("GL_ARB_sync")
|
|
|
|
|| wrapped_context.check_feature("GL_EXT_EGL_sync")
|
|
|
|
{
|
|
|
|
query.add_allocation_meta::<gst_gl::GLSyncMeta>(None)
|
|
|
|
}
|
2021-10-19 06:45:07 +00:00
|
|
|
}
|
2022-11-30 09:55:05 +00:00
|
|
|
}
|
2023-02-21 19:15:49 +00:00
|
|
|
|
|
|
|
Ok(())
|
2021-10-19 06:45:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn query(&self, query: &mut gst::QueryRef) -> bool {
|
|
|
|
gst::log!(CAT, imp: self, "Handling query {:?}", query);
|
|
|
|
|
|
|
|
match query.view_mut() {
|
2022-12-28 17:57:54 +00:00
|
|
|
#[cfg(any(target_os = "macos", feature = "gst_gl"))]
|
2021-10-19 06:45:07 +00:00
|
|
|
gst::QueryViewMut::Context(q) => {
|
|
|
|
// Avoid holding the locks while we respond to the query
|
|
|
|
// The objects are ref-counted anyway.
|
2023-02-21 19:15:49 +00:00
|
|
|
let mut display_clone = None;
|
|
|
|
let mut wrapped_context_clone = None;
|
|
|
|
if let GLContext::Initialized {
|
|
|
|
display,
|
|
|
|
wrapped_context,
|
|
|
|
..
|
|
|
|
} = &*GL_CONTEXT.lock().unwrap()
|
|
|
|
{
|
|
|
|
display_clone = Some(display.clone());
|
|
|
|
wrapped_context_clone = Some(wrapped_context.clone());
|
|
|
|
}
|
2021-10-19 06:45:07 +00:00
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
if let (Some(display), Some(wrapped_context)) =
|
|
|
|
(display_clone, wrapped_context_clone)
|
2022-12-29 08:55:13 +00:00
|
|
|
{
|
2021-10-19 06:45:07 +00:00
|
|
|
return gst_gl::functions::gl_handle_context_query(
|
2022-12-22 20:29:22 +00:00
|
|
|
&*self.obj(),
|
2021-10-19 06:45:07 +00:00
|
|
|
q,
|
2023-02-21 19:15:49 +00:00
|
|
|
Some(&display),
|
|
|
|
None::<&gst_gl::GLContext>,
|
|
|
|
Some(&wrapped_context),
|
2021-10-19 06:45:07 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
BaseSinkImplExt::parent_query(self, query)
|
|
|
|
}
|
|
|
|
_ => BaseSinkImplExt::parent_query(self, query),
|
|
|
|
}
|
2021-10-16 12:02:23 +00:00
|
|
|
}
|
2021-04-09 12:38:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl VideoSinkImpl for PaintableSink {
|
2022-10-09 13:06:59 +00:00
|
|
|
fn show_frame(&self, buffer: &gst::Buffer) -> Result<gst::FlowSuccess, gst::FlowError> {
|
|
|
|
gst::trace!(CAT, imp: self, "Rendering buffer {:?}", buffer);
|
2021-04-09 12:38:02 +00:00
|
|
|
|
2021-10-19 06:45:07 +00:00
|
|
|
// Empty buffer, nothing to render
|
|
|
|
if buffer.n_memory() == 0 {
|
|
|
|
gst::trace!(
|
|
|
|
CAT,
|
|
|
|
imp: self,
|
|
|
|
"Empty buffer, nothing to render. Returning."
|
|
|
|
);
|
|
|
|
return Ok(gst::FlowSuccess::Ok);
|
|
|
|
};
|
|
|
|
|
2021-04-09 12:38:02 +00:00
|
|
|
let info = self.info.lock().unwrap();
|
|
|
|
let info = info.as_ref().ok_or_else(|| {
|
2022-10-09 13:06:59 +00:00
|
|
|
gst::error!(CAT, imp: self, "Received no caps yet");
|
2021-04-09 12:38:02 +00:00
|
|
|
gst::FlowError::NotNegotiated
|
|
|
|
})?;
|
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
let wrapped_context = {
|
2022-12-28 17:57:54 +00:00
|
|
|
#[cfg(not(any(target_os = "macos", feature = "gst_gl")))]
|
2022-11-30 09:55:05 +00:00
|
|
|
{
|
2023-02-21 19:15:49 +00:00
|
|
|
None
|
2022-11-30 09:55:05 +00:00
|
|
|
}
|
2022-12-28 17:57:54 +00:00
|
|
|
#[cfg(any(target_os = "macos", feature = "gst_gl"))]
|
2022-11-30 09:55:05 +00:00
|
|
|
{
|
2023-02-21 19:15:49 +00:00
|
|
|
let gl_context = GL_CONTEXT.lock().unwrap();
|
|
|
|
if let GLContext::Initialized {
|
|
|
|
wrapped_context, ..
|
|
|
|
} = &*gl_context
|
|
|
|
{
|
|
|
|
Some(wrapped_context.clone())
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
2022-11-30 09:55:05 +00:00
|
|
|
}
|
|
|
|
};
|
2023-02-21 19:15:49 +00:00
|
|
|
let frame = Frame::new(buffer, info, wrapped_context.as_ref()).map_err(|err| {
|
2022-11-30 09:55:05 +00:00
|
|
|
gst::error!(CAT, imp: self, "Failed to map video frame");
|
|
|
|
err
|
|
|
|
})?;
|
2021-04-09 12:38:02 +00:00
|
|
|
self.pending_frame.lock().unwrap().replace(frame);
|
|
|
|
|
|
|
|
let sender = self.sender.lock().unwrap();
|
|
|
|
let sender = sender.as_ref().ok_or_else(|| {
|
2022-10-09 13:06:59 +00:00
|
|
|
gst::error!(CAT, imp: self, "Have no main thread sender");
|
2022-12-22 18:45:21 +00:00
|
|
|
gst::FlowError::Flushing
|
2021-04-09 12:38:02 +00:00
|
|
|
})?;
|
|
|
|
|
|
|
|
sender.send(SinkEvent::FrameChanged).map_err(|_| {
|
2022-10-09 13:06:59 +00:00
|
|
|
gst::error!(CAT, imp: self, "Have main thread receiver shut down");
|
2022-12-22 18:45:21 +00:00
|
|
|
gst::FlowError::Flushing
|
2021-04-09 12:38:02 +00:00
|
|
|
})?;
|
|
|
|
|
|
|
|
Ok(gst::FlowSuccess::Ok)
|
|
|
|
}
|
|
|
|
}
|
2021-10-19 06:45:07 +00:00
|
|
|
|
|
|
|
impl PaintableSink {
|
|
|
|
fn pending_frame(&self) -> Option<Frame> {
|
|
|
|
self.pending_frame.lock().unwrap().take()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn do_action(&self, action: SinkEvent) -> glib::Continue {
|
2022-12-22 19:34:08 +00:00
|
|
|
let paintable = self.paintable.lock().unwrap();
|
|
|
|
let paintable = match &*paintable {
|
2022-12-22 20:29:22 +00:00
|
|
|
Some(paintable) => paintable,
|
2021-10-19 06:45:07 +00:00
|
|
|
None => return glib::Continue(false),
|
|
|
|
};
|
|
|
|
|
|
|
|
match action {
|
|
|
|
SinkEvent::FrameChanged => {
|
|
|
|
gst::trace!(CAT, imp: self, "Frame changed");
|
2022-12-22 19:34:08 +00:00
|
|
|
paintable
|
|
|
|
.get_ref()
|
|
|
|
.handle_frame_changed(self.pending_frame())
|
2021-10-19 06:45:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
glib::Continue(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn configure_caps(&self) {
|
2022-11-30 09:55:05 +00:00
|
|
|
#[allow(unused_mut)]
|
2021-10-19 06:45:07 +00:00
|
|
|
let mut tmp_caps = Self::pad_templates()[0].caps().clone();
|
|
|
|
|
2022-12-28 17:57:54 +00:00
|
|
|
#[cfg(any(target_os = "macos", feature = "gst_gl"))]
|
2022-11-30 09:55:05 +00:00
|
|
|
{
|
|
|
|
// Filter out GL caps from the template pads if we have no context
|
2023-02-21 19:15:49 +00:00
|
|
|
if !matches!(&*GL_CONTEXT.lock().unwrap(), GLContext::Initialized { .. }) {
|
2022-11-30 09:55:05 +00:00
|
|
|
tmp_caps = tmp_caps
|
|
|
|
.iter_with_features()
|
|
|
|
.filter(|(_, features)| !features.contains("memory:GLMemory"))
|
|
|
|
.map(|(s, c)| (s.to_owned(), c.to_owned()))
|
|
|
|
.collect::<gst::Caps>();
|
|
|
|
}
|
2021-10-19 06:45:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
self.cached_caps
|
|
|
|
.lock()
|
|
|
|
.expect("Failed to lock Mutex")
|
|
|
|
.replace(tmp_caps);
|
|
|
|
}
|
|
|
|
|
2022-12-22 19:34:08 +00:00
|
|
|
fn create_paintable(&self, paintable_storage: &mut MutexGuard<Option<ThreadGuard<Paintable>>>) {
|
2022-12-28 17:57:54 +00:00
|
|
|
#[cfg(any(target_os = "macos", feature = "gst_gl"))]
|
2022-11-30 09:55:05 +00:00
|
|
|
{
|
2023-02-21 19:15:49 +00:00
|
|
|
self.initialize_gl_context();
|
2022-11-30 09:55:05 +00:00
|
|
|
}
|
2021-10-19 06:45:07 +00:00
|
|
|
|
|
|
|
self.configure_caps();
|
2023-02-21 19:15:49 +00:00
|
|
|
self.initialize_paintable(paintable_storage);
|
2021-10-19 06:45:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn initialize_paintable(
|
|
|
|
&self,
|
2022-12-22 19:34:08 +00:00
|
|
|
paintable_storage: &mut MutexGuard<Option<ThreadGuard<Paintable>>>,
|
2021-10-19 06:45:07 +00:00
|
|
|
) {
|
|
|
|
gst::debug!(CAT, imp: self, "Initializing paintable");
|
|
|
|
|
2023-02-22 08:02:45 +00:00
|
|
|
// The channel for the SinkEvents
|
2023-03-12 08:27:16 +00:00
|
|
|
let (sender, receiver) = glib::MainContext::channel(glib::Priority::DEFAULT);
|
2023-02-22 08:02:45 +00:00
|
|
|
let self_ = self.to_owned();
|
|
|
|
|
|
|
|
let paintable = utils::invoke_on_main_thread(move || {
|
|
|
|
// Attach the receiver from the main thread to make sure it is called
|
|
|
|
// from a place where it can acquire the default main context.
|
|
|
|
receiver.attach(
|
|
|
|
Some(&glib::MainContext::default()),
|
|
|
|
glib::clone!(
|
|
|
|
@weak self_ => @default-return glib::Continue(false),
|
|
|
|
move |action| self_.do_action(action)
|
|
|
|
),
|
|
|
|
);
|
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
#[cfg(any(target_os = "macos", feature = "gst_gl"))]
|
|
|
|
{
|
|
|
|
let gdk_context = if let GLContext::Initialized { gdk_context, .. } =
|
|
|
|
&*GL_CONTEXT.lock().unwrap()
|
|
|
|
{
|
|
|
|
Some(gdk_context.get_ref().clone())
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
ThreadGuard::new(Paintable::new(gdk_context))
|
|
|
|
}
|
|
|
|
#[cfg(not(any(target_os = "macos", feature = "gst_gl")))]
|
|
|
|
{
|
|
|
|
ThreadGuard::new(Paintable::new(None))
|
|
|
|
}
|
2021-10-19 06:45:07 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
**paintable_storage = Some(paintable);
|
|
|
|
|
|
|
|
*self.sender.lock().unwrap() = Some(sender);
|
|
|
|
}
|
|
|
|
|
2022-12-28 17:57:54 +00:00
|
|
|
#[cfg(any(target_os = "macos", feature = "gst_gl"))]
|
2023-02-21 19:15:49 +00:00
|
|
|
fn initialize_gl_context(&self) {
|
2021-10-19 06:45:07 +00:00
|
|
|
gst::debug!(CAT, imp: self, "Realizing GDK GL Context");
|
|
|
|
|
2022-12-22 20:29:22 +00:00
|
|
|
let self_ = self.to_owned();
|
2023-02-21 19:15:49 +00:00
|
|
|
utils::invoke_on_main_thread(move || {
|
|
|
|
self_.initialize_gl_context_main();
|
|
|
|
});
|
|
|
|
}
|
2021-10-19 06:45:07 +00:00
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
#[cfg(any(target_os = "macos", feature = "gst_gl"))]
|
|
|
|
fn initialize_gl_context_main(&self) {
|
|
|
|
gst::debug!(CAT, imp: self, "Realizing GDK GL Context from main thread");
|
2021-10-19 06:45:07 +00:00
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
let mut gl_context_guard = GL_CONTEXT.lock().unwrap();
|
|
|
|
if !matches!(&*gl_context_guard, GLContext::Uninitialized) {
|
|
|
|
gst::debug!(CAT, imp: self, "Already initialized GL context before");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
*gl_context_guard = GLContext::Unsupported;
|
|
|
|
|
|
|
|
// This can return NULL but only happens in 2 situations:
|
|
|
|
// * If the function is called before gtk_init
|
|
|
|
// * If the function is called after gdk_display_close(default_display)
|
|
|
|
// Both of which are treated as programming errors.
|
|
|
|
//
|
|
|
|
// However, when we are building the docs, gtk_init doesn't get called
|
|
|
|
// and this would cause the documentation generation to error.
|
|
|
|
// Thus its okayish to return None here and fallback to software
|
|
|
|
// rendering, since this path isn't going to be used by applications
|
|
|
|
// anyway.
|
|
|
|
//
|
|
|
|
// FIXME: add a couple more gtk_init checks across the codebase where
|
|
|
|
// applicable since this is no longer going to panic.
|
|
|
|
let gdk_display = match gdk::Display::default() {
|
|
|
|
Some(display) => display,
|
|
|
|
None => {
|
|
|
|
gst::warning!(CAT, imp: self, "Failed to retrieve GDK display");
|
|
|
|
return;
|
2022-12-22 20:29:22 +00:00
|
|
|
}
|
2023-02-21 19:15:49 +00:00
|
|
|
};
|
|
|
|
let gdk_context = match gdk_display.create_gl_context() {
|
|
|
|
Ok(gdk_context) => gdk_context,
|
|
|
|
Err(err) => {
|
|
|
|
gst::warning!(CAT, imp: self, "Failed to create GDK GL Context: {err}");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
2022-12-22 20:29:22 +00:00
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
match gdk_context.type_().name() {
|
|
|
|
#[cfg(all(target_os = "linux", feature = "x11egl"))]
|
|
|
|
"GdkX11GLContextEGL" => (),
|
|
|
|
#[cfg(all(target_os = "linux", feature = "x11glx"))]
|
|
|
|
"GdkX11GLContextGLX" => (),
|
|
|
|
#[cfg(all(target_os = "linux", feature = "wayland"))]
|
|
|
|
"GdkWaylandGLContext" => (),
|
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
"GdkMacosGLContext" => (),
|
|
|
|
display => {
|
|
|
|
gst::error!(CAT, imp: self, "Unsupported GDK display {display} for GL");
|
|
|
|
return;
|
2022-12-22 20:29:22 +00:00
|
|
|
}
|
2023-02-21 19:15:49 +00:00
|
|
|
}
|
2021-10-19 06:45:07 +00:00
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
gst::info!(CAT, imp: self, "Realizing GDK GL Context",);
|
2021-10-19 06:45:07 +00:00
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
if let Err(err) = gdk_context.realize() {
|
|
|
|
gst::warning!(CAT, imp: self, "Failed to realize GDK GL Context: {err}");
|
|
|
|
return;
|
|
|
|
}
|
2022-12-22 20:29:22 +00:00
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
gst::info!(CAT, imp: self, "Successfully realized GDK GL Context");
|
2021-10-19 06:45:07 +00:00
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
gdk_context.make_current();
|
2021-10-19 06:45:07 +00:00
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
let res = match gdk_context.type_().name() {
|
2021-10-19 06:45:07 +00:00
|
|
|
#[cfg(all(target_os = "linux", feature = "x11egl"))]
|
2023-02-21 19:15:49 +00:00
|
|
|
"GdkX11GLContextEGL" => self.initialize_x11egl(gdk_display),
|
2021-10-19 06:45:07 +00:00
|
|
|
#[cfg(all(target_os = "linux", feature = "x11glx"))]
|
2023-02-21 19:15:49 +00:00
|
|
|
"GdkX11GLContextGLX" => self.initialize_x11glx(gdk_display),
|
2021-10-19 06:45:07 +00:00
|
|
|
#[cfg(all(target_os = "linux", feature = "wayland"))]
|
2023-02-21 19:15:49 +00:00
|
|
|
"GdkWaylandGLContext" => self.initialize_waylandegl(gdk_display),
|
2022-12-28 17:57:54 +00:00
|
|
|
#[cfg(target_os = "macos")]
|
2023-02-21 19:15:49 +00:00
|
|
|
"GdkMacosGLContext" => self.initialize_macosgl(gdk_display),
|
|
|
|
display_type => {
|
|
|
|
unreachable!("Unsupported GDK display {display_type} for GL");
|
2021-10-19 06:45:07 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
let (display, wrapped_context) = match res {
|
|
|
|
Some((display, wrapped_context)) => (display, wrapped_context),
|
2022-12-29 08:55:13 +00:00
|
|
|
None => {
|
2023-02-21 19:15:49 +00:00
|
|
|
return;
|
2022-12-29 08:55:13 +00:00
|
|
|
}
|
|
|
|
};
|
2021-10-19 06:45:07 +00:00
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
match wrapped_context.activate(true) {
|
|
|
|
Ok(_) => gst::info!(CAT, imp: self, "Successfully activated GL Context"),
|
2021-10-19 06:45:07 +00:00
|
|
|
Err(_) => {
|
|
|
|
gst::error!(CAT, imp: self, "Failed to activate GL context",);
|
2023-02-21 19:15:49 +00:00
|
|
|
return;
|
2021-10-19 06:45:07 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
if let Err(err) = wrapped_context.fill_info() {
|
2022-12-22 20:29:22 +00:00
|
|
|
gst::error!(
|
|
|
|
CAT,
|
|
|
|
imp: self,
|
|
|
|
"Failed to fill info on the GL Context: {err}",
|
|
|
|
);
|
|
|
|
// Deactivate the context upon failure
|
2023-02-21 19:15:49 +00:00
|
|
|
if wrapped_context.activate(false).is_err() {
|
2021-10-19 06:45:07 +00:00
|
|
|
gst::error!(
|
|
|
|
CAT,
|
|
|
|
imp: self,
|
2022-12-22 20:29:22 +00:00
|
|
|
"Failed to deactivate the context after failing fill info",
|
2021-10-19 06:45:07 +00:00
|
|
|
);
|
|
|
|
}
|
2023-02-21 19:15:49 +00:00
|
|
|
return;
|
2022-12-22 20:29:22 +00:00
|
|
|
}
|
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
gst::info!(CAT, imp: self, "Successfully initialized GL Context");
|
2023-01-07 10:42:42 +00:00
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
*gl_context_guard = GLContext::Initialized {
|
|
|
|
display,
|
|
|
|
wrapped_context,
|
|
|
|
gdk_context: ThreadGuard::new(gdk_context),
|
|
|
|
};
|
2021-10-19 06:45:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(all(target_os = "linux", feature = "x11egl"))]
|
|
|
|
fn initialize_x11egl(
|
|
|
|
&self,
|
|
|
|
display: gdk::Display,
|
2023-02-21 19:15:49 +00:00
|
|
|
) -> Option<(gst_gl::GLDisplay, gst_gl::GLContext)> {
|
2021-10-19 06:45:07 +00:00
|
|
|
gst::info!(
|
|
|
|
CAT,
|
|
|
|
imp: self,
|
2023-02-21 19:15:49 +00:00
|
|
|
"Initializing GL for x11 EGL backend and display"
|
2021-10-19 06:45:07 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
let platform = gst_gl::GLPlatform::EGL;
|
|
|
|
let (gl_api, _, _) = gst_gl::GLContext::current_gl_api(platform);
|
|
|
|
let gl_ctx = gst_gl::GLContext::current_gl_context(platform);
|
|
|
|
|
2022-12-22 20:29:22 +00:00
|
|
|
if gl_ctx == 0 {
|
2023-02-21 19:15:49 +00:00
|
|
|
gst::error!(CAT, imp: self, "Failed to get handle from GdkGLContext");
|
|
|
|
return None;
|
2022-12-22 20:29:22 +00:00
|
|
|
}
|
2021-10-19 06:45:07 +00:00
|
|
|
|
2022-12-22 20:29:22 +00:00
|
|
|
// FIXME: bindings
|
|
|
|
unsafe {
|
2022-12-29 09:10:13 +00:00
|
|
|
use glib::translate::*;
|
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
let display = display.downcast::<gdk_x11::X11Display>().unwrap();
|
|
|
|
let x11_display =
|
|
|
|
gdk_x11::ffi::gdk_x11_display_get_egl_display(display.to_glib_none().0);
|
2022-12-29 09:10:13 +00:00
|
|
|
if x11_display.is_null() {
|
|
|
|
gst::error!(CAT, imp: self, "Failed to get EGL display");
|
2023-02-21 19:15:49 +00:00
|
|
|
return None;
|
2022-12-29 09:10:13 +00:00
|
|
|
}
|
2021-10-19 06:45:07 +00:00
|
|
|
|
2022-12-22 20:29:22 +00:00
|
|
|
let gst_display = gst_gl_egl::ffi::gst_gl_display_egl_new_with_egl_display(x11_display);
|
2022-12-29 09:10:13 +00:00
|
|
|
let gst_display =
|
|
|
|
gst_gl::GLDisplay::from_glib_full(gst_display as *mut gst_gl::ffi::GstGLDisplay);
|
2021-10-19 06:45:07 +00:00
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
let wrapped_context =
|
2022-12-22 20:29:22 +00:00
|
|
|
gst_gl::GLContext::new_wrapped(&gst_display, gl_ctx, platform, gl_api);
|
2023-02-21 19:15:49 +00:00
|
|
|
let wrapped_context = match wrapped_context {
|
2022-12-29 09:10:13 +00:00
|
|
|
None => {
|
|
|
|
gst::error!(CAT, imp: self, "Failed to create wrapped GL context");
|
2023-02-21 19:15:49 +00:00
|
|
|
return None;
|
2022-12-29 09:10:13 +00:00
|
|
|
}
|
2023-02-21 19:15:49 +00:00
|
|
|
Some(wrapped_context) => wrapped_context,
|
2022-12-29 09:10:13 +00:00
|
|
|
};
|
2021-10-19 06:45:07 +00:00
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
Some((gst_display, wrapped_context))
|
2021-10-19 06:45:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(all(target_os = "linux", feature = "x11glx"))]
|
|
|
|
fn initialize_x11glx(
|
|
|
|
&self,
|
|
|
|
display: gdk::Display,
|
2023-02-21 19:15:49 +00:00
|
|
|
) -> Option<(gst_gl::GLDisplay, gst_gl::GLContext)> {
|
2021-10-19 06:45:07 +00:00
|
|
|
gst::info!(
|
|
|
|
CAT,
|
|
|
|
imp: self,
|
2023-02-21 19:15:49 +00:00
|
|
|
"Initializing GL for x11 GLX backend and display"
|
2021-10-19 06:45:07 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
let platform = gst_gl::GLPlatform::GLX;
|
|
|
|
let (gl_api, _, _) = gst_gl::GLContext::current_gl_api(platform);
|
|
|
|
let gl_ctx = gst_gl::GLContext::current_gl_context(platform);
|
|
|
|
|
2022-12-22 20:29:22 +00:00
|
|
|
if gl_ctx == 0 {
|
2023-02-21 19:15:49 +00:00
|
|
|
gst::error!(CAT, imp: self, "Failed to get handle from GdkGLContext");
|
|
|
|
return None;
|
2022-12-22 20:29:22 +00:00
|
|
|
}
|
2021-10-19 06:45:07 +00:00
|
|
|
|
2022-12-22 20:29:22 +00:00
|
|
|
// FIXME: bindings
|
|
|
|
unsafe {
|
2022-12-29 09:10:13 +00:00
|
|
|
use glib::translate::*;
|
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
let display = display.downcast::<gdk_x11::X11Display>().unwrap();
|
|
|
|
let x11_display = gdk_x11::ffi::gdk_x11_display_get_xdisplay(display.to_glib_none().0);
|
2022-12-29 09:10:13 +00:00
|
|
|
if x11_display.is_null() {
|
|
|
|
gst::error!(CAT, imp: self, "Failed to get X11 display");
|
2023-02-21 19:15:49 +00:00
|
|
|
return None;
|
2022-12-29 09:10:13 +00:00
|
|
|
}
|
2021-10-19 06:45:07 +00:00
|
|
|
|
2022-12-22 20:29:22 +00:00
|
|
|
let gst_display = gst_gl_x11::ffi::gst_gl_display_x11_new_with_display(x11_display);
|
2022-12-29 09:10:13 +00:00
|
|
|
let gst_display =
|
|
|
|
gst_gl::GLDisplay::from_glib_full(gst_display as *mut gst_gl::ffi::GstGLDisplay);
|
2021-10-19 06:45:07 +00:00
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
let wrapped_context =
|
2022-12-22 20:29:22 +00:00
|
|
|
gst_gl::GLContext::new_wrapped(&gst_display, gl_ctx, platform, gl_api);
|
2023-02-21 19:15:49 +00:00
|
|
|
let wrapped_context = match wrapped_context {
|
2022-12-29 09:10:13 +00:00
|
|
|
None => {
|
|
|
|
gst::error!(CAT, imp: self, "Failed to create wrapped GL context");
|
2023-02-21 19:15:49 +00:00
|
|
|
return None;
|
2022-12-29 09:10:13 +00:00
|
|
|
}
|
2023-02-21 19:15:49 +00:00
|
|
|
Some(wrapped_context) => wrapped_context,
|
2022-12-29 09:10:13 +00:00
|
|
|
};
|
2021-10-19 06:45:07 +00:00
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
Some((gst_display, wrapped_context))
|
2021-10-19 06:45:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(all(target_os = "linux", feature = "wayland"))]
|
|
|
|
fn initialize_waylandegl(
|
|
|
|
&self,
|
|
|
|
display: gdk::Display,
|
2023-02-21 19:15:49 +00:00
|
|
|
) -> Option<(gst_gl::GLDisplay, gst_gl::GLContext)> {
|
2021-10-19 06:45:07 +00:00
|
|
|
gst::info!(
|
|
|
|
CAT,
|
|
|
|
imp: self,
|
2023-02-21 19:15:49 +00:00
|
|
|
"Initializing GL for Wayland EGL backend and display"
|
2021-10-19 06:45:07 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
let platform = gst_gl::GLPlatform::EGL;
|
|
|
|
let (gl_api, _, _) = gst_gl::GLContext::current_gl_api(platform);
|
|
|
|
let gl_ctx = gst_gl::GLContext::current_gl_context(platform);
|
|
|
|
|
2022-12-22 20:29:22 +00:00
|
|
|
if gl_ctx == 0 {
|
2023-02-21 19:15:49 +00:00
|
|
|
gst::error!(CAT, imp: self, "Failed to get handle from GdkGLContext");
|
|
|
|
return None;
|
2022-12-22 20:29:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: bindings
|
|
|
|
unsafe {
|
2022-12-29 09:10:13 +00:00
|
|
|
use glib::translate::*;
|
|
|
|
|
2022-12-22 20:29:22 +00:00
|
|
|
// let wayland_display = gdk_wayland::WaylandDisplay::wl_display(display.downcast());
|
|
|
|
// get the ptr directly since we are going to use it raw
|
2023-02-21 19:15:49 +00:00
|
|
|
let display = display.downcast::<gdk_wayland::WaylandDisplay>().unwrap();
|
2022-12-22 20:29:22 +00:00
|
|
|
let wayland_display =
|
2023-02-21 19:15:49 +00:00
|
|
|
gdk_wayland::ffi::gdk_wayland_display_get_wl_display(display.to_glib_none().0);
|
2022-12-29 09:10:13 +00:00
|
|
|
if wayland_display.is_null() {
|
|
|
|
gst::error!(CAT, imp: self, "Failed to get Wayland display");
|
2023-02-21 19:15:49 +00:00
|
|
|
return None;
|
2022-12-29 09:10:13 +00:00
|
|
|
}
|
2022-12-22 20:29:22 +00:00
|
|
|
|
|
|
|
let gst_display =
|
|
|
|
gst_gl_wayland::ffi::gst_gl_display_wayland_new_with_display(wayland_display);
|
2022-12-29 09:10:13 +00:00
|
|
|
let gst_display =
|
|
|
|
gst_gl::GLDisplay::from_glib_full(gst_display as *mut gst_gl::ffi::GstGLDisplay);
|
2022-12-22 20:29:22 +00:00
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
let wrapped_context =
|
2022-12-22 20:29:22 +00:00
|
|
|
gst_gl::GLContext::new_wrapped(&gst_display, gl_ctx, platform, gl_api);
|
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
let wrapped_context = match wrapped_context {
|
2022-12-29 09:10:13 +00:00
|
|
|
None => {
|
|
|
|
gst::error!(CAT, imp: self, "Failed to create wrapped GL context");
|
2023-02-21 19:15:49 +00:00
|
|
|
return None;
|
2022-12-29 09:10:13 +00:00
|
|
|
}
|
2023-02-21 19:15:49 +00:00
|
|
|
Some(wrapped_context) => wrapped_context,
|
2022-12-29 09:10:13 +00:00
|
|
|
};
|
2022-12-22 20:29:22 +00:00
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
Some((gst_display, wrapped_context))
|
2021-10-19 06:45:07 +00:00
|
|
|
}
|
|
|
|
}
|
2022-12-23 08:16:17 +00:00
|
|
|
|
2022-12-28 17:57:54 +00:00
|
|
|
#[cfg(target_os = "macos")]
|
2022-12-23 08:16:17 +00:00
|
|
|
fn initialize_macosgl(
|
|
|
|
&self,
|
|
|
|
display: gdk::Display,
|
2023-02-21 19:15:49 +00:00
|
|
|
) -> Option<(gst_gl::GLDisplay, gst_gl::GLContext)> {
|
2022-12-23 08:16:17 +00:00
|
|
|
gst::info!(
|
|
|
|
CAT,
|
|
|
|
imp: self,
|
2023-02-21 19:15:49 +00:00
|
|
|
"Initializing GL for macOS backend and display"
|
2022-12-23 08:16:17 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
let platform = gst_gl::GLPlatform::CGL;
|
|
|
|
let (gl_api, _, _) = gst_gl::GLContext::current_gl_api(platform);
|
|
|
|
let gl_ctx = gst_gl::GLContext::current_gl_context(platform);
|
|
|
|
|
|
|
|
if gl_ctx == 0 {
|
2023-02-21 19:15:49 +00:00
|
|
|
gst::error!(CAT, imp: self, "Failed to get handle from GdkGLContext");
|
|
|
|
return None;
|
2022-12-23 08:16:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let gst_display = gst_gl::GLDisplay::new();
|
|
|
|
unsafe {
|
2023-02-21 19:15:49 +00:00
|
|
|
let wrapped_context =
|
2022-12-23 08:16:17 +00:00
|
|
|
gst_gl::GLContext::new_wrapped(&gst_display, gl_ctx, platform, gl_api);
|
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
let wrapped_context = match wrapped_context {
|
2022-12-29 09:10:13 +00:00
|
|
|
None => {
|
|
|
|
gst::error!(CAT, imp: self, "Failed to create wrapped GL context");
|
2023-03-18 14:04:36 +00:00
|
|
|
return None;
|
2022-12-29 09:10:13 +00:00
|
|
|
}
|
2023-02-21 19:15:49 +00:00
|
|
|
Some(wrapped_context) => wrapped_context,
|
2022-12-29 09:10:13 +00:00
|
|
|
};
|
2022-12-23 08:16:17 +00:00
|
|
|
|
2023-02-21 19:15:49 +00:00
|
|
|
Some((gst_display, wrapped_context))
|
2022-12-23 08:16:17 +00:00
|
|
|
}
|
|
|
|
}
|
2021-10-19 06:45:07 +00:00
|
|
|
}
|