video/gtk4: Initial colorimetry support

Similar to GtkGstSink.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/2022>
This commit is contained in:
Robert Mader 2024-12-23 16:36:49 +01:00
parent 85455c937f
commit 6450381017
3 changed files with 109 additions and 20 deletions

View file

@ -388,7 +388,7 @@ foreach glib_version : glib_versions
endforeach endforeach
if get_option('gtk4').allowed() and gtk_dep.found() if get_option('gtk4').allowed() and gtk_dep.found()
gtk4_versions = ['4.16','4.14','4.12','4.10'] gtk4_versions = ['4.20','4.18','4.16','4.14','4.12','4.10']
foreach gtk4_version : gtk4_versions foreach gtk4_version : gtk4_versions
if gtk_dep.version().version_compare(f'>=@gtk4_version@') if gtk_dep.version().version_compare(f'>=@gtk4_version@')
found_features += {'gtk': 'gtk_v' + gtk4_version.underscorify()} found_features += {'gtk': 'gtk_v' + gtk4_version.underscorify()}

View file

@ -56,6 +56,8 @@ gtk_v4_10 = ["gtk/v4_10"]
gtk_v4_12 = ["gtk/v4_12", "gtk_v4_10"] gtk_v4_12 = ["gtk/v4_12", "gtk_v4_10"]
gtk_v4_14 = ["gtk/v4_14", "gtk_v4_12"] gtk_v4_14 = ["gtk/v4_14", "gtk_v4_12"]
gtk_v4_16 = ["gtk/v4_16", "gtk_v4_14"] gtk_v4_16 = ["gtk/v4_16", "gtk_v4_14"]
gtk_v4_18 = ["gtk/v4_18", "gtk_v4_16"]
gtk_v4_20 = ["gtk/v4_20", "gtk_v4_18"]
[package.metadata.capi] [package.metadata.capi]
min_version = "0.9.21" min_version = "0.9.21"

View file

@ -241,6 +241,40 @@ fn video_format_to_memory_format(f: gst_video::VideoFormat) -> gdk::MemoryFormat
} }
} }
#[cfg(feature = "gtk_v4_20")]
fn colorimetry_to_color_state(colorimetry: gst_video::VideoColorimetry) -> Option<gdk::ColorState> {
// Ignore incomplete colorimetries and fall back to format dependent default color states in
// GTK. This should make it easier to detect buggy sources and avoid confusing and unexpected
// output.
if colorimetry.primaries() == gst_video::VideoColorPrimaries::Unknown
|| colorimetry.transfer() == gst_video::VideoTransferFunction::Unknown
|| colorimetry.matrix() == gst_video::VideoColorMatrix::Unknown
|| colorimetry.range() == gst_video::VideoColorRange::Unknown
{
return None;
}
let color_params = gdk::CicpParams::new();
color_params.set_color_primaries(colorimetry.primaries().to_iso());
color_params.set_transfer_function(colorimetry.transfer().to_iso());
color_params.set_matrix_coefficients(colorimetry.matrix().to_iso());
match colorimetry.range() {
gst_video::VideoColorRange::Range0_255 => color_params.set_range(gdk::CicpRange::Full),
gst_video::VideoColorRange::Range16_235 => color_params.set_range(gdk::CicpRange::Narrow),
_ => panic!("Unhandled range"),
}
match color_params.build_color_state() {
Ok(color_state) => Some(color_state),
Err(error) => {
println!("Could not build color state: {}", error);
None
}
}
}
fn video_frame_to_memory_texture( fn video_frame_to_memory_texture(
frame: gst_video::VideoFrame<gst_video::video_frame::Readable>, frame: gst_video::VideoFrame<gst_video::video_frame::Readable>,
cached_textures: &mut HashMap<TextureCacheId, gdk::Texture>, cached_textures: &mut HashMap<TextureCacheId, gdk::Texture>,
@ -261,14 +295,36 @@ fn video_frame_to_memory_texture(
let height = frame.height(); let height = frame.height();
let rowstride = frame.plane_stride()[0] as usize; let rowstride = frame.plane_stride()[0] as usize;
let texture = gdk::MemoryTexture::new( let texture = {
width as i32, #[cfg(feature = "gtk_v4_20")]
height as i32, {
format, let info = frame.info().clone();
&glib::Bytes::from_owned(FrameWrapper(frame)),
rowstride, let mut builder = gdk::MemoryTextureBuilder::new()
) .set_width(width as i32)
.upcast::<gdk::Texture>(); .set_height(height as i32)
.set_format(format)
.set_bytes(Some(&glib::Bytes::from_owned(FrameWrapper(frame))))
.set_stride(rowstride);
if let Some(color_state) = colorimetry_to_color_state(info.colorimetry()) {
builder = builder.set_color_state(&color_state);
}
builder.build()
}
#[cfg(not(feature = "gtk_v4_20"))]
{
gdk::MemoryTexture::new(
width as i32,
height as i32,
format,
&glib::Bytes::from_owned(FrameWrapper(frame)),
rowstride,
)
}
.upcast::<gdk::Texture>()
};
cached_textures.insert(TextureCacheId::Memory(ptr), texture.clone()); cached_textures.insert(TextureCacheId::Memory(ptr), texture.clone());
used_textures.insert(TextureCacheId::Memory(ptr)); used_textures.insert(TextureCacheId::Memory(ptr));
@ -345,17 +401,40 @@ fn video_frame_to_gl_texture(
}; };
let sync_point = (*sync_meta.as_ptr()).data; let sync_point = (*sync_meta.as_ptr()).data;
gdk::GLTextureBuilder::new() let builder = {
.set_context(Some(gdk_context)) #[cfg(feature = "gtk_v4_20")]
.set_id(texture_id as u32) {
.set_width(width as i32) let mut mut_builder = gdk::GLTextureBuilder::new()
.set_height(height as i32) .set_context(Some(gdk_context))
.set_format(format) .set_id(texture_id as u32)
.set_sync(Some(sync_point)) .set_width(width as i32)
.build_with_release_func(move || { .set_height(height as i32)
// Unmap and drop the GStreamer GL texture once GTK is done with it and not earlier .set_format(format)
drop(frame); .set_sync(Some(sync_point));
})
if let Some(color_state) =
colorimetry_to_color_state(frame.info().colorimetry())
{
mut_builder = mut_builder.set_color_state(&color_state);
}
mut_builder
}
#[cfg(not(feature = "gtk_v4_20"))]
{
gdk::GLTextureBuilder::new()
.set_context(Some(gdk_context))
.set_id(texture_id as u32)
.set_width(width as i32)
.set_height(height as i32)
.set_format(format)
.set_sync(Some(sync_point))
}
};
builder.build_with_release_func(move || {
// Unmap and drop the GStreamer GL texture once GTK is done with it and not earlier
drop(frame);
})
} }
#[cfg(not(feature = "gtk_v4_12"))] #[cfg(not(feature = "gtk_v4_12"))]
{ {
@ -409,6 +488,14 @@ fn video_frame_to_dmabuf_texture(
.set_width(width) .set_width(width)
.set_height(height) .set_height(height)
.set_n_planes(n_planes); .set_n_planes(n_planes);
#[cfg(feature = "gtk_v4_20")]
{
if let Some(color_state) = colorimetry_to_color_state(info.colorimetry()) {
builder = builder.set_color_state(Some(color_state).as_ref());
}
}
for plane in 0..(n_planes as usize) { for plane in 0..(n_planes as usize) {
unsafe { unsafe {
builder = builder.set_fd(plane as u32, fds[plane]); builder = builder.set_fd(plane as u32, fds[plane]);