From 20380e699ebf7de6cd1e1a9569073d6e9011d3b0 Mon Sep 17 00:00:00 2001 From: Taruntej Kanakamalla Date: Fri, 5 Apr 2024 17:12:55 +0530 Subject: [PATCH 01/16] webrtcsrc: change the producer-id type for request-encoded-filter With https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1477 the producer id used while emitting the request-encoded-filter can be a None if the msid of the webrtcbin's pad is None. This might not affect the signal handler written in C but can panic in an existing Rust application with signal handler which can only handle valid String type as its param for the producer id. So change the param type to Option in the signal builder for request-encoded-fiter signal Part-of: --- net/webrtc/src/webrtcsrc/imp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/webrtc/src/webrtcsrc/imp.rs b/net/webrtc/src/webrtcsrc/imp.rs index 6923a99a..b779facf 100644 --- a/net/webrtc/src/webrtcsrc/imp.rs +++ b/net/webrtc/src/webrtcsrc/imp.rs @@ -240,7 +240,7 @@ impl ObjectImpl for BaseWebRTCSrc { */ glib::subclass::Signal::builder("request-encoded-filter") .param_types([ - String::static_type(), + Option::::static_type(), String::static_type(), Option::::static_type(), ]) From 18a51c360d18602af9d5bab9aa6dc736fd5ee018 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Tue, 28 Nov 2023 19:53:10 +0200 Subject: [PATCH 02/16] mp4/fmp4: support flac inside the iso (f)mp4 container Part-of: --- docs/plugins/gst_plugins_cache.json | 4 +- mux/fmp4/src/fmp4mux/boxes.rs | 56 ++++++-- mux/fmp4/src/fmp4mux/imp.rs | 40 ++++-- mux/fmp4/tests/tests.rs | 45 ++++++ mux/mp4/src/mp4mux/boxes.rs | 57 ++++++-- mux/mp4/src/mp4mux/imp.rs | 111 +++++++++------ mux/mp4/tests/tests.rs | 208 ++++++++++++++++------------ 7 files changed, 356 insertions(+), 165 deletions(-) diff --git a/docs/plugins/gst_plugins_cache.json b/docs/plugins/gst_plugins_cache.json index b247e99e..9731c0f1 100644 --- a/docs/plugins/gst_plugins_cache.json +++ b/docs/plugins/gst_plugins_cache.json @@ -2038,7 +2038,7 @@ "long-name": "ISOFMP4Mux", "pad-templates": { "sink_%%u": { - "caps": "video/x-h264:\n stream-format: { (string)avc, (string)avc3 }\n alignment: au\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-h265:\n stream-format: { (string)hvc1, (string)hev1 }\n alignment: au\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-vp8:\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-vp9:\n profile: { (string)0, (string)1, (string)2, (string)3 }\n chroma-format: { (string)4:2:0, (string)4:2:2, (string)4:4:4 }\n bit-depth-luma: { (uint)8, (uint)10, (uint)12 }\nbit-depth-chroma: { (uint)8, (uint)10, (uint)12 }\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-av1:\n stream-format: obu-stream\n alignment: tu\n profile: { (string)main, (string)high, (string)professional }\n chroma-format: { (string)4:0:0, (string)4:2:0, (string)4:2:2, (string)4:4:4 }\n bit-depth-luma: { (uint)8, (uint)10, (uint)12 }\nbit-depth-chroma: { (uint)8, (uint)10, (uint)12 }\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\naudio/mpeg:\n mpegversion: 4\n stream-format: raw\n channels: [ 1, 65535 ]\n rate: [ 1, 2147483647 ]\naudio/x-opus:\nchannel-mapping-family: [ 0, 255 ]\n channels: [ 1, 8 ]\n rate: [ 1, 2147483647 ]\n", + "caps": "video/x-h264:\n stream-format: { (string)avc, (string)avc3 }\n alignment: au\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-h265:\n stream-format: { (string)hvc1, (string)hev1 }\n alignment: au\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-vp8:\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-vp9:\n profile: { (string)0, (string)1, (string)2, (string)3 }\n chroma-format: { (string)4:2:0, (string)4:2:2, (string)4:4:4 }\n bit-depth-luma: { (uint)8, (uint)10, (uint)12 }\nbit-depth-chroma: { (uint)8, (uint)10, (uint)12 }\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-av1:\n stream-format: obu-stream\n alignment: tu\n profile: { (string)main, (string)high, (string)professional }\n chroma-format: { (string)4:0:0, (string)4:2:0, (string)4:2:2, (string)4:4:4 }\n bit-depth-luma: { (uint)8, (uint)10, (uint)12 }\nbit-depth-chroma: { (uint)8, (uint)10, (uint)12 }\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\naudio/mpeg:\n mpegversion: 4\n stream-format: raw\n channels: [ 1, 65535 ]\n rate: [ 1, 2147483647 ]\naudio/x-opus:\nchannel-mapping-family: [ 0, 255 ]\n channels: [ 1, 8 ]\n rate: [ 1, 2147483647 ]\naudio/x-flac:\n framed: true\n channels: [ 1, 8 ]\n rate: [ 1, 655350 ]\n", "direction": "sink", "presence": "request", "type": "GstFMP4MuxPad" @@ -3251,7 +3251,7 @@ "klass": "Codec/Muxer", "pad-templates": { "sink_%%u": { - "caps": "video/x-h264:\n stream-format: { (string)avc, (string)avc3 }\n alignment: au\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-h265:\n stream-format: { (string)hvc1, (string)hev1 }\n alignment: au\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-vp8:\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-vp9:\n profile: { (string)0, (string)1, (string)2, (string)3 }\n chroma-format: { (string)4:2:0, (string)4:2:2, (string)4:4:4 }\n bit-depth-luma: { (uint)8, (uint)10, (uint)12 }\nbit-depth-chroma: { (uint)8, (uint)10, (uint)12 }\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-av1:\n stream-format: obu-stream\n alignment: tu\n profile: { (string)main, (string)high, (string)professional }\n chroma-format: { (string)4:0:0, (string)4:2:0, (string)4:2:2, (string)4:4:4 }\n bit-depth-luma: { (uint)8, (uint)10, (uint)12 }\nbit-depth-chroma: { (uint)8, (uint)10, (uint)12 }\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\naudio/mpeg:\n mpegversion: 4\n stream-format: raw\n channels: [ 1, 65535 ]\n rate: [ 1, 2147483647 ]\naudio/x-opus:\nchannel-mapping-family: [ 0, 255 ]\n channels: [ 1, 8 ]\n rate: [ 1, 2147483647 ]\n", + "caps": "video/x-h264:\n stream-format: { (string)avc, (string)avc3 }\n alignment: au\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-h265:\n stream-format: { (string)hvc1, (string)hev1 }\n alignment: au\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-vp8:\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-vp9:\n profile: { (string)0, (string)1, (string)2, (string)3 }\n chroma-format: { (string)4:2:0, (string)4:2:2, (string)4:4:4 }\n bit-depth-luma: { (uint)8, (uint)10, (uint)12 }\nbit-depth-chroma: { (uint)8, (uint)10, (uint)12 }\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\nvideo/x-av1:\n stream-format: obu-stream\n alignment: tu\n profile: { (string)main, (string)high, (string)professional }\n chroma-format: { (string)4:0:0, (string)4:2:0, (string)4:2:2, (string)4:4:4 }\n bit-depth-luma: { (uint)8, (uint)10, (uint)12 }\nbit-depth-chroma: { (uint)8, (uint)10, (uint)12 }\n width: [ 1, 65535 ]\n height: [ 1, 65535 ]\naudio/mpeg:\n mpegversion: 4\n stream-format: raw\n channels: [ 1, 65535 ]\n rate: [ 1, 2147483647 ]\naudio/x-opus:\nchannel-mapping-family: [ 0, 255 ]\n channels: [ 1, 8 ]\n rate: [ 1, 2147483647 ]\naudio/x-flac:\n framed: true\n channels: [ 1, 8 ]\n rate: [ 1, 655350 ]\n", "direction": "sink", "presence": "request", "type": "GstRsMP4MuxPad" diff --git a/mux/fmp4/src/fmp4mux/boxes.rs b/mux/fmp4/src/fmp4mux/boxes.rs index 55c8c520..e2fdeae7 100644 --- a/mux/fmp4/src/fmp4mux/boxes.rs +++ b/mux/fmp4/src/fmp4mux/boxes.rs @@ -9,6 +9,7 @@ use gst::prelude::*; use anyhow::{anyhow, bail, Context, Error}; +use std::convert::TryFrom; use super::Buffer; @@ -604,9 +605,8 @@ fn write_tkhd( // Volume let s = stream.caps.structure(0).unwrap(); match s.name().as_str() { - "audio/mpeg" | "audio/x-opus" | "audio/x-alaw" | "audio/x-mulaw" | "audio/x-adpcm" => { - v.extend((1u16 << 8).to_be_bytes()) - } + "audio/mpeg" | "audio/x-opus" | "audio/x-flac" | "audio/x-alaw" | "audio/x-mulaw" + | "audio/x-adpcm" => v.extend((1u16 << 8).to_be_bytes()), _ => v.extend(0u16.to_be_bytes()), } @@ -745,9 +745,8 @@ fn write_hdlr( let (handler_type, name) = match s.name().as_str() { "video/x-h264" | "video/x-h265" | "video/x-vp8" | "video/x-vp9" | "video/x-av1" | "image/jpeg" => (b"vide", b"VideoHandler\0".as_slice()), - "audio/mpeg" | "audio/x-opus" | "audio/x-alaw" | "audio/x-mulaw" | "audio/x-adpcm" => { - (b"soun", b"SoundHandler\0".as_slice()) - } + "audio/mpeg" | "audio/x-opus" | "audio/x-flac" | "audio/x-alaw" | "audio/x-mulaw" + | "audio/x-adpcm" => (b"soun", b"SoundHandler\0".as_slice()), "application/x-onvif-metadata" => (b"meta", b"MetadataHandler\0".as_slice()), _ => unreachable!(), }; @@ -777,7 +776,8 @@ fn write_minf( // Flags are always 1 for unspecified reasons write_full_box(v, b"vmhd", FULL_BOX_VERSION_0, 1, |v| write_vmhd(v, cfg))? } - "audio/mpeg" | "audio/x-opus" | "audio/x-alaw" | "audio/x-mulaw" | "audio/x-adpcm" => { + "audio/mpeg" | "audio/x-opus" | "audio/x-flac" | "audio/x-alaw" | "audio/x-mulaw" + | "audio/x-adpcm" => { write_full_box(v, b"smhd", FULL_BOX_VERSION_0, FULL_BOX_FLAGS_NONE, |v| { write_smhd(v, cfg) })? @@ -886,9 +886,8 @@ fn write_stsd( match s.name().as_str() { "video/x-h264" | "video/x-h265" | "video/x-vp8" | "video/x-vp9" | "video/x-av1" | "image/jpeg" => write_visual_sample_entry(v, cfg, stream)?, - "audio/mpeg" | "audio/x-opus" | "audio/x-alaw" | "audio/x-mulaw" | "audio/x-adpcm" => { - write_audio_sample_entry(v, cfg, stream)? - } + "audio/mpeg" | "audio/x-opus" | "audio/x-flac" | "audio/x-alaw" | "audio/x-mulaw" + | "audio/x-adpcm" => write_audio_sample_entry(v, cfg, stream)?, "application/x-onvif-metadata" => write_xml_meta_data_sample_entry(v, cfg, stream)?, _ => unreachable!(), } @@ -1262,6 +1261,7 @@ fn write_audio_sample_entry( let fourcc = match s.name().as_str() { "audio/mpeg" => b"mp4a", "audio/x-opus" => b"Opus", + "audio/x-flac" => b"fLaC", "audio/x-alaw" => b"alaw", "audio/x-mulaw" => b"ulaw", "audio/x-adpcm" => { @@ -1280,6 +1280,10 @@ fn write_audio_sample_entry( let bitrate = s.get::("bitrate").context("no ADPCM bitrate field")?; (bitrate / 8000) as u16 } + "audio/x-flac" => with_flac_metadata(&stream.caps, |streaminfo, _| { + 1 + (u16::from_be_bytes([streaminfo[16], streaminfo[17]]) >> 4 & 0b11111) + }) + .context("FLAC metadata error")?, _ => 16u16, }; @@ -1322,6 +1326,9 @@ fn write_audio_sample_entry( "audio/x-opus" => { write_dops(v, &stream.caps)?; } + "audio/x-flac" => { + write_dfla(v, &stream.caps)?; + } "audio/x-alaw" | "audio/x-mulaw" | "audio/x-adpcm" => { // Nothing to do here } @@ -1516,6 +1523,35 @@ fn write_dops(v: &mut Vec, caps: &gst::Caps) -> Result<(), Error> { }) } +fn with_flac_metadata( + caps: &gst::Caps, + cb: impl FnOnce(&[u8], &[gst::glib::SendValue]) -> R, +) -> Result { + let caps = caps.structure(0).unwrap(); + let header = caps.get::("streamheader").unwrap(); + let (streaminfo, remainder) = header.as_ref().split_first().unwrap(); + let streaminfo = streaminfo.get::<&gst::BufferRef>().unwrap(); + let streaminfo = streaminfo.map_readable().unwrap(); + // 13 bytes for the Ogg/FLAC prefix and 38 for the streaminfo itself. + match <&[_; 13 + 38]>::try_from(streaminfo.as_slice()) { + Ok(i) if i.starts_with(b"\x7FFLAC\x01\x00") => Ok(cb(&i[13..], remainder)), + Ok(_) | Err(_) => bail!("Unknown streamheader format"), + } +} + +fn write_dfla(v: &mut Vec, caps: &gst::Caps) -> Result<(), Error> { + write_full_box(v, b"dfLa", 0, 0, move |v| { + with_flac_metadata(caps, |streaminfo, remainder| { + v.extend(streaminfo); + for metadata in remainder { + let metadata = metadata.get::<&gst::BufferRef>().unwrap(); + let metadata = metadata.map_readable().unwrap(); + v.extend(&metadata[..]); + } + }) + }) +} + fn write_xml_meta_data_sample_entry( v: &mut Vec, _cfg: &super::HeaderConfiguration, diff --git a/mux/fmp4/src/fmp4mux/imp.rs b/mux/fmp4/src/fmp4mux/imp.rs index 592b42dd..501a482e 100644 --- a/mux/fmp4/src/fmp4mux/imp.rs +++ b/mux/fmp4/src/fmp4mux/imp.rs @@ -205,6 +205,8 @@ struct Stream { caps: gst::Caps, /// Whether this stream is intra-only and has frame reordering. delta_frames: DeltaFrames, + /// Whether this stream might have header frames without timestamps that should be ignored. + discard_header_buffers: bool, /// Currently queued GOPs, including incomplete ones. queued_gops: VecDeque, @@ -271,11 +273,17 @@ pub(crate) struct FMP4Mux { impl FMP4Mux { /// Checks if a buffer is valid according to the stream configuration. - fn check_buffer( - buffer: &gst::BufferRef, - sinkpad: &super::FMP4MuxPad, - delta_frames: super::DeltaFrames, - ) -> Result<(), gst::FlowError> { + fn check_buffer(buffer: &gst::BufferRef, stream: &Stream) -> Result<(), gst::FlowError> { + let Stream { + sinkpad, + delta_frames, + discard_header_buffers, + .. + } = stream; + if *discard_header_buffers && buffer.flags().contains(gst::BufferFlags::HEADER) { + return Err(gst_base::AGGREGATOR_FLOW_NEED_DATA); + } + if delta_frames.requires_dts() && buffer.dts().is_none() { gst::error!(CAT, obj: sinkpad, "Require DTS for video streams"); return Err(gst::FlowError::Error); @@ -314,12 +322,10 @@ impl FMP4Mux { } // Pop buffer here, it will be stored in the pre-queue after calculating its timestamps - let mut buffer = match stream.sinkpad.pop_buffer() { - None => return Ok(None), - Some(buffer) => buffer, + let Some(mut buffer) = stream.sinkpad.pop_buffer() else { + return Ok(None); }; - - Self::check_buffer(&buffer, &stream.sinkpad, stream.delta_frames)?; + Self::check_buffer(&buffer, stream)?; let segment = match stream.sinkpad.segment().downcast::().ok() { Some(segment) => segment, @@ -2555,6 +2561,7 @@ impl FMP4Mux { let s = caps.structure(0).unwrap(); let mut delta_frames = DeltaFrames::IntraOnly; + let mut discard_header_buffers = false; match s.name().as_str() { "video/x-h264" | "video/x-h265" => { if !s.has_field_with_type("codec_data", gst::Buffer::static_type()) { @@ -2598,6 +2605,13 @@ impl FMP4Mux { return Err(gst::FlowError::NotNegotiated); } } + "audio/x-flac" => { + discard_header_buffers = true; + if let Err(e) = s.get::("streamheader") { + gst::error!(CAT, obj: pad, "Muxing FLAC into MP4 needs streamheader: {}", e); + return Err(gst::FlowError::NotNegotiated); + }; + } "audio/x-alaw" | "audio/x-mulaw" => (), "audio/x-adpcm" => (), "application/x-onvif-metadata" => (), @@ -2608,6 +2622,7 @@ impl FMP4Mux { sinkpad: pad, caps, delta_frames, + discard_header_buffers, pre_queue: VecDeque::new(), queued_gops: VecDeque::new(), fragment_filled: false, @@ -3465,6 +3480,11 @@ impl ElementImpl for ISOFMP4Mux { .field("channels", gst::IntRange::new(1i32, 8)) .field("rate", gst::IntRange::new(1, i32::MAX)) .build(), + gst::Structure::builder("audio/x-flac") + .field("framed", true) + .field("channels", gst::IntRange::::new(1, 8)) + .field("rate", gst::IntRange::::new(1, 10 * u16::MAX as i32)) + .build(), ] .into_iter() .collect::(), diff --git a/mux/fmp4/tests/tests.rs b/mux/fmp4/tests/tests.rs index a32e65fc..5beba426 100644 --- a/mux/fmp4/tests/tests.rs +++ b/mux/fmp4/tests/tests.rs @@ -19,6 +19,33 @@ fn init() { }); } +fn to_completion(pipeline: &gst::Pipeline) { + pipeline + .set_state(gst::State::Playing) + .expect("Unable to set the pipeline to the `Playing` state"); + + for msg in pipeline.bus().unwrap().iter_timed(gst::ClockTime::NONE) { + use gst::MessageView; + + match msg.view() { + MessageView::Eos(..) => break, + MessageView::Error(err) => { + panic!( + "Error from {:?}: {} ({:?})", + err.src().map(|s| s.path_string()), + err.error(), + err.debug() + ); + } + _ => (), + } + } + + pipeline + .set_state(gst::State::Null) + .expect("Unable to set the pipeline to the `Null` state"); +} + fn test_buffer_flags_single_stream(cmaf: bool, set_dts: bool, caps: gst::Caps) { let mut h = if cmaf { gst_check::Harness::new("cmafmux") @@ -1993,3 +2020,21 @@ fn test_chunking_single_stream_gops_after_fragment_end_after_next_chunk_end() { let ev = h.pull_event().unwrap(); assert_eq!(ev.type_(), gst::EventType::Eos); } + +#[test] +fn test_roundtrip_vp9_flac() { + init(); + + let pipeline = gst::parse::launch( + r#" + videotestsrc num-buffers=99 ! vp9enc ! vp9parse ! mux. + audiotestsrc num-buffers=149 ! flacenc ! flacparse ! mux. + isofmp4mux name=mux ! qtdemux name=demux + demux.audio_0 ! queue ! flacdec ! fakesink + demux.video_0 ! queue ! vp9dec ! fakesink + "#, + ) + .unwrap(); + let pipeline = pipeline.downcast().unwrap(); + to_completion(&pipeline); +} diff --git a/mux/mp4/src/mp4mux/boxes.rs b/mux/mp4/src/mp4mux/boxes.rs index 4e2188b0..170fbc9c 100644 --- a/mux/mp4/src/mp4mux/boxes.rs +++ b/mux/mp4/src/mp4mux/boxes.rs @@ -9,7 +9,7 @@ use gst::prelude::*; use anyhow::{anyhow, bail, Context, Error}; - +use std::convert::TryFrom; use std::str::FromStr; fn write_box) -> Result>( @@ -382,9 +382,8 @@ fn write_tkhd( // Volume let s = stream.caps.structure(0).unwrap(); match s.name().as_str() { - "audio/mpeg" | "audio/x-opus" | "audio/x-alaw" | "audio/x-mulaw" | "audio/x-adpcm" => { - v.extend((1u16 << 8).to_be_bytes()) - } + "audio/mpeg" | "audio/x-opus" | "audio/x-flac" | "audio/x-alaw" | "audio/x-mulaw" + | "audio/x-adpcm" => v.extend((1u16 << 8).to_be_bytes()), _ => v.extend(0u16.to_be_bytes()), } @@ -514,9 +513,8 @@ fn write_hdlr( let (handler_type, name) = match s.name().as_str() { "video/x-h264" | "video/x-h265" | "video/x-vp8" | "video/x-vp9" | "video/x-av1" | "image/jpeg" => (b"vide", b"VideoHandler\0".as_slice()), - "audio/mpeg" | "audio/x-opus" | "audio/x-alaw" | "audio/x-mulaw" | "audio/x-adpcm" => { - (b"soun", b"SoundHandler\0".as_slice()) - } + "audio/mpeg" | "audio/x-opus" | "audio/x-flac" | "audio/x-alaw" | "audio/x-mulaw" + | "audio/x-adpcm" => (b"soun", b"SoundHandler\0".as_slice()), "application/x-onvif-metadata" => (b"meta", b"MetadataHandler\0".as_slice()), _ => unreachable!(), }; @@ -546,7 +544,8 @@ fn write_minf( // Flags are always 1 for unspecified reasons write_full_box(v, b"vmhd", FULL_BOX_VERSION_0, 1, |v| write_vmhd(v, header))? } - "audio/mpeg" | "audio/x-opus" | "audio/x-alaw" | "audio/x-mulaw" | "audio/x-adpcm" => { + "audio/mpeg" | "audio/x-opus" | "audio/x-flac" | "audio/x-alaw" | "audio/x-mulaw" + | "audio/x-adpcm" => { write_full_box(v, b"smhd", FULL_BOX_VERSION_0, FULL_BOX_FLAGS_NONE, |v| { write_smhd(v, header) })? @@ -703,9 +702,8 @@ fn write_stsd( match s.name().as_str() { "video/x-h264" | "video/x-h265" | "video/x-vp8" | "video/x-vp9" | "video/x-av1" | "image/jpeg" => write_visual_sample_entry(v, header, stream)?, - "audio/mpeg" | "audio/x-opus" | "audio/x-alaw" | "audio/x-mulaw" | "audio/x-adpcm" => { - write_audio_sample_entry(v, header, stream)? - } + "audio/mpeg" | "audio/x-opus" | "audio/x-flac" | "audio/x-alaw" | "audio/x-mulaw" + | "audio/x-adpcm" => write_audio_sample_entry(v, header, stream)?, "application/x-onvif-metadata" => write_xml_meta_data_sample_entry(v, header, stream)?, _ => unreachable!(), } @@ -1079,6 +1077,7 @@ fn write_audio_sample_entry( let fourcc = match s.name().as_str() { "audio/mpeg" => b"mp4a", "audio/x-opus" => b"Opus", + "audio/x-flac" => b"fLaC", "audio/x-alaw" => b"alaw", "audio/x-mulaw" => b"ulaw", "audio/x-adpcm" => { @@ -1097,6 +1096,10 @@ fn write_audio_sample_entry( let bitrate = s.get::("bitrate").context("no ADPCM bitrate field")?; (bitrate / 8000) as u16 } + "audio/x-flac" => with_flac_metadata(&stream.caps, |streaminfo, _| { + 1 + (u16::from_be_bytes([streaminfo[16], streaminfo[17]]) >> 4 & 0b11111) + }) + .context("FLAC metadata error")?, _ => 16u16, }; @@ -1139,6 +1142,9 @@ fn write_audio_sample_entry( "audio/x-opus" => { write_dops(v, &stream.caps)?; } + "audio/x-flac" => { + write_dfla(v, &stream.caps)?; + } "audio/x-alaw" | "audio/x-mulaw" | "audio/x-adpcm" => { // Nothing to do here } @@ -1333,6 +1339,35 @@ fn write_dops(v: &mut Vec, caps: &gst::Caps) -> Result<(), Error> { }) } +fn with_flac_metadata( + caps: &gst::Caps, + cb: impl FnOnce(&[u8], &[gst::glib::SendValue]) -> R, +) -> Result { + let caps = caps.structure(0).unwrap(); + let header = caps.get::("streamheader").unwrap(); + let (streaminfo, remainder) = header.as_ref().split_first().unwrap(); + let streaminfo = streaminfo.get::<&gst::BufferRef>().unwrap(); + let streaminfo = streaminfo.map_readable().unwrap(); + // 13 bytes for the Ogg/FLAC prefix and 38 for the streaminfo itself. + match <&[_; 13 + 38]>::try_from(streaminfo.as_slice()) { + Ok(i) if i.starts_with(b"\x7FFLAC\x01\x00") => Ok(cb(&i[13..], remainder)), + Ok(_) | Err(_) => bail!("Unknown streamheader format"), + } +} + +fn write_dfla(v: &mut Vec, caps: &gst::Caps) -> Result<(), Error> { + write_full_box(v, b"dfLa", 0, 0, move |v| { + with_flac_metadata(caps, |streaminfo, remainder| { + v.extend(streaminfo); + for metadata in remainder { + let metadata = metadata.get::<&gst::BufferRef>().unwrap(); + let metadata = metadata.map_readable().unwrap(); + v.extend(&metadata[..]); + } + }) + }) +} + fn write_xml_meta_data_sample_entry( v: &mut Vec, _header: &super::Header, diff --git a/mux/mp4/src/mp4mux/imp.rs b/mux/mp4/src/mp4mux/imp.rs index 24c06173..bb9a791d 100644 --- a/mux/mp4/src/mp4mux/imp.rs +++ b/mux/mp4/src/mp4mux/imp.rs @@ -108,6 +108,8 @@ struct Stream { caps: gst::Caps, /// Whether this stream is intra-only and has frame reordering. delta_frames: super::DeltaFrames, + /// Whether this stream might have header frames without timestamps that should be ignored. + discard_header_buffers: bool, /// Already written out chunks with their samples for this stream chunks: Vec, @@ -165,7 +167,12 @@ impl MP4Mux { buffer: &gst::BufferRef, sinkpad: &super::MP4MuxPad, delta_frames: super::DeltaFrames, + discard_headers: bool, ) -> Result<(), gst::FlowError> { + if discard_headers && buffer.flags().contains(gst::BufferFlags::HEADER) { + return Err(gst_base::AGGREGATOR_FLOW_NEED_DATA); + } + if delta_frames.requires_dts() && buffer.dts().is_none() { gst::error!(CAT, obj: sinkpad, "Require DTS for video streams"); return Err(gst::FlowError::Error); @@ -188,6 +195,7 @@ impl MP4Mux { &self, sinkpad: &super::MP4MuxPad, delta_frames: super::DeltaFrames, + discard_headers: bool, pre_queue: &mut VecDeque<(gst::FormattedSegment, gst::Buffer)>, running_time_utc_time_mapping: &Option<(gst::Signed, gst::ClockTime)>, ) -> Result, gst::Buffer)>, gst::FlowError> { @@ -195,13 +203,10 @@ impl MP4Mux { return Ok(Some((segment.clone(), buffer.clone()))); } - let mut buffer = match sinkpad.peek_buffer() { - None => return Ok(None), - Some(buffer) => buffer, + let Some(mut buffer) = sinkpad.peek_buffer() else { + return Ok(None); }; - - Self::check_buffer(&buffer, sinkpad, delta_frames)?; - + Self::check_buffer(&buffer, sinkpad, delta_frames, discard_headers)?; let mut segment = match sinkpad.segment().downcast::().ok() { Some(segment) => segment, None => { @@ -276,19 +281,20 @@ impl MP4Mux { fn pop_buffer( &self, - sinkpad: &super::MP4MuxPad, - delta_frames: super::DeltaFrames, - pre_queue: &mut VecDeque<(gst::FormattedSegment, gst::Buffer)>, - running_time_utc_time_mapping: &mut Option<(gst::Signed, gst::ClockTime)>, + stream: &mut Stream, ) -> Result, gst::Buffer)>, gst::FlowError> { + let Stream { + sinkpad, pre_queue, .. + } = stream; + // In ONVIF mode we need to get UTC times for each buffer and synchronize based on that. // Queue up to 6s of data to get the first UTC time and then backdate. if self.obj().class().as_ref().variant == super::Variant::ONVIF - && running_time_utc_time_mapping.is_none() + && stream.running_time_utc_time_mapping.is_none() { if let Some((last, first)) = Option::zip(pre_queue.back(), pre_queue.front()) { // Existence of PTS/DTS checked below - let (last, first) = if delta_frames.requires_dts() { + let (last, first) = if stream.delta_frames.requires_dts() { ( last.0.to_running_time_full(last.1.dts()).unwrap(), first.0.to_running_time_full(first.1.dts()).unwrap(), @@ -312,19 +318,20 @@ impl MP4Mux { } } - let buffer = match sinkpad.pop_buffer() { - None => { - if sinkpad.is_eos() { - gst::error!(CAT, obj: sinkpad, "Got no UTC time before EOS"); - return Err(gst::FlowError::Error); - } else { - return Err(gst_base::AGGREGATOR_FLOW_NEED_DATA); - } + let Some(buffer) = sinkpad.pop_buffer() else { + if sinkpad.is_eos() { + gst::error!(CAT, obj: sinkpad, "Got no UTC time before EOS"); + return Err(gst::FlowError::Error); + } else { + return Err(gst_base::AGGREGATOR_FLOW_NEED_DATA); } - Some(buffer) => buffer, }; - - Self::check_buffer(&buffer, sinkpad, delta_frames)?; + Self::check_buffer( + &buffer, + sinkpad, + stream.delta_frames, + stream.discard_header_buffers, + )?; let segment = match sinkpad.segment().downcast::().ok() { Some(segment) => segment, @@ -350,7 +357,7 @@ impl MP4Mux { ); let mapping = (running_time, utc_time); - *running_time_utc_time_mapping = Some(mapping); + stream.running_time_utc_time_mapping = Some(mapping); // Push the buffer onto the pre-queue and re-timestamp it and all other buffers // based on the mapping above. @@ -391,7 +398,7 @@ impl MP4Mux { // Fall through below and pop the first buffer finally } - if let Some((segment, buffer)) = pre_queue.pop_front() { + if let Some((segment, buffer)) = stream.pre_queue.pop_front() { return Ok(Some((segment, buffer))); } @@ -400,23 +407,26 @@ impl MP4Mux { // for calculating the duration to the previous buffer, and then put into the pre-queue // - or this is the very first buffer and we just put it into the queue overselves above if self.obj().class().as_ref().variant == super::Variant::ONVIF { - if sinkpad.is_eos() { + if stream.sinkpad.is_eos() { return Ok(None); } unreachable!(); } - let buffer = match sinkpad.pop_buffer() { - None => return Ok(None), - Some(buffer) => buffer, + let Some(buffer) = stream.sinkpad.pop_buffer() else { + return Ok(None); }; + Self::check_buffer( + &buffer, + &stream.sinkpad, + stream.delta_frames, + stream.discard_header_buffers, + )?; - Self::check_buffer(&buffer, sinkpad, delta_frames)?; - - let segment = match sinkpad.segment().downcast::().ok() { + let segment = match stream.sinkpad.segment().downcast::().ok() { Some(segment) => segment, None => { - gst::error!(CAT, obj: sinkpad, "Got buffer before segment"); + gst::error!(CAT, obj: stream.sinkpad, "Got buffer before segment"); return Err(gst::FlowError::Error); } }; @@ -442,6 +452,12 @@ impl MP4Mux { Some(PendingBuffer { duration: Some(_), .. }) => return Ok(()), + Some(PendingBuffer { ref buffer, .. }) + if stream.discard_header_buffers + && buffer.flags().contains(gst::BufferFlags::HEADER) => + { + return Err(gst_base::AGGREGATOR_FLOW_NEED_DATA); + } Some(PendingBuffer { timestamp, pts, @@ -449,13 +465,15 @@ impl MP4Mux { ref mut duration, .. }) => { - // Already have a pending buffer but no duration, so try to get that now - let (segment, buffer) = match self.peek_buffer( + let peek_outcome = self.peek_buffer( &stream.sinkpad, stream.delta_frames, + stream.discard_header_buffers, &mut stream.pre_queue, &stream.running_time_utc_time_mapping, - )? { + )?; + // Already have a pending buffer but no duration, so try to get that now + let (segment, buffer) = match peek_outcome { Some(res) => res, None => { if stream.sinkpad.is_eos() { @@ -532,12 +550,7 @@ impl MP4Mux { None => { // Have no buffer queued at all yet - let (segment, buffer) = match self.pop_buffer( - &stream.sinkpad, - stream.delta_frames, - &mut stream.pre_queue, - &mut stream.running_time_utc_time_mapping, - )? { + let (segment, buffer) = match self.pop_buffer(stream)? { Some(res) => res, None => { if stream.sinkpad.is_eos() { @@ -870,6 +883,7 @@ impl MP4Mux { let s = caps.structure(0).unwrap(); let mut delta_frames = super::DeltaFrames::IntraOnly; + let mut discard_header_buffers = false; match s.name().as_str() { "video/x-h264" | "video/x-h265" => { if !s.has_field_with_type("codec_data", gst::Buffer::static_type()) { @@ -913,6 +927,13 @@ impl MP4Mux { return Err(gst::FlowError::NotNegotiated); } } + "audio/x-flac" => { + discard_header_buffers = true; + if let Err(e) = s.get::("streamheader") { + gst::error!(CAT, obj: pad, "Muxing FLAC into MP4 needs streamheader: {}", e); + return Err(gst::FlowError::NotNegotiated); + }; + } "audio/x-alaw" | "audio/x-mulaw" => (), "audio/x-adpcm" => (), "application/x-onvif-metadata" => (), @@ -924,6 +945,7 @@ impl MP4Mux { pre_queue: VecDeque::new(), caps, delta_frames, + discard_header_buffers, chunks: Vec::new(), pending_buffer: None, queued_chunk_time: gst::ClockTime::ZERO, @@ -1523,6 +1545,11 @@ impl ElementImpl for ISOMP4Mux { .field("channels", gst::IntRange::new(1i32, 8)) .field("rate", gst::IntRange::new(1, i32::MAX)) .build(), + gst::Structure::builder("audio/x-flac") + .field("framed", true) + .field("channels", gst::IntRange::::new(1, 8)) + .field("rate", gst::IntRange::::new(1, 10 * u16::MAX as i32)) + .build(), ] .into_iter() .collect::(), diff --git a/mux/mp4/tests/tests.rs b/mux/mp4/tests/tests.rs index fc6e360f..f3f2ece3 100644 --- a/mux/mp4/tests/tests.rs +++ b/mux/mp4/tests/tests.rs @@ -7,6 +7,8 @@ // SPDX-License-Identifier: MPL-2.0 // +use std::path::Path; + use gst::prelude::*; use gst_pbutils::prelude::*; @@ -20,33 +22,57 @@ fn init() { }); } -#[test] -fn test_basic() { - init(); +struct Pipeline(gst::Pipeline); +impl std::ops::Deref for Pipeline { + type Target = gst::Pipeline; - struct Pipeline(gst::Pipeline); - impl std::ops::Deref for Pipeline { - type Target = gst::Pipeline; - - fn deref(&self) -> &Self::Target { - &self.0 - } + fn deref(&self) -> &Self::Target { + &self.0 } - impl Drop for Pipeline { - fn drop(&mut self) { - let _ = self.0.set_state(gst::State::Null); - } +} +impl Drop for Pipeline { + fn drop(&mut self) { + let _ = self.0.set_state(gst::State::Null); } +} - let pipeline = match gst::parse::launch( - "videotestsrc num-buffers=99 ! x264enc ! mux. \ - audiotestsrc num-buffers=140 ! fdkaacenc ! mux. \ - isomp4mux name=mux ! filesink name=sink \ - ", - ) { - Ok(pipeline) => Pipeline(pipeline.downcast::().unwrap()), - Err(_) => return, +impl Pipeline { + fn into_completion(self) { + self.set_state(gst::State::Playing) + .expect("Unable to set the pipeline to the `Playing` state"); + + for msg in self.bus().unwrap().iter_timed(gst::ClockTime::NONE) { + use gst::MessageView; + + match msg.view() { + MessageView::Eos(..) => break, + MessageView::Error(err) => { + panic!( + "Error from {:?}: {} ({:?})", + err.src().map(|s| s.path_string()), + err.error(), + err.debug() + ); + } + _ => (), + } + } + + self.set_state(gst::State::Null) + .expect("Unable to set the pipeline to the `Null` state"); + } +} + +fn test_basic_with(video_enc: &str, audio_enc: &str, cb: impl FnOnce(&Path)) { + let Ok(pipeline) = gst::parse::launch(&format!( + "videotestsrc num-buffers=99 ! {video_enc} ! mux. \ + audiotestsrc num-buffers=140 ! {audio_enc} ! mux. \ + isomp4mux name=mux ! filesink name=sink" + )) else { + println!("could not build encoding pipeline"); + return; }; + let pipeline = Pipeline(pipeline.downcast::().unwrap()); let dir = tempfile::TempDir::new().unwrap(); let mut location = dir.path().to_owned(); @@ -54,73 +80,75 @@ fn test_basic() { let sink = pipeline.by_name("sink").unwrap(); sink.set_property("location", location.to_str().expect("Non-UTF8 filename")); + pipeline.into_completion(); - pipeline - .set_state(gst::State::Playing) - .expect("Unable to set the pipeline to the `Playing` state"); - - for msg in pipeline.bus().unwrap().iter_timed(gst::ClockTime::NONE) { - use gst::MessageView; - - match msg.view() { - MessageView::Eos(..) => break, - MessageView::Error(err) => { - panic!( - "Error from {:?}: {} ({:?})", - err.src().map(|s| s.path_string()), - err.error(), - err.debug() - ); - } - _ => (), - } - } - - pipeline - .set_state(gst::State::Null) - .expect("Unable to set the pipeline to the `Null` state"); - - drop(pipeline); - - let discoverer = gst_pbutils::Discoverer::new(gst::ClockTime::from_seconds(5)) - .expect("Failed to create discoverer"); - let info = discoverer - .discover_uri( - url::Url::from_file_path(&location) - .expect("Failed to convert filename to URL") - .as_str(), - ) - .expect("Failed to discover MP4 file"); - - assert_eq!(info.duration(), Some(gst::ClockTime::from_mseconds(3_300))); - - let audio_streams = info.audio_streams(); - assert_eq!(audio_streams.len(), 1); - let audio_stream = &audio_streams[0]; - assert_eq!(audio_stream.channels(), 1); - assert_eq!(audio_stream.sample_rate(), 44_100); - let caps = audio_stream.caps().unwrap(); - assert!( - caps.can_intersect( - &gst::Caps::builder("audio/mpeg") - .any_features() - .field("mpegversion", 4i32) - .build() - ), - "Unexpected audio caps {caps:?}" - ); - - let video_streams = info.video_streams(); - assert_eq!(video_streams.len(), 1); - let video_stream = &video_streams[0]; - assert_eq!(video_stream.width(), 320); - assert_eq!(video_stream.height(), 240); - assert_eq!(video_stream.framerate(), gst::Fraction::new(30, 1)); - assert_eq!(video_stream.par(), gst::Fraction::new(1, 1)); - assert!(!video_stream.is_interlaced()); - let caps = video_stream.caps().unwrap(); - assert!( - caps.can_intersect(&gst::Caps::builder("video/x-h264").any_features().build()), - "Unexpected video caps {caps:?}" - ); + cb(&location) +} + +#[test] +fn test_basic_x264_aac() { + init(); + test_basic_with("x264enc", "fdkaacenc", |location| { + let discoverer = gst_pbutils::Discoverer::new(gst::ClockTime::from_seconds(5)) + .expect("Failed to create discoverer"); + let info = discoverer + .discover_uri( + url::Url::from_file_path(location) + .expect("Failed to convert filename to URL") + .as_str(), + ) + .expect("Failed to discover MP4 file"); + + assert_eq!(info.duration(), Some(gst::ClockTime::from_mseconds(3_300))); + + let audio_streams = info.audio_streams(); + assert_eq!(audio_streams.len(), 1); + let audio_stream = &audio_streams[0]; + assert_eq!(audio_stream.channels(), 1); + assert_eq!(audio_stream.sample_rate(), 44_100); + let caps = audio_stream.caps().unwrap(); + assert!( + caps.can_intersect( + &gst::Caps::builder("audio/mpeg") + .any_features() + .field("mpegversion", 4i32) + .build() + ), + "Unexpected audio caps {caps:?}" + ); + + let video_streams = info.video_streams(); + assert_eq!(video_streams.len(), 1); + let video_stream = &video_streams[0]; + assert_eq!(video_stream.width(), 320); + assert_eq!(video_stream.height(), 240); + assert_eq!(video_stream.framerate(), gst::Fraction::new(30, 1)); + assert_eq!(video_stream.par(), gst::Fraction::new(1, 1)); + assert!(!video_stream.is_interlaced()); + let caps = video_stream.caps().unwrap(); + assert!( + caps.can_intersect(&gst::Caps::builder("video/x-h264").any_features().build()), + "Unexpected video caps {caps:?}" + ); + }) +} + +#[test] +fn test_roundtrip_vp9_flac() { + init(); + test_basic_with("vp9enc ! vp9parse", "flacenc ! flacparse", |location| { + let Ok(pipeline) = gst::parse::launch( + "filesrc name=src ! qtdemux name=demux \ + demux.audio_0 ! queue ! flacdec ! fakesink \ + demux.video_0 ! queue ! vp9dec ! fakesink", + ) else { + panic!("could not build decoding pipeline") + }; + let pipeline = Pipeline(pipeline.downcast::().unwrap()); + pipeline + .by_name("src") + .unwrap() + .set_property("location", location.display().to_string()); + pipeline.into_completion(); + }) } From 0c55ac9e31a65ec8756f08ad38f66540b326b431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Laignel?= Date: Tue, 9 Apr 2024 17:50:12 +0200 Subject: [PATCH 03/16] webrtcsink: don't panic with bitrate handling unsupported encoders When an encoder was not supported by the `VideoEncoder` `bitrate` accessors, an `unimplemented` panic would occur which would poison `state` & `settings` `Mutex`s resulting in other threads panicking, notably entering `end_session()`, which lead to many failures in `BinImplExt::parent_remove_element()` until a segmentation fault ended the process. This was observed using `vaapivp9enc`. This commit logs a warning if an encoder isn't supported by the `bitrate` accessors and silently by-passes `bitrate`-related operations when unsupported. Part-of: --- net/webrtc/src/webrtcsink/homegrown_cc.rs | 9 +-- net/webrtc/src/webrtcsink/imp.rs | 81 +++++++++++++++++------ net/webrtc/src/webrtcsink/mod.rs | 2 + 3 files changed, 67 insertions(+), 25 deletions(-) diff --git a/net/webrtc/src/webrtcsink/homegrown_cc.rs b/net/webrtc/src/webrtcsink/homegrown_cc.rs index f8ea66db..22b07cd9 100644 --- a/net/webrtc/src/webrtcsink/homegrown_cc.rs +++ b/net/webrtc/src/webrtcsink/homegrown_cc.rs @@ -413,10 +413,11 @@ impl CongestionController { let fec_percentage = (fec_ratio * 50f64) as u32; for encoder in encoders.iter_mut() { - encoder.set_bitrate(element, target_bitrate); - encoder - .transceiver - .set_property("fec-percentage", fec_percentage); + if encoder.set_bitrate(element, target_bitrate).is_ok() { + encoder + .transceiver + .set_property("fec-percentage", fec_percentage); + } } } } diff --git a/net/webrtc/src/webrtcsink/imp.rs b/net/webrtc/src/webrtcsink/imp.rs index 20b0d1d4..22dd25d8 100644 --- a/net/webrtc/src/webrtcsink/imp.rs +++ b/net/webrtc/src/webrtcsink/imp.rs @@ -952,8 +952,24 @@ impl VideoEncoder { }) } - fn bitrate(&self) -> i32 { - match self.factory_name.as_str() { + fn is_bitrate_supported(factory_name: &str) -> bool { + matches!( + factory_name, + "vp8enc" + | "vp9enc" + | "x264enc" + | "nvh264enc" + | "vaapih264enc" + | "vaapivp8enc" + | "qsvh264enc" + | "nvv4l2h264enc" + | "nvv4l2vp8enc" + | "nvv4l2vp9enc" + ) + } + + fn bitrate(&self) -> Result { + let bitrate = match self.factory_name.as_str() { "vp8enc" | "vp9enc" => self.element.property::("target-bitrate"), "x264enc" | "nvh264enc" | "vaapih264enc" | "vaapivp8enc" | "qsvh264enc" => { (self.element.property::("bitrate") * 1000) as i32 @@ -961,8 +977,10 @@ impl VideoEncoder { "nvv4l2h264enc" | "nvv4l2vp8enc" | "nvv4l2vp9enc" => { (self.element.property::("bitrate")) as i32 } - factory => unimplemented!("Factory {} is currently not supported", factory), - } + _ => return Err(WebRTCSinkError::BitrateNotSupported), + }; + + Ok(bitrate) } fn scale_height_round_2(&self, height: i32) -> i32 { @@ -979,16 +997,21 @@ impl VideoEncoder { (width + 1) & !1 } - pub(crate) fn set_bitrate(&mut self, element: &super::BaseWebRTCSink, bitrate: i32) { + pub(crate) fn set_bitrate( + &mut self, + element: &super::BaseWebRTCSink, + bitrate: i32, + ) -> Result<(), WebRTCSinkError> { match self.factory_name.as_str() { "vp8enc" | "vp9enc" => self.element.set_property("target-bitrate", bitrate), - "x264enc" | "nvh264enc" | "vaapih264enc" | "vaapivp8enc" | "qsvh264enc" => self - .element - .set_property("bitrate", (bitrate / 1000) as u32), + "x264enc" | "nvh264enc" | "vaapih264enc" | "vaapivp8enc" | "qsvh264enc" => { + self.element + .set_property("bitrate", (bitrate / 1000) as u32); + } "nvv4l2h264enc" | "nvv4l2vp8enc" | "nvv4l2vp9enc" => { self.element.set_property("bitrate", bitrate as u32) } - factory => unimplemented!("Factory {} is currently not supported", factory), + _ => return Err(WebRTCSinkError::BitrateNotSupported), } let current_caps = self.filter.property::("caps"); @@ -1052,11 +1075,13 @@ impl VideoEncoder { self.filter.set_property("caps", caps); } + + Ok(()) } fn gather_stats(&self) -> gst::Structure { gst::Structure::builder("application/x-webrtcsink-video-encoder-stats") - .field("bitrate", self.bitrate()) + .field("bitrate", self.bitrate().unwrap_or(0i32)) .field("mitigation-mode", self.mitigation_mode) .field("codec-name", self.codec_name.as_str()) .field( @@ -1339,19 +1364,21 @@ impl Session { WebRTCSinkCongestionControl::Disabled => { // If congestion control is disabled, we simply use the highest // known "safe" value for the bitrate. - enc.set_bitrate(element, self.cc_info.max_bitrate as i32); + let _ = enc.set_bitrate(element, self.cc_info.max_bitrate as i32); enc.transceiver.set_property("fec-percentage", 50u32); } WebRTCSinkCongestionControl::Homegrown => { if let Some(congestion_controller) = self.congestion_controller.as_mut() { - congestion_controller.target_bitrate_on_delay += enc.bitrate(); - congestion_controller.target_bitrate_on_loss = - congestion_controller.target_bitrate_on_delay; - enc.transceiver.set_property("fec-percentage", 0u32); + if let Ok(bitrate) = enc.bitrate() { + congestion_controller.target_bitrate_on_delay += bitrate; + congestion_controller.target_bitrate_on_loss = + congestion_controller.target_bitrate_on_delay; + enc.transceiver.set_property("fec-percentage", 0u32); + } } else { /* If congestion control is disabled, we simply use the highest * known "safe" value for the bitrate. */ - enc.set_bitrate(element, self.cc_info.max_bitrate as i32); + let _ = enc.set_bitrate(element, self.cc_info.max_bitrate as i32); enc.transceiver.set_property("fec-percentage", 50u32); } } @@ -1497,6 +1524,7 @@ impl BaseWebRTCSink { fn configure_congestion_control( &self, payloader: &gst::Element, + codec: &Codec, extension_configuration_type: ExtensionConfigurationType, ) -> Result<(), Error> { if let ExtensionConfigurationType::Skip = extension_configuration_type { @@ -1505,6 +1533,16 @@ impl BaseWebRTCSink { let settings = self.settings.lock().unwrap(); + if codec.is_video() { + if let Some(enc_name) = codec.encoder_name().as_deref() { + if !VideoEncoder::is_bitrate_supported(enc_name) { + gst::error!(CAT, imp: self, "Bitrate handling is not supported yet for {enc_name}"); + + return Ok(()); + } + } + } + if settings.cc_info.heuristic == WebRTCSinkCongestionControl::Disabled { return Ok(()); } @@ -1620,7 +1658,7 @@ impl BaseWebRTCSink { payloader.set_property("ssrc", ssrc); } - self.configure_congestion_control(payloader, extension_configuration_type) + self.configure_congestion_control(payloader, codec, extension_configuration_type) } fn generate_ssrc( @@ -2951,10 +2989,11 @@ impl BaseWebRTCSink { } for encoder in session.encoders.iter_mut() { - encoder.set_bitrate(element, encoders_bitrate); - encoder - .transceiver - .set_property("fec-percentage", (fec_percentage as u32).min(100)); + if encoder.set_bitrate(element, encoders_bitrate).is_ok() { + encoder + .transceiver + .set_property("fec-percentage", (fec_percentage as u32).min(100)); + } } } } diff --git a/net/webrtc/src/webrtcsink/mod.rs b/net/webrtc/src/webrtcsink/mod.rs index f566868a..616ca67a 100644 --- a/net/webrtc/src/webrtcsink/mod.rs +++ b/net/webrtc/src/webrtcsink/mod.rs @@ -87,6 +87,8 @@ pub enum WebRTCSinkError { peer_id: String, details: String, }, + #[error("Bitrate handling currently not supported for requested encoder")] + BitrateNotSupported, } impl Default for BaseWebRTCSink { From 6d36b9160afd7a1fec8f4360f92957258aab98c6 Mon Sep 17 00:00:00 2001 From: Sanchayan Maity Date: Fri, 5 Apr 2024 13:43:10 +0530 Subject: [PATCH 04/16] aws: Introduce a property to use path-style addressing AWS SDK switched to virtual addressing as default instead of path style earlier. While MinIO supports virtual host style requests, path style requests are the default. Introduce a property to allow the use of path style addressing if required. For more information, see https://github.com/minio/minio/blob/master/docs/config/README.md#domain https://docs.aws.amazon.com/AmazonS3/latest/userguide/VirtualHosting.html Part-of: --- docs/plugins/gst_plugins_cache.json | 48 +++++++++++++++++++++++++++++ net/aws/src/s3hlssink/imp.rs | 13 ++++++++ net/aws/src/s3sink/multipartsink.rs | 13 ++++++++ net/aws/src/s3sink/putobjectsink.rs | 13 ++++++++ net/aws/src/s3src/imp.rs | 13 ++++++++ 5 files changed, 100 insertions(+) diff --git a/docs/plugins/gst_plugins_cache.json b/docs/plugins/gst_plugins_cache.json index 9731c0f1..f997a112 100644 --- a/docs/plugins/gst_plugins_cache.json +++ b/docs/plugins/gst_plugins_cache.json @@ -79,6 +79,18 @@ "type": "gchararray", "writable": true }, + "force-path-style": { + "blurb": "Force client to use path-style addressing for buckets", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "false", + "mutable": "null", + "readable": true, + "type": "gboolean", + "writable": true + }, "hlssink": { "blurb": "The underlying HLS sink being used", "conditionally-available": false, @@ -315,6 +327,18 @@ "type": "gboolean", "writable": true }, + "force-path-style": { + "blurb": "Force client to use path-style addressing for buckets", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "false", + "mutable": "null", + "readable": true, + "type": "gboolean", + "writable": true + }, "key": { "blurb": "The key of the file to write", "conditionally-available": false, @@ -529,6 +553,18 @@ "type": "gchararray", "writable": true }, + "force-path-style": { + "blurb": "Force client to use path-style addressing for buckets", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "false", + "mutable": "null", + "readable": true, + "type": "gboolean", + "writable": true + }, "key": { "blurb": "The key of the file to write", "conditionally-available": false, @@ -747,6 +783,18 @@ "type": "gchararray", "writable": true }, + "force-path-style": { + "blurb": "Force client to use path-style addressing for buckets", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "false", + "mutable": "null", + "readable": true, + "type": "gboolean", + "writable": true + }, "request-timeout": { "blurb": "Timeout for each S3 request (in ms, set to -1 for infinity)", "conditionally-available": false, diff --git a/net/aws/src/s3hlssink/imp.rs b/net/aws/src/s3hlssink/imp.rs index 6302fdfd..2f35bb97 100644 --- a/net/aws/src/s3hlssink/imp.rs +++ b/net/aws/src/s3hlssink/imp.rs @@ -39,6 +39,7 @@ const S3_CHANNEL_SIZE: usize = 32; const S3_ACL_DEFAULT: ObjectCannedAcl = ObjectCannedAcl::Private; const DEFAULT_RETRY_ATTEMPTS: u32 = 5; const DEFAULT_TIMEOUT_IN_MSECS: u64 = 15000; +const DEFAULT_FORCE_PATH_STYLE: bool = false; struct Settings { access_key: Option, @@ -57,6 +58,7 @@ struct Settings { video_sink: bool, config: Option, endpoint_uri: Option, + force_path_style: bool, } impl Default for Settings { @@ -79,6 +81,7 @@ impl Default for Settings { video_sink: false, config: None, endpoint_uri: None, + force_path_style: DEFAULT_FORCE_PATH_STYLE, } } } @@ -376,6 +379,7 @@ impl S3HlsSink { let sdk_config = settings.config.as_ref().expect("SDK config must be set"); let config_builder = config::Builder::from(sdk_config) + .force_path_style(settings.force_path_style) .region(settings.s3_region.clone()) .retry_config(RetryConfig::standard().with_max_attempts(settings.retry_attempts)); @@ -529,6 +533,11 @@ impl ObjectImpl for S3HlsSink { .blurb("The S3 endpoint URI to use") .mutable_ready() .build(), + glib::ParamSpecBoolean::builder("force-path-style") + .nick("Force path style") + .blurb("Force client to use path-style addressing for buckets") + .default_value(DEFAULT_FORCE_PATH_STYLE) + .build(), ] }); @@ -586,6 +595,9 @@ impl ObjectImpl for S3HlsSink { .get::>() .expect("type checked upstream"); } + "force-path-style" => { + settings.force_path_style = value.get::().expect("type checked upstream"); + } _ => unimplemented!(), } } @@ -606,6 +618,7 @@ impl ObjectImpl for S3HlsSink { "request-timeout" => (settings.request_timeout.as_millis() as u64).to_value(), "stats" => self.create_stats().to_value(), "endpoint-uri" => settings.endpoint_uri.to_value(), + "force-path-style" => settings.force_path_style.to_value(), _ => unimplemented!(), } } diff --git a/net/aws/src/s3sink/multipartsink.rs b/net/aws/src/s3sink/multipartsink.rs index 5554fb09..38751546 100644 --- a/net/aws/src/s3sink/multipartsink.rs +++ b/net/aws/src/s3sink/multipartsink.rs @@ -38,6 +38,7 @@ use crate::s3utils::{self, duration_from_millis, duration_to_millis, WaitError}; use super::OnError; +const DEFAULT_FORCE_PATH_STYLE: bool = false; const DEFAULT_RETRY_ATTEMPTS: u32 = 5; const DEFAULT_BUFFER_SIZE: u64 = 5 * 1024 * 1024; const DEFAULT_MULTIPART_UPLOAD_ON_ERROR: OnError = OnError::DoNothing; @@ -114,6 +115,7 @@ struct Settings { multipart_upload_on_error: OnError, request_timeout: Duration, endpoint_uri: Option, + force_path_style: bool, } impl Settings { @@ -168,6 +170,7 @@ impl Default for Settings { multipart_upload_on_error: DEFAULT_MULTIPART_UPLOAD_ON_ERROR, request_timeout: Duration::from_millis(DEFAULT_REQUEST_TIMEOUT_MSEC), endpoint_uri: None, + force_path_style: DEFAULT_FORCE_PATH_STYLE, } } } @@ -524,6 +527,7 @@ impl S3Sink { })?; let config_builder = config::Builder::from(&sdk_config) + .force_path_style(settings.force_path_style) .retry_config(RetryConfig::standard().with_max_attempts(settings.retry_attempts)); let config = if let Some(ref uri) = settings.endpoint_uri { @@ -775,6 +779,11 @@ impl ObjectImpl for S3Sink { .nick("content-disposition") .blurb("Content-Disposition header to set for uploaded object") .build(), + glib::ParamSpecBoolean::builder("force-path-style") + .nick("Force path style") + .blurb("Force client to use path-style addressing for buckets") + .default_value(DEFAULT_FORCE_PATH_STYLE) + .build(), ] }); @@ -888,6 +897,9 @@ impl ObjectImpl for S3Sink { .get::>() .expect("type checked upstream"); } + "force-path-style" => { + settings.force_path_style = value.get::().expect("type checked upstream"); + } _ => unimplemented!(), } } @@ -929,6 +941,7 @@ impl ObjectImpl for S3Sink { "endpoint-uri" => settings.endpoint_uri.to_value(), "content-type" => settings.content_type.to_value(), "content-disposition" => settings.content_disposition.to_value(), + "force-path-style" => settings.force_path_style.to_value(), _ => unimplemented!(), } } diff --git a/net/aws/src/s3sink/putobjectsink.rs b/net/aws/src/s3sink/putobjectsink.rs index 8b13d5f3..fb3ecd35 100644 --- a/net/aws/src/s3sink/putobjectsink.rs +++ b/net/aws/src/s3sink/putobjectsink.rs @@ -36,6 +36,7 @@ const DEFAULT_FLUSH_INTERVAL_BUFFERS: u64 = 1; const DEFAULT_FLUSH_INTERVAL_BYTES: u64 = 0; const DEFAULT_FLUSH_INTERVAL_TIME: gst::ClockTime = gst::ClockTime::from_nseconds(0); const DEFAULT_FLUSH_ON_ERROR: bool = false; +const DEFAULT_FORCE_PATH_STYLE: bool = false; // General setting for create / abort requests const DEFAULT_REQUEST_TIMEOUT_MSEC: u64 = 15_000; @@ -80,6 +81,7 @@ struct Settings { retry_attempts: u32, request_timeout: Duration, endpoint_uri: Option, + force_path_style: bool, flush_interval_buffers: u64, flush_interval_bytes: u64, flush_interval_time: Option, @@ -136,6 +138,7 @@ impl Default for Settings { retry_attempts: DEFAULT_RETRY_ATTEMPTS, request_timeout: Duration::from_millis(DEFAULT_REQUEST_TIMEOUT_MSEC), endpoint_uri: None, + force_path_style: DEFAULT_FORCE_PATH_STYLE, flush_interval_buffers: DEFAULT_FLUSH_INTERVAL_BUFFERS, flush_interval_bytes: DEFAULT_FLUSH_INTERVAL_BYTES, flush_interval_time: Some(DEFAULT_FLUSH_INTERVAL_TIME), @@ -293,6 +296,7 @@ impl S3PutObjectSink { })?; let config_builder = config::Builder::from(&sdk_config) + .force_path_style(settings.force_path_style) .retry_config(RetryConfig::standard().with_max_attempts(settings.retry_attempts)); let config = if let Some(ref uri) = settings.endpoint_uri { @@ -446,6 +450,11 @@ impl ObjectImpl for S3PutObjectSink { .blurb("Whether to write out the data on error (like stopping without an EOS)") .default_value(DEFAULT_FLUSH_ON_ERROR) .build(), + glib::ParamSpecBoolean::builder("force-path-style") + .nick("Force path style") + .blurb("Force client to use path-style addressing for buckets") + .default_value(DEFAULT_FORCE_PATH_STYLE) + .build(), ] }); @@ -542,6 +551,9 @@ impl ObjectImpl for S3PutObjectSink { "flush-on-error" => { settings.flush_on_error = value.get::().expect("type checked upstream"); } + "force-path-style" => { + settings.force_path_style = value.get::().expect("type checked upstream"); + } _ => unimplemented!(), } } @@ -575,6 +587,7 @@ impl ObjectImpl for S3PutObjectSink { "flush-interval-bytes" => settings.flush_interval_bytes.to_value(), "flush-interval-time" => settings.flush_interval_time.to_value(), "flush-on-error" => settings.flush_on_error.to_value(), + "force-path-style" => settings.force_path_style.to_value(), _ => unimplemented!(), } } diff --git a/net/aws/src/s3src/imp.rs b/net/aws/src/s3src/imp.rs index bfb8b76f..25e6f7cd 100644 --- a/net/aws/src/s3src/imp.rs +++ b/net/aws/src/s3src/imp.rs @@ -29,6 +29,7 @@ use gst_base::subclass::prelude::*; use crate::s3url::*; use crate::s3utils::{self, duration_from_millis, duration_to_millis, WaitError}; +const DEFAULT_FORCE_PATH_STYLE: bool = false; const DEFAULT_RETRY_ATTEMPTS: u32 = 5; const DEFAULT_REQUEST_TIMEOUT_MSEC: u64 = 15000; const DEFAULT_RETRY_DURATION_MSEC: u64 = 60_000; @@ -53,6 +54,7 @@ struct Settings { retry_attempts: u32, request_timeout: Duration, endpoint_uri: Option, + force_path_style: bool, } impl Default for Settings { @@ -66,6 +68,7 @@ impl Default for Settings { retry_attempts: DEFAULT_RETRY_ATTEMPTS, request_timeout: duration, endpoint_uri: None, + force_path_style: DEFAULT_FORCE_PATH_STYLE, } } } @@ -128,6 +131,7 @@ impl S3Src { })?; let config_builder = config::Builder::from(&sdk_config) + .force_path_style(settings.force_path_style) .retry_config(RetryConfig::standard().with_max_attempts(settings.retry_attempts)); let config = if let Some(ref uri) = settings.endpoint_uri { @@ -316,6 +320,11 @@ impl ObjectImpl for S3Src { .nick("S3 endpoint URI") .blurb("The S3 endpoint URI to use") .build(), + glib::ParamSpecBoolean::builder("force-path-style") + .nick("Force path style") + .blurb("Force client to use path-style addressing for buckets") + .default_value(DEFAULT_FORCE_PATH_STYLE) + .build(), ] }); @@ -365,6 +374,9 @@ impl ObjectImpl for S3Src { .get::>() .expect("type checked upstream"); } + "force-path-style" => { + settings.force_path_style = value.get::().expect("type checked upstream"); + } _ => unimplemented!(), } } @@ -391,6 +403,7 @@ impl ObjectImpl for S3Src { } "retry-attempts" => settings.retry_attempts.to_value(), "endpoint-uri" => settings.endpoint_uri.to_value(), + "force-path-style" => settings.force_path_style.to_value(), _ => unimplemented!(), } } From 47429e2ed856181cb46ce5a96698baecfec313fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Laignel?= Date: Tue, 9 Apr 2024 16:24:16 +0200 Subject: [PATCH 05/16] gccbwe: don't log an error when handling a buffer list while stopping When `webrtcsink` was stopped, `gccbwe` could log an error if it was handling a buffer list. This commit logs an error only if `push_list()` returned an error other than `Flushing`. Part-of: --- net/rtp/src/gcc/imp.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/rtp/src/gcc/imp.rs b/net/rtp/src/gcc/imp.rs index 30ca1697..598405a3 100644 --- a/net/rtp/src/gcc/imp.rs +++ b/net/rtp/src/gcc/imp.rs @@ -1096,7 +1096,9 @@ impl BandwidthEstimator { if !list.is_empty() { if let Err(err) = bwe.imp().push_list(list) { - gst::error!(CAT, obj: bwe, "pause task, reason: {err:?}"); + if err != gst::FlowError::Flushing { + gst::error!(CAT, obj: bwe, "pause task, reason: {err:?}"); + } pause() } } From e1b8b8befda33cd046d57224bbb5369c15b7be34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Laignel?= Date: Sun, 14 Apr 2024 16:25:41 +0200 Subject: [PATCH 06/16] webrtcsink: don't panic if input CAPS are not supported If a user constrained the supported CAPS, for instance using `video-caps`: ```shell gst-launch-1.0 videotestsrc ! video/x-raw,format=I420 ! x264enc \ ! webrtcsink video-caps=video/x-vp8 ``` ... a panic would occur which was internally caught without the user being informed except for the following message which was written to stderr: > thread 'tokio-runtime-worker' panicked at net/webrtc/src/webrtcsink/imp.rs:3533:22: > expected audio or video raw caps: video/x-h264, [...]
> note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace The pipeline kept running. This commit converts the panic into an `Error` which bubbles up as an element `StreamError::CodecNotFound` which can be handled by the application. With the above `gst-launch`, this terminates the pipeline with: > [...] ERROR webrtcsink net/webrtc/src/webrtcsink/imp.rs:3771:gstrswebrtc:: > webrtcsink::imp::BaseWebRTCSink::start_stream_discovery_if_needed::{{closure}}: > Error running discovery: Unsupported caps: video/x-h264, [...]
> ERROR: from element /GstPipeline:pipeline0/GstWebRTCSink:webrtcsink0: > There is no codec present that can handle the stream's type.
> Additional debug info:
> net/webrtc/src/webrtcsink/imp.rs(3772): gstrswebrtc::webrtcsink::imp::BaseWebRTCSink:: > start_stream_discovery_if_needed::{{closure}} (): /GstPipeline:pipeline0/GstWebRTCSink:webrtcsink0: > Failed to look up output caps: Unsupported caps: video/x-h264, [...]
> Execution ended after 0:00:00.055716661
> Setting pipeline to NULL ... Part-of: --- net/webrtc/src/webrtcsink/imp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/webrtc/src/webrtcsink/imp.rs b/net/webrtc/src/webrtcsink/imp.rs index 22dd25d8..2f2549f5 100644 --- a/net/webrtc/src/webrtcsink/imp.rs +++ b/net/webrtc/src/webrtcsink/imp.rs @@ -3439,7 +3439,7 @@ impl BaseWebRTCSink { let is_video = match sink_caps.structure(0).unwrap().name().as_str() { "video/x-raw" => true, "audio/x-raw" => false, - _ => panic!("expected audio or video raw caps: {sink_caps}"), + _ => anyhow::bail!("Unsupported caps: {}", discovery_info.caps), }; codecs From b498c44df5c0608b453948a765c2a19dd16707b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 25 Apr 2024 15:37:28 +0300 Subject: [PATCH 07/16] rtpgccbwe: Move away from deprecated time::Instant to std::time::Instant Part-of: --- net/rtp/src/gcc/imp.rs | 54 +++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/net/rtp/src/gcc/imp.rs b/net/rtp/src/gcc/imp.rs index 598405a3..e1181fff 100644 --- a/net/rtp/src/gcc/imp.rs +++ b/net/rtp/src/gcc/imp.rs @@ -26,6 +26,7 @@ use std::{ fmt::Debug, mem, sync::Mutex, + time::Instant, }; use time::Duration; @@ -268,7 +269,7 @@ struct Detector { last_received_packets: BTreeMap, // Order by seqnums, front is the newest, back is the oldest // Last loss update - last_loss_update: Option, + last_loss_update: Option, // Moving average of the packet loss loss_average: f64, @@ -280,13 +281,13 @@ struct Detector { // Threshold fields threshold: Duration, - last_threshold_update: Option, + last_threshold_update: Option, num_deltas: i64, // Overuse related fields increasing_counter: u32, last_overuse_estimate: Duration, - last_use_detector_update: time::Instant, + last_use_detector_update: Instant, increasing_duration: Duration, // round-trip-time estimations @@ -337,7 +338,7 @@ impl Detector { last_threshold_update: None, num_deltas: 0, - last_use_detector_update: time::Instant::now(), + last_use_detector_update: Instant::now(), increasing_counter: 0, last_overuse_estimate: Duration::ZERO, increasing_duration: Duration::ZERO, @@ -519,11 +520,14 @@ impl Detector { } fn compute_loss_average(&mut self, loss_fraction: f64) { - let now = time::Instant::now(); + let now = Instant::now(); if let Some(ref last_update) = self.last_loss_update { self.loss_average = loss_fraction - + (-(now - *last_update).whole_milliseconds() as f64).exp() + + (-Duration::try_from(now - *last_update) + .unwrap() + .whole_milliseconds() as f64) + .exp() * (self.loss_average - loss_fraction); } @@ -588,7 +592,7 @@ impl Detector { const K_D: f64 = 0.00018; // Table1. Coefficient for the adaptive threshold const MAX_TIME_DELTA: Duration = Duration::milliseconds(100); - let now = time::Instant::now(); + let now = Instant::now(); if self.last_threshold_update.is_none() { self.last_threshold_update = Some(now); } @@ -604,7 +608,9 @@ impl Detector { } else { K_U }; - let time_delta = (now - self.last_threshold_update.unwrap()).min(MAX_TIME_DELTA); + let time_delta = Duration::try_from(now - self.last_threshold_update.unwrap()) + .unwrap() + .min(MAX_TIME_DELTA); let d = abs_estimate - self.threshold; let add = k * d.whole_milliseconds() as f64 * time_delta.whole_milliseconds() as f64; @@ -616,7 +622,7 @@ impl Detector { fn overuse_filter(&mut self) { let (th_usage, estimate) = self.compare_threshold(); - let now = time::Instant::now(); + let now = Instant::now(); let delta = now - self.last_use_detector_update; self.last_use_detector_update = now; match th_usage { @@ -695,14 +701,14 @@ struct State { /// Used in additive mode to track last control time, influences /// calculation of added value according to gcc section 5.5 - last_increase_on_delay: Option, - last_decrease_on_delay: time::Instant, + last_increase_on_delay: Option, + last_decrease_on_delay: Instant, /// Bitrate target based on loss for all video streams. target_bitrate_on_loss: Bitrate, - last_increase_on_loss: time::Instant, - last_decrease_on_loss: time::Instant, + last_increase_on_loss: Instant, + last_decrease_on_loss: Instant, /// Exponential moving average, updated when bitrate is /// decreased @@ -723,7 +729,7 @@ struct State { budget_offset: i64, flow_return: Result, - last_push: time::Instant, + last_push: Instant, } impl Default for State { @@ -731,11 +737,11 @@ impl Default for State { Self { target_bitrate_on_delay: DEFAULT_ESTIMATED_BITRATE, target_bitrate_on_loss: DEFAULT_ESTIMATED_BITRATE, - last_increase_on_loss: time::Instant::now(), - last_decrease_on_loss: time::Instant::now(), + last_increase_on_loss: Instant::now(), + last_decrease_on_loss: Instant::now(), ema: Default::default(), last_increase_on_delay: None, - last_decrease_on_delay: time::Instant::now(), + last_decrease_on_delay: Instant::now(), min_bitrate: DEFAULT_MIN_BITRATE, max_bitrate: DEFAULT_MAX_BITRATE, detector: Detector::new(), @@ -744,7 +750,7 @@ impl Default for State { last_control_op: BandwidthEstimationOp::Increase("Initial increase".into()), flow_return: Err(gst::FlowError::Flushing), clock_entry: None, - last_push: time::Instant::now(), + last_push: Instant::now(), budget_offset: 0, } } @@ -753,8 +759,8 @@ impl Default for State { impl State { // 4. sending engine implementing a "leaky bucket" fn create_buffer_list(&mut self, bwe: &super::BandwidthEstimator) -> BufferList { - let now = time::Instant::now(); - let elapsed = now - self.last_push; + let now = Instant::now(); + let elapsed = Duration::try_from(now - self.last_push).unwrap(); let mut budget = (elapsed.whole_nanoseconds() as i64) .mul_div_round( self.estimated_bitrate as i64, @@ -803,7 +809,7 @@ impl State { } fn compute_increased_rate(&mut self, bwe: &super::BandwidthEstimator) -> Option { - let now = time::Instant::now(); + let now = Instant::now(); let target_bitrate = self.target_bitrate_on_delay as f64; let effective_bitrate = self.detector.effective_bitrate(); let time_since_last_update_ms = match self.last_increase_on_delay { @@ -813,7 +819,7 @@ impl State { return None; } - (now - prev).whole_milliseconds() as f64 + Duration::try_from(now - prev).unwrap().whole_milliseconds() as f64 } }; @@ -950,7 +956,7 @@ impl State { fn loss_control(&mut self, bwe: &super::BandwidthEstimator) -> bool { let loss_ratio = self.detector.loss_ratio(); - let now = time::Instant::now(); + let now = Instant::now(); if loss_ratio > LOSS_DECREASE_THRESHOLD && (now - self.last_decrease_on_loss) > LOSS_UPDATE_INTERVAL @@ -993,7 +999,7 @@ impl State { _ => (), }, NetworkUsage::Over => { - let now = time::Instant::now(); + let now = Instant::now(); if now - self.last_decrease_on_delay > DELAY_UPDATE_INTERVAL { let effective_bitrate = self.detector.effective_bitrate(); let target = From dad5b4e670136679fd17c41095ae2df81c2464d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sat, 20 Apr 2024 23:19:20 +0300 Subject: [PATCH 08/16] gtk4: Implement support for directly importing dmabufs Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/441 Part-of: --- Cargo.lock | 25 ++ Cargo.toml | 1 + docs/plugins/gst_plugins_cache.json | 2 +- video/gtk4/Cargo.toml | 2 + video/gtk4/examples/gtksink.rs | 27 +- video/gtk4/src/sink/frame.rs | 357 ++++++++++++++++++++------- video/gtk4/src/sink/imp.rs | 196 ++++++++++++--- video/gtk4/src/sink/paintable/imp.rs | 68 ++--- video/gtk4/src/sink/paintable/mod.rs | 4 +- 9 files changed, 519 insertions(+), 163 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d4bc9d7d..1f224e82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2424,6 +2424,7 @@ dependencies = [ "gdk4-x11", "gst-plugin-version-helper", "gstreamer", + "gstreamer-allocators", "gstreamer-base", "gstreamer-gl", "gstreamer-gl-egl", @@ -3009,6 +3010,30 @@ dependencies = [ "thiserror", ] +[[package]] +name = "gstreamer-allocators" +version = "0.23.0" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#e117010bc001f87551713c528bf3abff5c9848ae" +dependencies = [ + "glib", + "gstreamer", + "gstreamer-allocators-sys", + "libc", + "once_cell", +] + +[[package]] +name = "gstreamer-allocators-sys" +version = "0.23.0" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#e117010bc001f87551713c528bf3abff5c9848ae" +dependencies = [ + "glib-sys", + "gobject-sys", + "gstreamer-sys", + "libc", + "system-deps", +] + [[package]] name = "gstreamer-app" version = "0.22.4" diff --git a/Cargo.toml b/Cargo.toml index d334f7f1..562312b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -130,6 +130,7 @@ gdk-wayland = { package = "gdk4-wayland", git = "https://github.com/gtk-rs/gtk4- gdk-x11 = { package = "gdk4-x11", git = "https://github.com/gtk-rs/gtk4-rs", branch = "0.8", version = "0.8"} gdk-win32 = { package = "gdk4-win32", git = "https://github.com/gtk-rs/gtk4-rs", branch = "0.8", version = "0.8"} gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.22", version = "0.22" } +gst-allocators = { package = "gstreamer-allocators", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.22", version = "0.22" } gst-app = { package = "gstreamer-app", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.22", version = "0.22" } gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.22", version = "0.22" } gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.22", version = "0.22" } diff --git a/docs/plugins/gst_plugins_cache.json b/docs/plugins/gst_plugins_cache.json index f997a112..ed48d0b0 100644 --- a/docs/plugins/gst_plugins_cache.json +++ b/docs/plugins/gst_plugins_cache.json @@ -2409,7 +2409,7 @@ "long-name": "GTK 4 Paintable Sink", "pad-templates": { "sink": { - "caps": "video/x-raw:\n format: { BGRA, ARGB, RGBA, ABGR, RGB, BGR }\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\n framerate: [ 0/1, 2147483647/1 ]\n\nvideo/x-raw(memory:GLMemory, meta:GstVideoOverlayComposition):\n format: { RGBA, RGB }\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\n framerate: [ 0/1, 2147483647/1 ]\n texture-target: 2D\n\nvideo/x-raw(memory:GLMemory):\n format: { RGBA, RGB }\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\n framerate: [ 0/1, 2147483647/1 ]\n texture-target: 2D\n\nvideo/x-raw(memory:SystemMemory, meta:GstVideoOverlayComposition):\n format: { BGRA, ARGB, RGBA, ABGR, RGB, BGR }\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\n framerate: [ 0/1, 2147483647/1 ]\n\nvideo/x-raw(meta:GstVideoOverlayComposition):\n format: { BGRA, ARGB, RGBA, ABGR, RGB, BGR }\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\n framerate: [ 0/1, 2147483647/1 ]\n", + "caps": "video/x-raw(memory:GLMemory, meta:GstVideoOverlayComposition):\n format: { RGBA, RGB }\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\n framerate: [ 0/1, 2147483647/1 ]\n texture-target: 2D\n\nvideo/x-raw(memory:GLMemory):\n format: { RGBA, RGB }\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\n framerate: [ 0/1, 2147483647/1 ]\n texture-target: 2D\n\nvideo/x-raw(memory:SystemMemory, meta:GstVideoOverlayComposition):\n format: { BGRA, ARGB, RGBA, ABGR, RGB, BGR }\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\n framerate: [ 0/1, 2147483647/1 ]\n\nvideo/x-raw(meta:GstVideoOverlayComposition):\n format: { BGRA, ARGB, RGBA, ABGR, RGB, BGR }\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\n framerate: [ 0/1, 2147483647/1 ]\nvideo/x-raw:\n format: { BGRA, ARGB, RGBA, ABGR, RGB, BGR }\n width: [ 1, 2147483647 ]\n height: [ 1, 2147483647 ]\n framerate: [ 0/1, 2147483647/1 ]\n", "direction": "sink", "presence": "always" } diff --git a/video/gtk4/Cargo.toml b/video/gtk4/Cargo.toml index fce75fd9..ce011d65 100644 --- a/video/gtk4/Cargo.toml +++ b/video/gtk4/Cargo.toml @@ -17,6 +17,7 @@ gst = { workspace = true, features = ["v1_16"] } gst-base.workspace = true gst-video.workspace = true gst-gl = { workspace = true, features = ["v1_16"], optional = true } +gst-allocators = { workspace = true, features = ["v1_24"], optional = true } gst-gl-wayland = { workspace = true, features = ["v1_16"], optional = true } gst-gl-x11 = { workspace = true, features = ["v1_16"], optional = true } @@ -50,6 +51,7 @@ wayland = ["gtk/v4_6", "gdk-wayland", "gst-gl", "gst-gl-wayland"] x11glx = ["gtk/v4_6", "gdk-x11", "gst-gl", "gst-gl-x11"] x11egl = ["gtk/v4_6", "gdk-x11", "gst-gl", "gst-gl-egl"] winegl = ["gdk-win32/egl", "gst-gl-egl"] +dmabuf = ["gst-allocators", "wayland", "gtk_v4_14", "gst-video/v1_24"] capi = [] doc = ["gst/v1_18"] gtk_v4_10 = ["gtk/v4_10"] diff --git a/video/gtk4/examples/gtksink.rs b/video/gtk4/examples/gtksink.rs index b431b5c5..abda23c6 100644 --- a/video/gtk4/examples/gtksink.rs +++ b/video/gtk4/examples/gtksink.rs @@ -6,13 +6,6 @@ use gtk::{gdk, gio, glib}; use std::cell::RefCell; fn create_ui(app: >k::Application) { - 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")); - let pipeline = gst::Pipeline::new(); let overlay = gst::ElementFactory::make("clockoverlay") @@ -64,8 +57,26 @@ fn create_ui(app: >k::Application) { src.link_filtered(&overlay, &caps).unwrap(); overlay.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(); picture.set_paintable(Some(&paintable)); - vbox.append(&picture); + + #[cfg(feature = "gtk_v4_14")] + { + let offload = gtk::GraphicsOffload::new(Some(&picture)); + offload.set_enabled(gtk::GraphicsOffloadEnabled::Enabled); + vbox.append(&offload); + } + #[cfg(not(feature = "gtk_v4_14"))] + { + vbox.append(&picture); + } + + let label = gtk::Label::new(Some("Position: 00:00:00")); vbox.append(&label); window.set_child(Some(&vbox)); diff --git a/video/gtk4/src/sink/frame.rs b/video/gtk4/src/sink/frame.rs index aaa7e224..f9f1c61b 100644 --- a/video/gtk4/src/sink/frame.rs +++ b/video/gtk4/src/sink/frame.rs @@ -14,7 +14,61 @@ use gst_video::prelude::*; #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] use gst_gl::prelude::*; use gtk::{gdk, glib}; -use std::collections::{HashMap, HashSet}; +use std::{ + collections::{HashMap, HashSet}, + ops, +}; + +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum VideoInfo { + VideoInfo(gst_video::VideoInfo), + #[cfg(all(target_os = "linux", feature = "dmabuf"))] + DmaDrm(gst_video::VideoInfoDmaDrm), +} + +impl From for VideoInfo { + fn from(v: gst_video::VideoInfo) -> Self { + VideoInfo::VideoInfo(v) + } +} + +#[cfg(all(target_os = "linux", feature = "dmabuf"))] +impl From for VideoInfo { + fn from(v: gst_video::VideoInfoDmaDrm) -> Self { + VideoInfo::DmaDrm(v) + } +} + +impl ops::Deref for VideoInfo { + type Target = gst_video::VideoInfo; + + fn deref(&self) -> &Self::Target { + match self { + VideoInfo::VideoInfo(info) => info, + #[cfg(all(target_os = "linux", feature = "dmabuf"))] + VideoInfo::DmaDrm(info) => info, + } + } +} + +impl VideoInfo { + #[cfg(all(target_os = "linux", feature = "dmabuf"))] + fn dma_drm(&self) -> Option<&gst_video::VideoInfoDmaDrm> { + match self { + VideoInfo::VideoInfo(..) => None, + VideoInfo::DmaDrm(info) => Some(info), + } + } +} + +#[derive(Debug, PartialEq, Eq, Hash)] +pub enum TextureCacheId { + Memory(usize), + #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] + GL(usize), + #[cfg(all(target_os = "linux", feature = "dmabuf"))] + DmaBuf([i32; 4]), +} #[derive(Debug)] enum MappedFrame { @@ -24,6 +78,17 @@ enum MappedFrame { frame: gst_gl::GLVideoFrame, wrapped_context: gst_gl::GLContext, }, + #[cfg(all(target_os = "linux", feature = "dmabuf"))] + DmaBuf { + buffer: gst::Buffer, + info: gst_video::VideoInfoDmaDrm, + n_planes: u32, + fds: [i32; 4], + offsets: [usize; 4], + strides: [usize; 4], + width: u32, + height: u32, + }, } impl MappedFrame { @@ -32,6 +97,8 @@ impl MappedFrame { MappedFrame::SysMem(frame) => frame.buffer(), #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] MappedFrame::GL { frame, .. } => frame.buffer(), + #[cfg(all(target_os = "linux", feature = "dmabuf"))] + MappedFrame::DmaBuf { buffer, .. } => buffer, } } @@ -40,6 +107,8 @@ impl MappedFrame { MappedFrame::SysMem(frame) => frame.width(), #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] MappedFrame::GL { frame, .. } => frame.width(), + #[cfg(all(target_os = "linux", feature = "dmabuf"))] + MappedFrame::DmaBuf { info, .. } => info.width(), } } @@ -48,6 +117,8 @@ impl MappedFrame { MappedFrame::SysMem(frame) => frame.height(), #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] MappedFrame::GL { frame, .. } => frame.height(), + #[cfg(all(target_os = "linux", feature = "dmabuf"))] + MappedFrame::DmaBuf { info, .. } => info.height(), } } @@ -56,6 +127,8 @@ impl MappedFrame { MappedFrame::SysMem(frame) => frame.format_info(), #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] MappedFrame::GL { frame, .. } => frame.format_info(), + #[cfg(all(target_os = "linux", feature = "dmabuf"))] + MappedFrame::DmaBuf { info, .. } => info.format_info(), } } } @@ -108,16 +181,16 @@ fn video_format_to_memory_format(f: gst_video::VideoFormat) -> gdk::MemoryFormat fn video_frame_to_memory_texture( frame: gst_video::VideoFrame, - cached_textures: &mut HashMap, - used_textures: &mut HashSet, + cached_textures: &mut HashMap, + used_textures: &mut HashSet, ) -> (gdk::Texture, f64) { - let texture_id = frame.plane_data(0).unwrap().as_ptr() as usize; + let ptr = frame.plane_data(0).unwrap().as_ptr() as usize; let pixel_aspect_ratio = (frame.info().par().numer() as f64) / (frame.info().par().denom() as f64); - if let Some(texture) = cached_textures.get(&texture_id) { - used_textures.insert(texture_id); + if let Some(texture) = cached_textures.get(&TextureCacheId::Memory(ptr)) { + used_textures.insert(TextureCacheId::Memory(ptr)); return (texture.clone(), pixel_aspect_ratio); } @@ -135,8 +208,8 @@ fn video_frame_to_memory_texture( ) .upcast::(); - cached_textures.insert(texture_id, texture.clone()); - used_textures.insert(texture_id); + cached_textures.insert(TextureCacheId::Memory(ptr), texture.clone()); + used_textures.insert(TextureCacheId::Memory(ptr)); (texture, pixel_aspect_ratio) } @@ -144,8 +217,8 @@ fn video_frame_to_memory_texture( #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] fn video_frame_to_gl_texture( frame: gst_gl::GLVideoFrame, - cached_textures: &mut HashMap, - used_textures: &mut HashSet, + cached_textures: &mut HashMap, + used_textures: &mut HashSet, gdk_context: &gdk::GLContext, #[allow(unused)] wrapped_context: &gst_gl::GLContext, ) -> (gdk::Texture, f64) { @@ -154,8 +227,8 @@ fn video_frame_to_gl_texture( let pixel_aspect_ratio = (frame.info().par().numer() as f64) / (frame.info().par().denom() as f64); - if let Some(texture) = cached_textures.get(&(texture_id)) { - used_textures.insert(texture_id); + if let Some(texture) = cached_textures.get(&TextureCacheId::GL(texture_id)) { + used_textures.insert(TextureCacheId::GL(texture_id)); return (texture.clone(), pixel_aspect_ratio); } @@ -237,18 +310,64 @@ fn video_frame_to_gl_texture( .upcast::() }; - cached_textures.insert(texture_id, texture.clone()); - used_textures.insert(texture_id); + cached_textures.insert(TextureCacheId::GL(texture_id), texture.clone()); + used_textures.insert(TextureCacheId::GL(texture_id)); (texture, pixel_aspect_ratio) } +#[cfg(all(target_os = "linux", feature = "dmabuf"))] +#[allow(clippy::too_many_arguments)] +fn video_frame_to_dmabuf_texture( + buffer: gst::Buffer, + cached_textures: &mut HashMap, + used_textures: &mut HashSet, + info: &gst_video::VideoInfoDmaDrm, + n_planes: u32, + fds: &[i32; 4], + offsets: &[usize; 4], + strides: &[usize; 4], + width: u32, + height: u32, +) -> Result<(gdk::Texture, f64), glib::Error> { + let pixel_aspect_ratio = (info.par().numer() as f64) / (info.par().denom() as f64); + + if let Some(texture) = cached_textures.get(&TextureCacheId::DmaBuf(*fds)) { + used_textures.insert(TextureCacheId::DmaBuf(*fds)); + return Ok((texture.clone(), pixel_aspect_ratio)); + } + + let builder = gdk::DmabufTextureBuilder::new(); + builder.set_display(&gdk::Display::default().unwrap()); + builder.set_fourcc(info.fourcc()); + builder.set_modifier(info.modifier()); + builder.set_width(width); + builder.set_height(height); + builder.set_n_planes(n_planes); + for plane in 0..(n_planes as usize) { + builder.set_fd(plane as u32, fds[plane]); + builder.set_offset(plane as u32, offsets[plane] as u32); + builder.set_stride(plane as u32, strides[plane] as u32); + } + + let texture = unsafe { + builder.build_with_release_func(move || { + drop(buffer); + })? + }; + + cached_textures.insert(TextureCacheId::DmaBuf(*fds), texture.clone()); + used_textures.insert(TextureCacheId::DmaBuf(*fds)); + + Ok((texture, pixel_aspect_ratio)) +} + impl Frame { pub(crate) fn into_textures( self, #[allow(unused_variables)] gdk_context: Option<&gdk::GLContext>, - cached_textures: &mut HashMap, - ) -> Vec { + cached_textures: &mut HashMap, + ) -> Result, glib::Error> { let mut textures = Vec::with_capacity(1 + self.overlays.len()); let mut used_textures = HashSet::with_capacity(1 + self.overlays.len()); @@ -278,6 +397,28 @@ impl Frame { &wrapped_context, ) } + #[cfg(all(target_os = "linux", feature = "dmabuf"))] + MappedFrame::DmaBuf { + buffer, + info, + n_planes, + fds, + offsets, + strides, + width, + height, + } => video_frame_to_dmabuf_texture( + buffer, + cached_textures, + &mut used_textures, + &info, + n_planes, + &fds, + &offsets, + &strides, + width, + height, + )?, }; textures.push(Texture { @@ -309,14 +450,14 @@ impl Frame { // Remove textures that were not used this time cached_textures.retain(|id, _| used_textures.contains(id)); - textures + Ok(textures) } } impl Frame { pub(crate) fn new( buffer: &gst::Buffer, - info: &gst_video::VideoInfo, + info: &VideoInfo, #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] wrapped_context: Option< &gst_gl::GLContext, >, @@ -327,77 +468,125 @@ impl Frame { // Empty buffers get filtered out in show_frame debug_assert!(buffer.n_memory() > 0); - let mut frame; + #[allow(unused_mut)] + let mut frame = None; - #[cfg(not(any(target_os = "macos", target_os = "windows", feature = "gst-gl")))] + #[cfg(all(target_os = "linux", feature = "dmabuf"))] { - frame = Self { - frame: MappedFrame::SysMem( + // Check we received a buffer with dmabuf memory and if so do some checks before + // passing it onwards + if frame.is_none() + && buffer + .peek_memory(0) + .is_memory_type::() + { + if let Some((vmeta, info)) = + Option::zip(buffer.meta::(), info.dma_drm()) + { + let mut fds = [-1i32; 4]; + let mut offsets = [0; 4]; + let mut strides = [0; 4]; + let n_planes = vmeta.n_planes() as usize; + + let vmeta_offsets = vmeta.offset(); + let vmeta_strides = vmeta.stride(); + + for plane in 0..n_planes { + let Some((range, skip)) = + buffer.find_memory(vmeta_offsets[plane]..(vmeta_offsets[plane] + 1)) + else { + break; + }; + + let mem = buffer.peek_memory(range.start); + let Some(mem) = mem.downcast_memory_ref::() + else { + break; + }; + + let fd = mem.fd(); + fds[plane] = fd; + offsets[plane] = mem.offset() + skip; + strides[plane] = vmeta_strides[plane] as usize; + } + + // All fds valid? + if fds[0..n_planes].iter().all(|fd| *fd != -1) { + frame = Some(MappedFrame::DmaBuf { + buffer: buffer.clone(), + info: info.clone(), + n_planes: n_planes as u32, + fds, + offsets, + strides, + width: vmeta.width(), + height: vmeta.height(), + }); + } + } + } + #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] + { + if frame.is_none() { + // Check we received a buffer with GL memory and if the context of that memory + // can share with the wrapped context around the GDK GL context. + // + // If not it has to be uploaded to the GPU. + let memory_ctx = buffer + .peek_memory(0) + .downcast_memory_ref::() + .and_then(|m| { + let ctx = m.context(); + if wrapped_context + .map_or(false, |wrapped_context| wrapped_context.can_share(ctx)) + { + Some(ctx) + } else { + None + } + }); + + if let Some(memory_ctx) = memory_ctx { + // If there is no GLSyncMeta yet then we need to add one here now, which requires + // obtaining a writable buffer. + let mapped_frame = if buffer.meta::().is_some() { + gst_gl::GLVideoFrame::from_buffer_readable(buffer.clone(), info) + .map_err(|_| gst::FlowError::Error)? + } else { + let mut buffer = buffer.clone(); + { + let buffer = buffer.make_mut(); + gst_gl::GLSyncMeta::add(buffer, memory_ctx); + } + gst_gl::GLVideoFrame::from_buffer_readable(buffer, info) + .map_err(|_| gst::FlowError::Error)? + }; + + // Now that it's guaranteed that there is a sync meta and the frame is mapped, set + // a sync point so we can ensure that the texture is ready later when making use of + // it as gdk::GLTexture. + let meta = mapped_frame.buffer().meta::().unwrap(); + meta.set_sync_point(memory_ctx); + + frame = Some(MappedFrame::GL { + frame: mapped_frame, + wrapped_context: wrapped_context.unwrap().clone(), + }); + } + } + } + } + + let mut frame = Self { + frame: match frame { + Some(frame) => frame, + None => MappedFrame::SysMem( gst_video::VideoFrame::from_buffer_readable(buffer.clone(), info) .map_err(|_| gst::FlowError::Error)?, ), - overlays: vec![], - }; - } - #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] - { - // Check we received a buffer with GL memory and if the context of that memory - // can share with the wrapped context around the GDK GL context. - // - // If not it has to be uploaded to the GPU. - let memory_ctx = buffer - .peek_memory(0) - .downcast_memory_ref::() - .and_then(|m| { - let ctx = m.context(); - if wrapped_context - .map_or(false, |wrapped_context| wrapped_context.can_share(ctx)) - { - Some(ctx) - } else { - None - } - }); - - if let Some(memory_ctx) = memory_ctx { - // If there is no GLSyncMeta yet then we need to add one here now, which requires - // obtaining a writable buffer. - let mapped_frame = if buffer.meta::().is_some() { - gst_gl::GLVideoFrame::from_buffer_readable(buffer.clone(), info) - .map_err(|_| gst::FlowError::Error)? - } else { - let mut buffer = buffer.clone(); - { - let buffer = buffer.make_mut(); - gst_gl::GLSyncMeta::add(buffer, memory_ctx); - } - gst_gl::GLVideoFrame::from_buffer_readable(buffer, info) - .map_err(|_| gst::FlowError::Error)? - }; - - // Now that it's guaranteed that there is a sync meta and the frame is mapped, set - // a sync point so we can ensure that the texture is ready later when making use of - // it as gdk::GLTexture. - let meta = mapped_frame.buffer().meta::().unwrap(); - meta.set_sync_point(memory_ctx); - - frame = Self { - frame: MappedFrame::GL { - frame: mapped_frame, - wrapped_context: wrapped_context.unwrap().clone(), - }, - overlays: vec![], - }; - } else { - frame = Self { - frame: MappedFrame::SysMem( - gst_video::VideoFrame::from_buffer_readable(buffer.clone(), info) - .map_err(|_| gst::FlowError::Error)?, - ), - overlays: vec![], - }; - } - } + }, + overlays: vec![], + }; frame.overlays = frame .frame diff --git a/video/gtk4/src/sink/imp.rs b/video/gtk4/src/sink/imp.rs index dcad8d42..eff4b2ec 100644 --- a/video/gtk4/src/sink/imp.rs +++ b/video/gtk4/src/sink/imp.rs @@ -1,7 +1,7 @@ // // Copyright (C) 2021 Bilal Elmoussaoui // Copyright (C) 2021 Jordan Petridis -// Copyright (C) 2021 Sebastian Dröge +// Copyright (C) 2021-2024 Sebastian Dröge // // 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 @@ -62,7 +62,7 @@ pub(crate) static CAT: Lazy = Lazy::new(|| { pub struct PaintableSink { paintable: Mutex>>, window: Mutex>>, - info: Mutex>, + info: Mutex>, sender: Mutex>>, pending_frame: Mutex>, cached_caps: Mutex>, @@ -163,53 +163,99 @@ impl ElementImpl for PaintableSink { { let caps = caps.get_mut().unwrap(); + #[cfg(all(target_os = "linux", feature = "dmabuf"))] + { + for features in [ + [ + gst_allocators::CAPS_FEATURE_MEMORY_DMABUF, + gst_video::CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, + ] + .as_slice(), + [gst_allocators::CAPS_FEATURE_MEMORY_DMABUF].as_slice(), + ] { + let c = gst_video::VideoCapsBuilder::new() + .format(gst_video::VideoFormat::DmaDrm) + .features(features.iter().copied()) + .build(); + caps.append(c); + } + } + for features in [ - None, #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] Some(gst::CapsFeatures::new([ - "memory:GLMemory", - "meta:GstVideoOverlayComposition", + gst_gl::CAPS_FEATURE_MEMORY_GL_MEMORY, + gst_video::CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, ])), #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] - Some(gst::CapsFeatures::new(["memory:GLMemory"])), + Some(gst::CapsFeatures::new([ + gst_gl::CAPS_FEATURE_MEMORY_GL_MEMORY, + ])), Some(gst::CapsFeatures::new([ "memory:SystemMemory", - "meta:GstVideoOverlayComposition", + gst_video::CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, ])), - Some(gst::CapsFeatures::new(["meta:GstVideoOverlayComposition"])), + Some(gst::CapsFeatures::new([ + gst_video::CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, + ])), + None, ] { - const GL_FORMATS: &[gst_video::VideoFormat] = - &[gst_video::VideoFormat::Rgba, gst_video::VideoFormat::Rgb]; - const NON_GL_FORMATS: &[gst_video::VideoFormat] = &[ - gst_video::VideoFormat::Bgra, - gst_video::VideoFormat::Argb, - gst_video::VideoFormat::Rgba, - gst_video::VideoFormat::Abgr, - gst_video::VideoFormat::Rgb, - gst_video::VideoFormat::Bgr, - ]; - - let formats = if features - .as_ref() - .is_some_and(|features| features.contains("memory:GLMemory")) + #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] { - GL_FORMATS - } else { - NON_GL_FORMATS - }; + const GL_FORMATS: &[gst_video::VideoFormat] = + &[gst_video::VideoFormat::Rgba, gst_video::VideoFormat::Rgb]; + const NON_GL_FORMATS: &[gst_video::VideoFormat] = &[ + gst_video::VideoFormat::Bgra, + gst_video::VideoFormat::Argb, + gst_video::VideoFormat::Rgba, + gst_video::VideoFormat::Abgr, + gst_video::VideoFormat::Rgb, + gst_video::VideoFormat::Bgr, + ]; - let mut c = gst_video::video_make_raw_caps(formats).build(); + let formats = if features.as_ref().is_some_and(|features| { + features.contains(gst_gl::CAPS_FEATURE_MEMORY_GL_MEMORY) + }) { + GL_FORMATS + } else { + NON_GL_FORMATS + }; - if let Some(features) = features { - let c = c.get_mut().unwrap(); + let mut c = gst_video::video_make_raw_caps(formats).build(); - if features.contains("memory:GLMemory") { - c.set("texture-target", "2D") + if let Some(features) = features { + let c = c.get_mut().unwrap(); + + if features.contains(gst_gl::CAPS_FEATURE_MEMORY_GL_MEMORY) { + c.set("texture-target", "2D") + } + c.set_features_simple(Some(features)); } - c.set_features_simple(Some(features)); + caps.append(c); } + #[cfg(not(any( + target_os = "macos", + target_os = "windows", + feature = "gst-gl" + )))] + { + const FORMATS: &[gst_video::VideoFormat] = &[ + gst_video::VideoFormat::Bgra, + gst_video::VideoFormat::Argb, + gst_video::VideoFormat::Rgba, + gst_video::VideoFormat::Abgr, + gst_video::VideoFormat::Rgb, + gst_video::VideoFormat::Bgr, + ]; - caps.append(c); + let mut c = gst_video::video_make_raw_caps(FORMATS).build(); + + if let Some(features) = features { + let c = c.get_mut().unwrap(); + c.set_features_simple(Some(features)); + } + caps.append(c); + } } } @@ -361,8 +407,21 @@ impl BaseSinkImpl for PaintableSink { fn set_caps(&self, caps: &gst::Caps) -> Result<(), gst::LoggableError> { gst::debug!(CAT, imp: self, "Setting caps {caps:?}"); - let video_info = gst_video::VideoInfo::from_caps(caps) - .map_err(|_| gst::loggable_error!(CAT, "Invalid caps"))?; + #[allow(unused_mut)] + let mut video_info = None; + #[cfg(all(target_os = "linux", feature = "dmabuf"))] + { + if let Ok(info) = gst_video::VideoInfoDmaDrm::from_caps(caps) { + video_info = Some(info.into()); + } + } + + let video_info = match video_info { + Some(info) => info, + None => gst_video::VideoInfo::from_caps(caps) + .map_err(|_| gst::loggable_error!(CAT, "Invalid caps"))? + .into(), + }; self.info.lock().unwrap().replace(video_info); @@ -516,10 +575,11 @@ impl PaintableSink { match action { SinkEvent::FrameChanged => { + let Some(frame) = self.pending_frame() else { + return glib::ControlFlow::Continue; + }; gst::trace!(CAT, imp: self, "Frame changed"); - paintable - .get_ref() - .handle_frame_changed(self.pending_frame()) + paintable.get_ref().handle_frame_changed(&self.obj(), frame); } } @@ -530,13 +590,59 @@ impl PaintableSink { #[allow(unused_mut)] let mut tmp_caps = Self::pad_templates()[0].caps().clone(); + #[cfg(all(target_os = "linux", feature = "dmabuf"))] + { + let formats = utils::invoke_on_main_thread(move || { + let Some(display) = gdk::Display::default() else { + return vec![]; + }; + let dmabuf_formats = display.dmabuf_formats(); + + let mut formats = vec![]; + let n_formats = dmabuf_formats.n_formats(); + for i in 0..n_formats { + let (fourcc, modifier) = dmabuf_formats.format(i); + + if fourcc == 0 || modifier == (u64::MAX >> 8) { + continue; + } + + formats.push(gst_video::dma_drm_fourcc_to_string(fourcc, modifier)); + } + + formats + }); + + if formats.is_empty() { + // Filter out dmabufs caps from the template pads if we have no supported formats + if !matches!(&*GL_CONTEXT.lock().unwrap(), GLContext::Initialized { .. }) { + tmp_caps = tmp_caps + .iter_with_features() + .filter(|(_, features)| { + !features.contains(gst_allocators::CAPS_FEATURE_MEMORY_DMABUF) + }) + .map(|(s, c)| (s.to_owned(), c.to_owned())) + .collect::(); + } + } else { + let tmp_caps = tmp_caps.make_mut(); + for (s, f) in tmp_caps.iter_with_features_mut() { + if f.contains(gst_allocators::CAPS_FEATURE_MEMORY_DMABUF) { + s.set("drm-format", gst::List::new(&formats)); + } + } + } + } + #[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))] { // Filter out GL caps from the template pads if we have no context if !matches!(&*GL_CONTEXT.lock().unwrap(), GLContext::Initialized { .. }) { tmp_caps = tmp_caps .iter_with_features() - .filter(|(_, features)| !features.contains("memory:GLMemory")) + .filter(|(_, features)| { + !features.contains(gst_gl::CAPS_FEATURE_MEMORY_GL_MEMORY) + }) .map(|(s, c)| (s.to_owned(), c.to_owned())) .collect::(); } @@ -564,7 +670,17 @@ impl PaintableSink { let window = gtk::Window::new(); let picture = gtk::Picture::new(); picture.set_paintable(Some(&paintable)); - window.set_child(Some(&picture)); + + #[cfg(feature = "gtk_v4_14")] + { + let offload = gtk::GraphicsOffload::new(Some(&picture)); + offload.set_enabled(gtk::GraphicsOffloadEnabled::Enabled); + window.set_child(Some(&offload)); + } + #[cfg(not(feature = "gtk_v4_14"))] + { + window.set_child(Some(&picture)); + } window.set_default_size(640, 480); window.connect_close_request({ diff --git a/video/gtk4/src/sink/paintable/imp.rs b/video/gtk4/src/sink/paintable/imp.rs index eccff6b8..f71cc9ec 100644 --- a/video/gtk4/src/sink/paintable/imp.rs +++ b/video/gtk4/src/sink/paintable/imp.rs @@ -31,7 +31,7 @@ static CAT: Lazy = Lazy::new(|| { #[derive(Debug)] pub struct Paintable { paintables: RefCell>, - cached_textures: RefCell>, + cached_textures: RefCell>, gl_context: RefCell>, background_color: Cell, #[cfg(feature = "gtk_v4_10")] @@ -197,12 +197,14 @@ impl PaintableImpl for Paintable { ((frame_height as f64 * scale_y) - (frame_height as f64 * scale_x)) / 2.0; scale_y = scale_x; } - } - snapshot.append_color( - &background_color, - &graphene::Rect::new(0f32, 0f32, width as f32, height as f32), - ); + if !background_color.is_clear() { + snapshot.append_color( + &background_color, + &graphene::Rect::new(0f32, 0f32, width as f32, height as f32), + ); + } + } snapshot.translate(&graphene::Point::new(trans_x as f32, trans_y as f32)); @@ -331,34 +333,44 @@ impl PaintableImpl for Paintable { } impl Paintable { - pub(super) fn handle_frame_changed(&self, frame: Option) { + pub(super) fn handle_frame_changed(&self, sink: &crate::PaintableSink, frame: Frame) { let context = self.gl_context.borrow(); - if let Some(frame) = frame { - gst::trace!(CAT, imp: self, "Received new frame"); - let new_paintables = - frame.into_textures(context.as_ref(), &mut self.cached_textures.borrow_mut()); - let new_size = new_paintables - .first() - .map(|p| (f32::round(p.width) as u32, f32::round(p.height) as u32)) - .unwrap(); + gst::trace!(CAT, imp: self, "Received new frame"); - let old_paintables = self.paintables.replace(new_paintables); - let old_size = old_paintables - .first() - .map(|p| (f32::round(p.width) as u32, f32::round(p.height) as u32)); + let new_paintables = + match frame.into_textures(context.as_ref(), &mut self.cached_textures.borrow_mut()) { + Ok(textures) => textures, + Err(err) => { + gst::element_error!( + sink, + gst::ResourceError::Failed, + ["Failed to transform frame into textures: {err}"] + ); + return; + } + }; - if Some(new_size) != old_size { - gst::debug!( - CAT, - imp: self, - "Size changed from {old_size:?} to {new_size:?}", - ); - self.obj().invalidate_size(); - } + let new_size = new_paintables + .first() + .map(|p| (f32::round(p.width) as u32, f32::round(p.height) as u32)) + .unwrap(); - self.obj().invalidate_contents(); + let old_paintables = self.paintables.replace(new_paintables); + let old_size = old_paintables + .first() + .map(|p| (f32::round(p.width) as u32, f32::round(p.height) as u32)); + + if Some(new_size) != old_size { + gst::debug!( + CAT, + imp: self, + "Size changed from {old_size:?} to {new_size:?}", + ); + self.obj().invalidate_size(); } + + self.obj().invalidate_contents(); } pub(super) fn handle_flush_frames(&self) { diff --git a/video/gtk4/src/sink/paintable/mod.rs b/video/gtk4/src/sink/paintable/mod.rs index 835c43de..63e42b3d 100644 --- a/video/gtk4/src/sink/paintable/mod.rs +++ b/video/gtk4/src/sink/paintable/mod.rs @@ -30,8 +30,8 @@ impl Paintable { } impl Paintable { - pub(crate) fn handle_frame_changed(&self, frame: Option) { - self.imp().handle_frame_changed(frame); + pub(crate) fn handle_frame_changed(&self, sink: &crate::PaintableSink, frame: Frame) { + self.imp().handle_frame_changed(sink, frame); } pub(crate) fn handle_flush_frames(&self) { From 3deae304429956ec6087df3f23c8e8db06fb3417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 24 Apr 2024 15:22:57 +0200 Subject: [PATCH 09/16] gtk4paintablesink: Implement child proxy interface This allows setting properties on the paintable from gst-launch-1.0. Part-of: --- docs/plugins/gst_plugins_cache.json | 3 + video/gtk4/src/sink/imp.rs | 89 ++++++++++++++++++++++++----- video/gtk4/src/sink/mod.rs | 3 +- 3 files changed, 80 insertions(+), 15 deletions(-) diff --git a/docs/plugins/gst_plugins_cache.json b/docs/plugins/gst_plugins_cache.json index ed48d0b0..9799df11 100644 --- a/docs/plugins/gst_plugins_cache.json +++ b/docs/plugins/gst_plugins_cache.json @@ -2405,6 +2405,9 @@ "GInitiallyUnowned", "GObject" ], + "interfaces": [ + "GstChildProxy" + ], "klass": "Sink/Video", "long-name": "GTK 4 Paintable Sink", "pad-templates": { diff --git a/video/gtk4/src/sink/imp.rs b/video/gtk4/src/sink/imp.rs index eff4b2ec..8b98c4cb 100644 --- a/video/gtk4/src/sink/imp.rs +++ b/video/gtk4/src/sink/imp.rs @@ -82,6 +82,7 @@ impl ObjectSubclass for PaintableSink { const NAME: &'static str = "GstGtk4PaintableSink"; type Type = super::PaintableSink; type ParentType = gst_video::VideoSink; + type Interfaces = (gst::ChildProxy,); } impl ObjectImpl for PaintableSink { @@ -110,12 +111,14 @@ impl ObjectImpl for PaintableSink { return None::<&gdk::Paintable>.to_value(); } - let mut paintable = self.paintable.lock().unwrap(); - if paintable.is_none() { - self.create_paintable(&mut paintable); + let mut paintable_guard = self.paintable.lock().unwrap(); + let mut created = false; + if paintable_guard.is_none() { + created = true; + self.create_paintable(&mut paintable_guard); } - let paintable = match &*paintable { + let paintable = match &*paintable_guard { Some(ref paintable) => paintable, None => { gst::error!(CAT, imp: self, "Failed to create paintable"); @@ -124,16 +127,31 @@ impl ObjectImpl for PaintableSink { }; // Getter must be called from the main thread - if paintable.is_owner() { - paintable.get_ref().to_value() - } else { + if !paintable.is_owner() { gst::error!( CAT, imp: self, "Can't retrieve Paintable from non-main thread" ); - None::<&gdk::Paintable>.to_value() + return None::<&gdk::Paintable>.to_value(); } + + let paintable = paintable.get_ref().clone(); + drop(paintable_guard); + + if created { + let self_ = self.to_owned(); + glib::MainContext::default().invoke(move || { + let paintable_guard = self_.paintable.lock().unwrap(); + if let Some(paintable) = &*paintable_guard { + let paintable_clone = paintable.get_ref().clone(); + drop(paintable_guard); + self_.obj().child_added(&paintable_clone, "paintable"); + } + }); + } + + paintable.to_value() } _ => unimplemented!(), } @@ -290,18 +308,31 @@ impl ElementImpl for PaintableSink { } } - let mut paintable = self.paintable.lock().unwrap(); - - if paintable.is_none() { - self.create_paintable(&mut paintable); + let mut paintable_guard = self.paintable.lock().unwrap(); + let mut created = false; + if paintable_guard.is_none() { + created = true; + self.create_paintable(&mut paintable_guard); } - if paintable.is_none() { + if paintable_guard.is_none() { gst::error!(CAT, imp: self, "Failed to create paintable"); return Err(gst::StateChangeError); } - drop(paintable); + drop(paintable_guard); + + if created { + let self_ = self.to_owned(); + glib::MainContext::default().invoke(move || { + let paintable_guard = self_.paintable.lock().unwrap(); + if let Some(paintable) = &*paintable_guard { + let paintable_clone = paintable.get_ref().clone(); + drop(paintable_guard); + self_.obj().child_added(&paintable_clone, "paintable"); + } + }); + } // 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 @@ -1189,3 +1220,33 @@ impl PaintableSink { } } } + +impl ChildProxyImpl for PaintableSink { + fn child_by_index(&self, index: u32) -> Option { + if index != 0 { + return None; + } + + let paintable = self.paintable.lock().unwrap(); + paintable + .as_ref() + .filter(|p| p.is_owner()) + .map(|p| p.get_ref().upcast_ref::().clone()) + } + + fn child_by_name(&self, name: &str) -> Option { + if name == "paintable" { + return self.child_by_index(0); + } + None + } + + fn children_count(&self) -> u32 { + let paintable = self.paintable.lock().unwrap(); + if paintable.is_some() { + 1 + } else { + 0 + } + } +} diff --git a/video/gtk4/src/sink/mod.rs b/video/gtk4/src/sink/mod.rs index 96ff9299..5008ed00 100644 --- a/video/gtk4/src/sink/mod.rs +++ b/video/gtk4/src/sink/mod.rs @@ -22,7 +22,8 @@ enum SinkEvent { glib::wrapper! { pub struct PaintableSink(ObjectSubclass) - @extends gst_video::VideoSink, gst_base::BaseSink, gst::Element, gst::Object; + @extends gst_video::VideoSink, gst_base::BaseSink, gst::Element, gst::Object, + @implements gst::ChildProxy; } impl PaintableSink { From ced8040701f07ed605ba5e25a2da7c1667960498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 24 Apr 2024 15:42:24 +0200 Subject: [PATCH 10/16] gtk4paintablesink: Add force-aspect-ratio property like in other video sinks Unlike in other sinks this defaults to false as generally every user of GDK paintables already ensures that the aspect ratio is kept and the paintable is layed out in the most optimal way based on the context. Part-of: --- video/gtk4/src/sink/paintable/imp.rs | 44 ++++++++++++++++++---------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/video/gtk4/src/sink/paintable/imp.rs b/video/gtk4/src/sink/paintable/imp.rs index f71cc9ec..b520fc02 100644 --- a/video/gtk4/src/sink/paintable/imp.rs +++ b/video/gtk4/src/sink/paintable/imp.rs @@ -37,6 +37,7 @@ pub struct Paintable { #[cfg(feature = "gtk_v4_10")] scaling_filter: Cell, use_scaling_filter: Cell, + force_aspect_ratio: Cell, #[cfg(not(feature = "gtk_v4_10"))] premult_shader: gsk::GLShader, } @@ -51,6 +52,7 @@ impl Default for Paintable { #[cfg(feature = "gtk_v4_10")] scaling_filter: Cell::new(gsk::ScalingFilter::Linear), use_scaling_filter: Cell::new(false), + force_aspect_ratio: Cell::new(false), #[cfg(not(feature = "gtk_v4_10"))] premult_shader: gsk::GLShader::from_bytes(&glib::Bytes::from_static(include_bytes!( "premult.glsl" @@ -94,6 +96,11 @@ impl ObjectImpl for Paintable { .blurb("Use selected scaling filter or GTK default for rendering") .default_value(false) .build(), + glib::ParamSpecBoolean::builder("force-aspect-ratio") + .nick("Force Aspect Ratio") + .blurb("When enabled, scaling will respect original aspect ratio") + .default_value(true) + .build(), ] }); @@ -117,6 +124,7 @@ impl ObjectImpl for Paintable { "scaling-filter" => self.scaling_filter.get().to_value(), #[cfg(feature = "gtk_v4_10")] "use-scaling-filter" => self.use_scaling_filter.get().to_value(), + "force-aspect-ratio" => self.force_aspect_ratio.get().to_value(), _ => unimplemented!(), } } @@ -139,6 +147,7 @@ impl ObjectImpl for Paintable { "scaling-filter" => self.scaling_filter.set(value.get().unwrap()), #[cfg(feature = "gtk_v4_10")] "use-scaling-filter" => self.use_scaling_filter.set(value.get().unwrap()), + "force-aspect-ratio" => self.force_aspect_ratio.set(value.get().unwrap()), _ => unimplemented!(), } } @@ -173,6 +182,7 @@ impl PaintableImpl for Paintable { let snapshot = snapshot.downcast_ref::().unwrap(); let background_color = self.background_color.get(); + let force_aspect_ratio = self.force_aspect_ratio.get(); let paintables = self.paintables.borrow(); if !paintables.is_empty() { @@ -186,23 +196,25 @@ impl PaintableImpl for Paintable { let mut trans_x = 0.0; let mut trans_y = 0.0; - // TODO: Property for keeping aspect ratio or not - if (scale_x - scale_y).abs() > f64::EPSILON { - if scale_x > scale_y { - trans_x = - ((frame_width as f64 * scale_x) - (frame_width as f64 * scale_y)) / 2.0; - scale_x = scale_y; - } else { - trans_y = - ((frame_height as f64 * scale_y) - (frame_height as f64 * scale_x)) / 2.0; - scale_y = scale_x; - } + if force_aspect_ratio { + if (scale_x - scale_y).abs() > f64::EPSILON { + if scale_x > scale_y { + trans_x = + ((frame_width as f64 * scale_x) - (frame_width as f64 * scale_y)) / 2.0; + scale_x = scale_y; + } else { + trans_y = ((frame_height as f64 * scale_y) + - (frame_height as f64 * scale_x)) + / 2.0; + scale_y = scale_x; + } - if !background_color.is_clear() { - snapshot.append_color( - &background_color, - &graphene::Rect::new(0f32, 0f32, width as f32, height as f32), - ); + if !background_color.is_clear() { + snapshot.append_color( + &background_color, + &graphene::Rect::new(0f32, 0f32, width as f32, height as f32), + ); + } } } From b225020bfb7cee841456acbea2fc536172d68565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 24 Apr 2024 15:48:53 +0200 Subject: [PATCH 11/16] gtk4paintablesink: Improve scaling logic If force-aspect-ratio=false then make sure to fully fill the given width/height with the video frame and avoid rounding errors. This makes sure that the video is rendered in the exact position selected by the caller and that graphics offloading is going to work more likely. In other cases and for all overlays, make sure that the calculated positions are staying inside (0, 0, width, height) as rendering outside is not allowed by GTK. Part-of: --- video/gtk4/src/sink/paintable/imp.rs | 269 ++++++++++++++------------- 1 file changed, 144 insertions(+), 125 deletions(-) diff --git a/video/gtk4/src/sink/paintable/imp.rs b/video/gtk4/src/sink/paintable/imp.rs index b520fc02..dd9b6749 100644 --- a/video/gtk4/src/sink/paintable/imp.rs +++ b/video/gtk4/src/sink/paintable/imp.rs @@ -185,42 +185,63 @@ impl PaintableImpl for Paintable { let force_aspect_ratio = self.force_aspect_ratio.get(); let paintables = self.paintables.borrow(); - if !paintables.is_empty() { - gst::trace!(CAT, imp: self, "Snapshotting frame"); + let Some(first_paintable) = paintables.first() else { + gst::trace!(CAT, imp: self, "Snapshotting black frame"); + snapshot.append_color( + &background_color, + &graphene::Rect::new(0f32, 0f32, width as f32, height as f32), + ); - let (frame_width, frame_height) = - paintables.first().map(|p| (p.width, p.height)).unwrap(); + return; + }; - let mut scale_x = width / frame_width as f64; - let mut scale_y = height / frame_height as f64; + gst::trace!(CAT, imp: self, "Snapshotting frame"); + + // The first paintable is the actual video frame and defines the overall size. + // + // Based on its size relative to the snapshot width/height, all other paintables are + // scaled accordingly. + let (frame_width, frame_height) = (first_paintable.width, first_paintable.height); + + let mut scale_x = width / frame_width as f64; + let mut scale_y = height / frame_height as f64; + + // Usually the caller makes sure that the aspect ratio is preserved. To enforce this here + // optionally, we scale the frame equally in both directions and center it. In addition the + // background color is drawn behind the frame to fill the gaps. + // + // This is not done by default for performance reasons and usually would draw a <1px + // background. + if force_aspect_ratio { let mut trans_x = 0.0; let mut trans_y = 0.0; - if force_aspect_ratio { - if (scale_x - scale_y).abs() > f64::EPSILON { - if scale_x > scale_y { - trans_x = - ((frame_width as f64 * scale_x) - (frame_width as f64 * scale_y)) / 2.0; - scale_x = scale_y; - } else { - trans_y = ((frame_height as f64 * scale_y) - - (frame_height as f64 * scale_x)) - / 2.0; - scale_y = scale_x; - } - - if !background_color.is_clear() { - snapshot.append_color( - &background_color, - &graphene::Rect::new(0f32, 0f32, width as f32, height as f32), - ); - } + if (scale_x - scale_y).abs() > f64::EPSILON { + if scale_x > scale_y { + trans_x = (width - (frame_width as f64 * scale_y)) / 2.0; + scale_x = scale_y; + } else { + trans_y = (height - (frame_height as f64 * scale_x)) / 2.0; + scale_y = scale_x; } } + if !background_color.is_clear() && (trans_x > f64::EPSILON || trans_y > f64::EPSILON) { + snapshot.append_color( + &background_color, + &graphene::Rect::new(0f32, 0f32, width as f32, height as f32), + ); + } snapshot.translate(&graphene::Point::new(trans_x as f32, trans_y as f32)); + } - for Texture { + // Make immutable + let scale_x = scale_x; + let scale_y = scale_y; + + for ( + idx, + Texture { texture, x, y, @@ -228,118 +249,116 @@ impl PaintableImpl for Paintable { height: paintable_height, global_alpha, has_alpha, - } in &*paintables + }, + ) in paintables.iter().enumerate() + { + snapshot.push_opacity(*global_alpha as f64); + + let bounds = if !force_aspect_ratio && idx == 0 { + // While this should end up with width again, be explicit in this case to avoid + // rounding errors and fill the whole area with the video frame. + graphene::Rect::new(0.0, 0.0, width as f32, height as f32) + } else { + // Scale texture position and size with the same scale factor as the main video + // frame, and make sure to not render outside (0, 0, width, height). + let x = f32::clamp(*x * scale_x as f32, 0.0, width as f32); + let y = f32::clamp(*y * scale_y as f32, 0.0, height as f32); + let texture_width = f32::min(*paintable_width * scale_x as f32, width as f32); + let texture_height = f32::min(*paintable_height * scale_y as f32, height as f32); + graphene::Rect::new(x, y, texture_width, texture_height) + }; + + // Only premultiply GL textures that expect to be in premultiplied RGBA format. + // + // For GTK 4.14 or newer we use the correct format directly when building the + // texture, but only if a GLES3+ context is used. In that case the NGL renderer is + // used by GTK, which supports non-premultiplied formats correctly and fast. + // + // For GTK 4.10-4.12, or 4.14 and newer if a GLES2 context is used, we use a + // self-mask to pre-multiply the alpha. + // + // For GTK before 4.10, we use a GL shader and hope that it works. + #[cfg(feature = "gtk_v4_10")] { - snapshot.push_opacity(*global_alpha as f64); - - let texture_width = *paintable_width * scale_x as f32; - let texture_height = *paintable_height * scale_y as f32; - let x = *x * scale_x as f32; - let y = *y * scale_y as f32; - let bounds = graphene::Rect::new(x, y, texture_width, texture_height); - - // Only premultiply GL textures that expect to be in premultiplied RGBA format. - // - // For GTK 4.14 or newer we use the correct format directly when building the - // texture, but only if a GLES3+ context is used. In that case the NGL renderer is - // used by GTK, which supports non-premultiplied formats correctly and fast. - // - // For GTK 4.10-4.12, or 4.14 and newer if a GLES2 context is used, we use a - // self-mask to pre-multiply the alpha. - // - // For GTK before 4.10, we use a GL shader and hope that it works. - #[cfg(feature = "gtk_v4_10")] - { - let context_requires_premult = { - #[cfg(feature = "gtk_v4_14")] - { - self.gl_context.borrow().as_ref().map_or(false, |context| { - context.api() != gdk::GLAPI::GLES || context.version().0 < 3 - }) - } - - #[cfg(not(feature = "gtk_v4_14"))] - { - true - } - }; - - let do_premult = - context_requires_premult && texture.is::() && *has_alpha; - if do_premult { - snapshot.push_mask(gsk::MaskMode::Alpha); - if self.use_scaling_filter.get() { - #[cfg(feature = "gtk_v4_10")] - snapshot.append_scaled_texture( - texture, - self.scaling_filter.get(), - &bounds, - ); - } else { - snapshot.append_texture(texture, &bounds); - } - snapshot.pop(); // pop mask - - // color matrix to set alpha of the source to 1.0 as it was - // already applied via the mask just above. - snapshot.push_color_matrix( - &graphene::Matrix::from_float({ - [ - 1.0, 0.0, 0.0, 0.0, // - 0.0, 1.0, 0.0, 0.0, // - 0.0, 0.0, 1.0, 0.0, // - 0.0, 0.0, 0.0, 0.0, - ] - }), - &graphene::Vec4::new(0.0, 0.0, 0.0, 1.0), - ); + let context_requires_premult = { + #[cfg(feature = "gtk_v4_14")] + { + self.gl_context.borrow().as_ref().map_or(false, |context| { + context.api() != gdk::GLAPI::GLES || context.version().0 < 3 + }) } + #[cfg(not(feature = "gtk_v4_14"))] + { + true + } + }; + + let do_premult = + context_requires_premult && texture.is::() && *has_alpha; + if do_premult { + snapshot.push_mask(gsk::MaskMode::Alpha); if self.use_scaling_filter.get() { #[cfg(feature = "gtk_v4_10")] snapshot.append_scaled_texture(texture, self.scaling_filter.get(), &bounds); } else { snapshot.append_texture(texture, &bounds); } + snapshot.pop(); // pop mask - if do_premult { - snapshot.pop(); // pop color matrix - snapshot.pop(); // pop mask 2 - } - } - #[cfg(not(feature = "gtk_v4_10"))] - { - let do_premult = - texture.is::() && *has_alpha && gtk::micro_version() < 13; - if do_premult { - snapshot.push_gl_shader( - &self.premult_shader, - &bounds, - gsk::ShaderArgsBuilder::new(&self.premult_shader, None).to_args(), - ); - } - - if self.use_scaling_filter.get() { - #[cfg(feature = "gtk_v4_10")] - snapshot.append_scaled_texture(texture, self.scaling_filter.get(), &bounds); - } else { - snapshot.append_texture(texture, &bounds); - } - - if do_premult { - snapshot.gl_shader_pop_texture(); // pop texture appended above from the shader - snapshot.pop(); // pop shader - } + // color matrix to set alpha of the source to 1.0 as it was + // already applied via the mask just above. + snapshot.push_color_matrix( + &graphene::Matrix::from_float({ + [ + 1.0, 0.0, 0.0, 0.0, // + 0.0, 1.0, 0.0, 0.0, // + 0.0, 0.0, 1.0, 0.0, // + 0.0, 0.0, 0.0, 0.0, + ] + }), + &graphene::Vec4::new(0.0, 0.0, 0.0, 1.0), + ); } - snapshot.pop(); // pop opacity + if self.use_scaling_filter.get() { + #[cfg(feature = "gtk_v4_10")] + snapshot.append_scaled_texture(texture, self.scaling_filter.get(), &bounds); + } else { + snapshot.append_texture(texture, &bounds); + } + + if do_premult { + snapshot.pop(); // pop color matrix + snapshot.pop(); // pop mask 2 + } } - } else { - gst::trace!(CAT, imp: self, "Snapshotting black frame"); - snapshot.append_color( - &background_color, - &graphene::Rect::new(0f32, 0f32, width as f32, height as f32), - ); + #[cfg(not(feature = "gtk_v4_10"))] + { + let do_premult = + texture.is::() && *has_alpha && gtk::micro_version() < 13; + if do_premult { + snapshot.push_gl_shader( + &self.premult_shader, + &bounds, + gsk::ShaderArgsBuilder::new(&self.premult_shader, None).to_args(), + ); + } + + if self.use_scaling_filter.get() { + #[cfg(feature = "gtk_v4_10")] + snapshot.append_scaled_texture(texture, self.scaling_filter.get(), &bounds); + } else { + snapshot.append_texture(texture, &bounds); + } + + if do_premult { + snapshot.gl_shader_pop_texture(); // pop texture appended above from the shader + snapshot.pop(); // pop shader + } + } + + snapshot.pop(); // pop opacity } } } From 0f5002ca643a422a5da9423378a4ed79bddbb998 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 24 Apr 2024 15:59:11 +0200 Subject: [PATCH 12/16] gtk4paintablesink: meson: Add auto-detection of GTK4 versions and dmabuf feature Part-of: --- meson.build | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/meson.build b/meson.build index 46a3f8aa..bbf4428b 100644 --- a/meson.build +++ b/meson.build @@ -301,6 +301,23 @@ if get_option('gtk4').allowed() gtk4_features += 'winegl' endif endif + + gst_allocators_dep = dependency('gstreamer-allocators-1.0', version: '>=1.24', required: false) + gtk_dep = dependency('gtk4', version: '>=4.6', required: get_option('gtk4')) + if gtk_dep.found() + if host_system == 'linux' and gtk_dep.version().version_compare('>=4.14') and gst_allocators_dep.found() + gtk4_features += 'dmabuf' + endif + + if gtk_dep.version().version_compare('>=4.14') + gtk4_features += 'gtk_v4_14' + elif gtk_dep.version().version_compare('>=4.12') + gtk4_features += 'gtk_v4_12' + elif gtk_dep.version().version_compare('>=4.10') + gtk4_features += 'gtk_v4_10' + endif + endif + plugins += { 'gtk4': { 'library': 'libgstgtk4', From 081b249c7555e466baeb676cebc4b6fa8368f408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Thu, 25 Apr 2024 13:51:40 +0300 Subject: [PATCH 13/16] gtk4paintablesink: Update README.md with all the new features Part-of: --- video/gtk4/README.md | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/video/gtk4/README.md b/video/gtk4/README.md index 4955d6a9..aaf66f33 100644 --- a/video/gtk4/README.md +++ b/video/gtk4/README.md @@ -1,10 +1,20 @@ -# Gtk 4 Sink & Paintable +# 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. -The Sink can generate GL Textures if the system is capable of it, but it needs to be compiled -with either `wayland`, `x11glx` or `x11egl` cargo features. +The sink can generate GL Textures if the system is capable of it, but it needs +to be compiled with either `wayland`, `x11glx` or `x11egl` cargo features. On +Windows and macOS this is enabled by default. + +Additionally, the sink can render DMABufs directly on Linux if GTK 4.14 or +newer is used. For this the `dmabuf` feature needs to be enabled. + +Depending on the GTK version that is used and should be supported as minimum, +new features or more efficient processing can be opted in with the `gtk_v4_10`, +`gtk_v4_12` and `gtk_v4_14` features. The minimum GTK version required by the +sink is GTK 4.4 on Linux without GL support, and 4.6 on Windows and macOS, and +on Linux with GL support. # Flatpak Integration @@ -44,7 +54,7 @@ To build and include the plugin in a Flatpak manifest, you can add the following { "type": "git", "url": "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs", - "branch": "0.10" + "branch": "0.12" } ], "build-options": { From a49581222afb0a1bb2c18ea98353c2798476f3d4 Mon Sep 17 00:00:00 2001 From: Maksym Khomenko Date: Sun, 21 Apr 2024 16:45:08 +0300 Subject: [PATCH 14/16] hrtfrender: use bitmask, not int, to prevent a capsnego failure Part-of: --- audio/audiofx/src/hrtfrender/imp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/audio/audiofx/src/hrtfrender/imp.rs b/audio/audiofx/src/hrtfrender/imp.rs index 06d49689..5ca5253e 100644 --- a/audio/audiofx/src/hrtfrender/imp.rs +++ b/audio/audiofx/src/hrtfrender/imp.rs @@ -649,7 +649,7 @@ impl BaseTransformImpl for HrtfRender { if direction == gst::PadDirection::Sink { s.set("channels", 2); - s.set("channel-mask", 0x3); + s.set("channel-mask", gst::Bitmask(0x3)); } else { let settings = self.settings.lock().unwrap(); if let Some(objs) = &settings.spatial_objects { From 45c04447cbfa61c5e7c3e9e299277c53a42c7158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 29 Apr 2024 11:54:05 +0300 Subject: [PATCH 15/16] Update Cargo.lock Part-of: --- Cargo.lock | 589 +++++++++++++++++++++++++++-------------------------- 1 file changed, 298 insertions(+), 291 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1f224e82..d00eaf0b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,9 +90,9 @@ checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "android-tzdata" @@ -159,9 +159,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] name = "arbitrary" @@ -177,7 +177,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -188,9 +188,9 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "async-channel" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" +checksum = "136d4d23bcc79e27423727b36823d86233aad06dfea531837b038394d11e9928" dependencies = [ "concurrent-queue", "event-listener", @@ -201,9 +201,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07dbbf24db18d609b1462965249abdf49129ccad073ec257da372adc83259c60" +checksum = "4e9eabd7a98fe442131a17c316bd9349c43695e49e730c3c8e12cfb5f4da2693" dependencies = [ "flate2", "futures-core", @@ -214,13 +214,13 @@ dependencies = [ [[package]] name = "async-recursion" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -242,24 +242,24 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] name = "async-task" -version = "4.7.0" +version = "4.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.79" +version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -914,15 +914,15 @@ checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7" [[package]] name = "built" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d17f4d6e4dc36d1a02fbedc2753a096848e7c1b0772f7654eab8e2c927dd53" +checksum = "41bfbdb21256b87a8b5e80fab81a8eed158178e812fd7ba451907518b2742f16" [[package]] name = "bumpalo" -version = "3.15.4" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byte-slice-cast" @@ -960,8 +960,8 @@ dependencies = [ [[package]] name = "cairo-rs" -version = "0.19.3" -source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#5e944f2eff8b344591666754f7ea3b359943ee8c" +version = "0.19.5" +source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#a7c5a9b6850f864cb3d30f679a827492b86247f1" dependencies = [ "bitflags 2.5.0", "cairo-sys-rs", @@ -972,8 +972,8 @@ dependencies = [ [[package]] name = "cairo-sys-rs" -version = "0.19.3" -source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#5e944f2eff8b344591666754f7ea3b359943ee8c" +version = "0.19.5" +source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#a7c5a9b6850f864cb3d30f679a827492b86247f1" dependencies = [ "glib-sys", "libc", @@ -982,12 +982,13 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.91" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd97381a8cc6493395a5afc4c691c1084b3768db713b73aa215217aa245d153" +checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" dependencies = [ "jobserver", "libc", + "once_cell", ] [[package]] @@ -1008,9 +1009,9 @@ dependencies = [ [[package]] name = "cea708-types" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd7f33493cb6f19aa19c6e688708f66bf792bc2c75137da2a03c7ebbdf7a44f9" +checksum = "82b825228dce83e7156c7cd189bcfe5ef8014320deca7dd619787fe594946426" dependencies = [ "env_logger 0.10.2", "log", @@ -1021,9 +1022,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.15.7" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa50868b64a9a6fda9d593ce778849ea8715cd2a3d2cc17ffdb4a2f2f2f1961d" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" dependencies = [ "smallvec", "target-lexicon", @@ -1037,9 +1038,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.37" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", @@ -1047,7 +1048,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -1090,7 +1091,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -1134,9 +1135,9 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "concurrent-queue" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ "crossbeam-utils", ] @@ -1364,7 +1365,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -1375,14 +1376,14 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] name = "dash-mpd" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cafa2c33eff2857e1a14c38aa9a432aa565a01e77804a541fce7aec3affb8f8" +checksum = "79b4bdd5f1c0c7493d780c645f0bff5b9361e6408210fa88910adb181efca64c" dependencies = [ "base64 0.22.0", "base64-serde", @@ -1402,19 +1403,6 @@ dependencies = [ "xattr", ] -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if", - "hashbrown 0.14.3", - "lock_api", - "once_cell", - "parking_lot_core", -] - [[package]] name = "dasp_frame" version = "0.11.0" @@ -1432,9 +1420,9 @@ checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" [[package]] name = "data-encoding" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "dav1d" @@ -1504,9 +1492,9 @@ dependencies = [ [[package]] name = "dssim-core" -version = "3.2.8" +version = "3.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fafad37c1f4f168243f3ac1b4cae0d358c528ac695670100337314e38d54b486" +checksum = "0c074fca6cdf5e3faaaf03f71e29cd5d92ea533b1432cf78910dafffc2ce872b" dependencies = [ "imgref", "itertools 0.12.1", @@ -1549,9 +1537,9 @@ dependencies = [ [[package]] name = "either" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "elliptic-curve" @@ -1575,9 +1563,9 @@ dependencies = [ [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] @@ -1636,9 +1624,9 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "332f51cb23d20b0de8458b86580878211da09bcd4503cb579c225b3d124cabb3" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" dependencies = [ "event-listener", "pin-project-lite", @@ -1652,9 +1640,9 @@ checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" [[package]] name = "fastrand" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "fdeflate" @@ -1703,9 +1691,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "4556222738635b7a3417ae6130d8f52201e45a0c4d1a907f0826383adb5f85e7" dependencies = [ "crc32fast", "miniz_oxide", @@ -1832,7 +1820,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -1867,8 +1855,8 @@ dependencies = [ [[package]] name = "gdk-pixbuf" -version = "0.19.3" -source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#5e944f2eff8b344591666754f7ea3b359943ee8c" +version = "0.19.5" +source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#a7c5a9b6850f864cb3d30f679a827492b86247f1" dependencies = [ "gdk-pixbuf-sys", "gio", @@ -1878,8 +1866,8 @@ dependencies = [ [[package]] name = "gdk-pixbuf-sys" -version = "0.19.3" -source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#5e944f2eff8b344591666754f7ea3b359943ee8c" +version = "0.19.5" +source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#a7c5a9b6850f864cb3d30f679a827492b86247f1" dependencies = [ "gio-sys", "glib-sys", @@ -1890,8 +1878,8 @@ dependencies = [ [[package]] name = "gdk4" -version = "0.8.1" -source = "git+https://github.com/gtk-rs/gtk4-rs?branch=0.8#05d26f6a97a0ccf856b565b4fd796cdf57059084" +version = "0.8.2" +source = "git+https://github.com/gtk-rs/gtk4-rs?branch=0.8#cf84b5cd36fc1aa31175bc24ff45e8ceb0710dec" dependencies = [ "cairo-rs", "gdk-pixbuf", @@ -1904,8 +1892,8 @@ dependencies = [ [[package]] name = "gdk4-sys" -version = "0.8.1" -source = "git+https://github.com/gtk-rs/gtk4-rs?branch=0.8#05d26f6a97a0ccf856b565b4fd796cdf57059084" +version = "0.8.2" +source = "git+https://github.com/gtk-rs/gtk4-rs?branch=0.8#cf84b5cd36fc1aa31175bc24ff45e8ceb0710dec" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -1920,8 +1908,8 @@ dependencies = [ [[package]] name = "gdk4-wayland" -version = "0.8.1" -source = "git+https://github.com/gtk-rs/gtk4-rs?branch=0.8#05d26f6a97a0ccf856b565b4fd796cdf57059084" +version = "0.8.2" +source = "git+https://github.com/gtk-rs/gtk4-rs?branch=0.8#cf84b5cd36fc1aa31175bc24ff45e8ceb0710dec" dependencies = [ "gdk4", "gdk4-wayland-sys", @@ -1932,8 +1920,8 @@ dependencies = [ [[package]] name = "gdk4-wayland-sys" -version = "0.8.1" -source = "git+https://github.com/gtk-rs/gtk4-rs?branch=0.8#05d26f6a97a0ccf856b565b4fd796cdf57059084" +version = "0.8.2" +source = "git+https://github.com/gtk-rs/gtk4-rs?branch=0.8#cf84b5cd36fc1aa31175bc24ff45e8ceb0710dec" dependencies = [ "glib-sys", "libc", @@ -1942,8 +1930,8 @@ dependencies = [ [[package]] name = "gdk4-win32" -version = "0.8.1" -source = "git+https://github.com/gtk-rs/gtk4-rs?branch=0.8#05d26f6a97a0ccf856b565b4fd796cdf57059084" +version = "0.8.2" +source = "git+https://github.com/gtk-rs/gtk4-rs?branch=0.8#cf84b5cd36fc1aa31175bc24ff45e8ceb0710dec" dependencies = [ "gdk4", "gdk4-win32-sys", @@ -1955,8 +1943,8 @@ dependencies = [ [[package]] name = "gdk4-win32-sys" -version = "0.8.1" -source = "git+https://github.com/gtk-rs/gtk4-rs?branch=0.8#05d26f6a97a0ccf856b565b4fd796cdf57059084" +version = "0.8.2" +source = "git+https://github.com/gtk-rs/gtk4-rs?branch=0.8#cf84b5cd36fc1aa31175bc24ff45e8ceb0710dec" dependencies = [ "gdk4-sys", "glib-sys", @@ -1966,8 +1954,8 @@ dependencies = [ [[package]] name = "gdk4-x11" -version = "0.8.1" -source = "git+https://github.com/gtk-rs/gtk4-rs?branch=0.8#05d26f6a97a0ccf856b565b4fd796cdf57059084" +version = "0.8.2" +source = "git+https://github.com/gtk-rs/gtk4-rs?branch=0.8#cf84b5cd36fc1aa31175bc24ff45e8ceb0710dec" dependencies = [ "gdk4", "gdk4-x11-sys", @@ -1978,8 +1966,8 @@ dependencies = [ [[package]] name = "gdk4-x11-sys" -version = "0.8.1" -source = "git+https://github.com/gtk-rs/gtk4-rs?branch=0.8#05d26f6a97a0ccf856b565b4fd796cdf57059084" +version = "0.8.2" +source = "git+https://github.com/gtk-rs/gtk4-rs?branch=0.8#cf84b5cd36fc1aa31175bc24ff45e8ceb0710dec" dependencies = [ "gdk4-sys", "glib-sys", @@ -2037,8 +2025,8 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "gio" -version = "0.19.3" -source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#5e944f2eff8b344591666754f7ea3b359943ee8c" +version = "0.19.5" +source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#a7c5a9b6850f864cb3d30f679a827492b86247f1" dependencies = [ "futures-channel", "futures-core", @@ -2054,8 +2042,8 @@ dependencies = [ [[package]] name = "gio-sys" -version = "0.19.3" -source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#5e944f2eff8b344591666754f7ea3b359943ee8c" +version = "0.19.5" +source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#a7c5a9b6850f864cb3d30f679a827492b86247f1" dependencies = [ "glib-sys", "gobject-sys", @@ -2066,8 +2054,8 @@ dependencies = [ [[package]] name = "glib" -version = "0.19.3" -source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#5e944f2eff8b344591666754f7ea3b359943ee8c" +version = "0.19.5" +source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#a7c5a9b6850f864cb3d30f679a827492b86247f1" dependencies = [ "bitflags 2.5.0", "futures-channel", @@ -2087,20 +2075,20 @@ dependencies = [ [[package]] name = "glib-macros" -version = "0.19.3" -source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#5e944f2eff8b344591666754f7ea3b359943ee8c" +version = "0.19.5" +source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#a7c5a9b6850f864cb3d30f679a827492b86247f1" dependencies = [ "heck 0.5.0", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] name = "glib-sys" -version = "0.19.3" -source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#5e944f2eff8b344591666754f7ea3b359943ee8c" +version = "0.19.5" +source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#a7c5a9b6850f864cb3d30f679a827492b86247f1" dependencies = [ "libc", "system-deps", @@ -2114,8 +2102,8 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "gobject-sys" -version = "0.19.3" -source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#5e944f2eff8b344591666754f7ea3b359943ee8c" +version = "0.19.5" +source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#a7c5a9b6850f864cb3d30f679a827492b86247f1" dependencies = [ "glib-sys", "libc", @@ -2124,8 +2112,8 @@ dependencies = [ [[package]] name = "graphene-rs" -version = "0.19.3" -source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#5e944f2eff8b344591666754f7ea3b359943ee8c" +version = "0.19.5" +source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#a7c5a9b6850f864cb3d30f679a827492b86247f1" dependencies = [ "glib", "graphene-sys", @@ -2134,8 +2122,8 @@ dependencies = [ [[package]] name = "graphene-sys" -version = "0.19.3" -source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#5e944f2eff8b344591666754f7ea3b359943ee8c" +version = "0.19.5" +source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#a7c5a9b6850f864cb3d30f679a827492b86247f1" dependencies = [ "glib-sys", "libc", @@ -2156,8 +2144,8 @@ dependencies = [ [[package]] name = "gsk4" -version = "0.8.1" -source = "git+https://github.com/gtk-rs/gtk4-rs?branch=0.8#05d26f6a97a0ccf856b565b4fd796cdf57059084" +version = "0.8.2" +source = "git+https://github.com/gtk-rs/gtk4-rs?branch=0.8#cf84b5cd36fc1aa31175bc24ff45e8ceb0710dec" dependencies = [ "cairo-rs", "gdk4", @@ -2170,8 +2158,8 @@ dependencies = [ [[package]] name = "gsk4-sys" -version = "0.8.1" -source = "git+https://github.com/gtk-rs/gtk4-rs?branch=0.8#05d26f6a97a0ccf856b565b4fd796cdf57059084" +version = "0.8.2" +source = "git+https://github.com/gtk-rs/gtk4-rs?branch=0.8#cf84b5cd36fc1aa31175bc24ff45e8ceb0710dec" dependencies = [ "cairo-sys-rs", "gdk4-sys", @@ -2648,11 +2636,11 @@ dependencies = [ "gstreamer-base", "headers 0.4.0", "http-body-util", - "hyper 1.2.0", + "hyper 1.3.1", "mime", "once_cell", "pin-project-lite", - "reqwest 0.12.3", + "reqwest 0.12.4", "tokio", "url", ] @@ -2845,7 +2833,7 @@ name = "gst-plugin-version-helper" version = "0.8.2" dependencies = [ "chrono", - "toml_edit 0.22.9", + "toml_edit 0.22.12", ] [[package]] @@ -2980,14 +2968,14 @@ dependencies = [ "gstreamer-webrtc", "once_cell", "parse_link_header", - "reqwest 0.12.3", + "reqwest 0.12.4", "tokio", ] [[package]] name = "gstreamer" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "cfg-if", "futures-channel", @@ -3012,8 +3000,8 @@ dependencies = [ [[package]] name = "gstreamer-allocators" -version = "0.23.0" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#e117010bc001f87551713c528bf3abff5c9848ae" +version = "0.22.4" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "glib", "gstreamer", @@ -3024,8 +3012,8 @@ dependencies = [ [[package]] name = "gstreamer-allocators-sys" -version = "0.23.0" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=main#e117010bc001f87551713c528bf3abff5c9848ae" +version = "0.22.4" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "glib-sys", "gobject-sys", @@ -3037,7 +3025,7 @@ dependencies = [ [[package]] name = "gstreamer-app" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "futures-core", "futures-sink", @@ -3051,7 +3039,7 @@ dependencies = [ [[package]] name = "gstreamer-app-sys" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "glib-sys", "gstreamer-base-sys", @@ -3063,7 +3051,7 @@ dependencies = [ [[package]] name = "gstreamer-audio" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "cfg-if", "glib", @@ -3079,7 +3067,7 @@ dependencies = [ [[package]] name = "gstreamer-audio-sys" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "glib-sys", "gobject-sys", @@ -3092,7 +3080,7 @@ dependencies = [ [[package]] name = "gstreamer-base" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "atomic_refcell", "cfg-if", @@ -3105,7 +3093,7 @@ dependencies = [ [[package]] name = "gstreamer-base-sys" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "glib-sys", "gobject-sys", @@ -3117,7 +3105,7 @@ dependencies = [ [[package]] name = "gstreamer-check" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "glib", "gstreamer", @@ -3127,7 +3115,7 @@ dependencies = [ [[package]] name = "gstreamer-check-sys" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "glib-sys", "gobject-sys", @@ -3139,7 +3127,7 @@ dependencies = [ [[package]] name = "gstreamer-gl" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "glib", "gstreamer", @@ -3153,7 +3141,7 @@ dependencies = [ [[package]] name = "gstreamer-gl-egl" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "glib", "gstreamer", @@ -3165,7 +3153,7 @@ dependencies = [ [[package]] name = "gstreamer-gl-egl-sys" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "glib-sys", "gstreamer-gl-sys", @@ -3176,7 +3164,7 @@ dependencies = [ [[package]] name = "gstreamer-gl-sys" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "glib-sys", "gobject-sys", @@ -3190,7 +3178,7 @@ dependencies = [ [[package]] name = "gstreamer-gl-wayland" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "glib", "gstreamer", @@ -3202,7 +3190,7 @@ dependencies = [ [[package]] name = "gstreamer-gl-wayland-sys" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "glib-sys", "gstreamer-gl-sys", @@ -3213,7 +3201,7 @@ dependencies = [ [[package]] name = "gstreamer-gl-x11" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "glib", "gstreamer", @@ -3225,7 +3213,7 @@ dependencies = [ [[package]] name = "gstreamer-gl-x11-sys" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "glib-sys", "gstreamer-gl-sys", @@ -3236,7 +3224,7 @@ dependencies = [ [[package]] name = "gstreamer-net" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "gio", "glib", @@ -3247,7 +3235,7 @@ dependencies = [ [[package]] name = "gstreamer-net-sys" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "gio-sys", "glib-sys", @@ -3259,7 +3247,7 @@ dependencies = [ [[package]] name = "gstreamer-pbutils" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "glib", "gstreamer", @@ -3273,7 +3261,7 @@ dependencies = [ [[package]] name = "gstreamer-pbutils-sys" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "glib-sys", "gobject-sys", @@ -3287,7 +3275,7 @@ dependencies = [ [[package]] name = "gstreamer-rtp" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "glib", "gstreamer", @@ -3298,7 +3286,7 @@ dependencies = [ [[package]] name = "gstreamer-rtp-sys" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "glib-sys", "gstreamer-base-sys", @@ -3310,7 +3298,7 @@ dependencies = [ [[package]] name = "gstreamer-sdp" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "glib", "gstreamer", @@ -3320,7 +3308,7 @@ dependencies = [ [[package]] name = "gstreamer-sdp-sys" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "glib-sys", "gstreamer-sys", @@ -3331,7 +3319,7 @@ dependencies = [ [[package]] name = "gstreamer-sys" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "glib-sys", "gobject-sys", @@ -3342,7 +3330,7 @@ dependencies = [ [[package]] name = "gstreamer-utils" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "gstreamer", "gstreamer-app", @@ -3354,7 +3342,7 @@ dependencies = [ [[package]] name = "gstreamer-video" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "cfg-if", "futures-channel", @@ -3371,7 +3359,7 @@ dependencies = [ [[package]] name = "gstreamer-video-sys" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "glib-sys", "gobject-sys", @@ -3384,7 +3372,7 @@ dependencies = [ [[package]] name = "gstreamer-webrtc" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "glib", "gstreamer", @@ -3396,7 +3384,7 @@ dependencies = [ [[package]] name = "gstreamer-webrtc-sys" version = "0.22.4" -source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#f2d3128bf9d0170288ce479bea021f0886617fac" +source = "git+https://gitlab.freedesktop.org/gstreamer/gstreamer-rs?branch=0.22#a0a23c6f453ee206a20943b508a086a8fc35cb28" dependencies = [ "glib-sys", "gstreamer-sdp-sys", @@ -3407,8 +3395,8 @@ dependencies = [ [[package]] name = "gtk4" -version = "0.8.1" -source = "git+https://github.com/gtk-rs/gtk4-rs?branch=0.8#05d26f6a97a0ccf856b565b4fd796cdf57059084" +version = "0.8.2" +source = "git+https://github.com/gtk-rs/gtk4-rs?branch=0.8#cf84b5cd36fc1aa31175bc24ff45e8ceb0710dec" dependencies = [ "cairo-rs", "field-offset", @@ -3427,21 +3415,19 @@ dependencies = [ [[package]] name = "gtk4-macros" -version = "0.8.1" -source = "git+https://github.com/gtk-rs/gtk4-rs?branch=0.8#05d26f6a97a0ccf856b565b4fd796cdf57059084" +version = "0.8.2" +source = "git+https://github.com/gtk-rs/gtk4-rs?branch=0.8#cf84b5cd36fc1aa31175bc24ff45e8ceb0710dec" dependencies = [ - "anyhow", "proc-macro-crate", - "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.60", ] [[package]] name = "gtk4-sys" -version = "0.8.1" -source = "git+https://github.com/gtk-rs/gtk4-rs?branch=0.8#05d26f6a97a0ccf856b565b4fd796cdf57059084" +version = "0.8.2" +source = "git+https://github.com/gtk-rs/gtk4-rs?branch=0.8#cf84b5cd36fc1aa31175bc24ff45e8ceb0710dec" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -3502,9 +3488,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", @@ -3729,7 +3715,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.6", + "socket2 0.4.10", "tokio", "tower-service", "tracing", @@ -3738,9 +3724,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a" +checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" dependencies = [ "bytes", "futures-channel", @@ -3809,7 +3795,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.2.0", + "hyper 1.3.1", "hyper-util", "native-tls", "tokio", @@ -3828,7 +3814,7 @@ dependencies = [ "futures-util", "http 1.1.0", "http-body 1.0.0", - "hyper 1.2.0", + "hyper 1.3.1", "pin-project-lite", "socket2 0.5.6", "tokio", @@ -3968,7 +3954,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown 0.14.5", "serde", ] @@ -3980,7 +3966,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -4035,9 +4021,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.28" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" dependencies = [ "libc", ] @@ -4391,9 +4377,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -4411,7 +4397,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" dependencies = [ - "hashbrown 0.14.3", + "hashbrown 0.14.5", ] [[package]] @@ -4697,7 +4683,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -4795,7 +4781,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -4850,8 +4836,8 @@ dependencies = [ [[package]] name = "pango" -version = "0.19.3" -source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#5e944f2eff8b344591666754f7ea3b359943ee8c" +version = "0.19.5" +source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#a7c5a9b6850f864cb3d30f679a827492b86247f1" dependencies = [ "gio", "glib", @@ -4861,8 +4847,8 @@ dependencies = [ [[package]] name = "pango-sys" -version = "0.19.3" -source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#5e944f2eff8b344591666754f7ea3b359943ee8c" +version = "0.19.5" +source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#a7c5a9b6850f864cb3d30f679a827492b86247f1" dependencies = [ "glib-sys", "gobject-sys", @@ -4872,8 +4858,8 @@ dependencies = [ [[package]] name = "pangocairo" -version = "0.19.3" -source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#5e944f2eff8b344591666754f7ea3b359943ee8c" +version = "0.19.5" +source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#a7c5a9b6850f864cb3d30f679a827492b86247f1" dependencies = [ "cairo-rs", "glib", @@ -4884,8 +4870,8 @@ dependencies = [ [[package]] name = "pangocairo-sys" -version = "0.19.3" -source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#5e944f2eff8b344591666754f7ea3b359943ee8c" +version = "0.19.5" +source = "git+https://github.com/gtk-rs/gtk-rs-core?branch=0.19#a7c5a9b6850f864cb3d30f679a827492b86247f1" dependencies = [ "cairo-sys-rs", "glib-sys", @@ -4902,9 +4888,9 @@ checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" dependencies = [ "lock_api", "parking_lot_core", @@ -4912,9 +4898,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "backtrace", "cfg-if", @@ -4923,7 +4909,7 @@ dependencies = [ "redox_syscall", "smallvec", "thread-id", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] @@ -5024,7 +5010,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -5076,9 +5062,9 @@ checksum = "c135f38778ad324d9e9ee68690bac2c1a51f340fdf96ca13e2ab3914eb2e51d8" [[package]] name = "polling" -version = "3.6.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c976a60b2d7e99d6f229e414670a9b85d13ac305cc6d1e9c134de58c5aaaf6" +checksum = "645493cf344456ef24219d02a768cf1fb92ddf8c92161679ae3d91b91a637be3" dependencies = [ "cfg-if", "concurrent-queue", @@ -5118,7 +5104,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" dependencies = [ "proc-macro2", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -5175,9 +5161,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] @@ -5198,7 +5184,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd" dependencies = [ "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -5219,7 +5205,7 @@ checksum = "80b776a1b2dc779f5ee0641f8ade0125bc1298dd41a9a0c16d8bd57b42d222b1" dependencies = [ "bytes", "heck 0.4.1", - "itertools 0.12.1", + "itertools 0.11.0", "log", "multimap 0.10.0", "once_cell", @@ -5228,7 +5214,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.58", + "syn 2.0.60", "tempfile", ] @@ -5239,10 +5225,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.11.0", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -5307,9 +5293,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -5427,11 +5413,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", ] [[package]] @@ -5526,9 +5512,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e6cc1e89e689536eb5aeede61520e874df5a4707df811cd5da4aa5fbb2aae19" +checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" dependencies = [ "async-compression", "base64 0.22.0", @@ -5542,7 +5528,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.0", "http-body-util", - "hyper 1.2.0", + "hyper 1.3.1", "hyper-tls 0.6.0", "hyper-util", "ipnet", @@ -5682,9 +5668,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.32" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags 2.5.0", "errno", @@ -5695,9 +5681,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.10" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring", @@ -5738,9 +5724,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247" +checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" [[package]] name = "rustls-webpki" @@ -5767,6 +5753,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scc" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec96560eea317a9cc4e0bb1f6a2c93c09a19b8c4fc5cb3fcc0ec1c094cd783e2" +dependencies = [ + "sdd", +] + [[package]] name = "schannel" version = "0.1.23" @@ -5798,6 +5793,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "sdd" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b84345e4c9bd703274a082fb80caaa99b7612be48dfaa1dd9266577ec412309d" + [[package]] name = "sdp-types" version = "0.1.6" @@ -5853,9 +5854,9 @@ checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "serde" -version = "1.0.197" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" dependencies = [ "serde_derive", ] @@ -5871,20 +5872,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] name = "serde_json" -version = "1.0.115" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "itoa", "ryu", @@ -5924,11 +5925,11 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.7.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee80b0e361bbf88fd2f6e242ccd19cfda072cb0faa6ae694ecee08199938569a" +checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20" dependencies = [ - "base64 0.21.7", + "base64 0.22.0", "chrono", "hex", "indexmap 1.9.3", @@ -5942,39 +5943,39 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.7.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6561dc161a9224638a31d876ccdfefbc1df91d3f3a8342eddb35f055d48c7655" +checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] name = "serial_test" -version = "3.0.0" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ad9342b3aaca7cb43c45c097dd008d4907070394bd0751a0aa8817e5a018d" +checksum = "4b4b487fe2acf240a021cf57c6b2b4903b1e78ca0ecd862a71b71d2a51fed77d" dependencies = [ - "dashmap", "futures", - "lazy_static", "log", + "once_cell", "parking_lot", + "scc", "serial_test_derive", ] [[package]] name = "serial_test_derive" -version = "3.0.0" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b93fb4adc70021ac1b47f7d45e8cc4169baaa7ea58483bc5b721d19a26202212" +checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -6048,9 +6049,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -6183,15 +6184,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", - "quote", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.58" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", @@ -6283,7 +6283,7 @@ checksum = "7ba277e77219e9eea169e8508942db1bf5d8a41ff2db9b20aab5a5aadc9fa25d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -6296,7 +6296,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -6313,22 +6313,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.58" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -6353,9 +6353,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.34" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", @@ -6374,9 +6374,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ "num-conv", "time-core", @@ -6424,7 +6424,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -6507,7 +6507,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.9", + "toml_edit 0.22.12", ] [[package]] @@ -6532,15 +6532,15 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.9" +version = "0.22.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" +checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" dependencies = [ "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.5", + "winnow 0.6.7", ] [[package]] @@ -6591,7 +6591,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -6733,9 +6733,9 @@ dependencies = [ [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" [[package]] name = "untrusted" @@ -6929,7 +6929,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", "wasm-bindgen-shared", ] @@ -6963,7 +6963,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -7008,11 +7008,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -7027,7 +7027,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -7045,7 +7045,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.4", + "windows-targets 0.52.5", ] [[package]] @@ -7065,17 +7065,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.4", - "windows_aarch64_msvc 0.52.4", - "windows_i686_gnu 0.52.4", - "windows_i686_msvc 0.52.4", - "windows_x86_64_gnu 0.52.4", - "windows_x86_64_gnullvm 0.52.4", - "windows_x86_64_msvc 0.52.4", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -7086,9 +7087,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -7098,9 +7099,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -7110,9 +7111,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -7122,9 +7129,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -7134,9 +7141,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -7146,9 +7153,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -7158,9 +7165,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" @@ -7173,9 +7180,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.5" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578" dependencies = [ "memchr", ] @@ -7265,7 +7272,7 @@ checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] @@ -7276,7 +7283,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.58", + "syn 2.0.60", ] [[package]] From 21139053947c0fe448448751c55375a3022deaea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Mon, 29 Apr 2024 11:54:25 +0300 Subject: [PATCH 16/16] deny: Remove syn override Part-of: --- deny.toml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/deny.toml b/deny.toml index e3d68df0..f348841c 100644 --- a/deny.toml +++ b/deny.toml @@ -102,11 +102,6 @@ version = "0.21" name = "socket2" version = "0.4" -# Various crates depend on an older version of syn -[[bans.skip]] -name = "syn" -version = "1.0" - # Various crates depend on an older version of bitflags [[bans.skip]] name = "bitflags"