From 9243f6c3c09cd8a6440e66250e81723df37f1943 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Tue, 4 Nov 2025 18:00:31 -0800 Subject: [PATCH 1/2] Initial MSE traits w/ gstreamer backing Signed-off-by: Ashwin Naren --- Cargo.lock | 31 ++++++ Cargo.toml | 4 +- backends/gstreamer/Cargo.toml | 2 + backends/gstreamer/lib.rs | 9 ++ backends/gstreamer/media_source.rs | 118 ++++++++++++++++++++++ backends/gstreamer/player.rs | 35 +++++++ backends/gstreamer/source_buffer.rs | 119 +++++++++++++++++++++++ backends/gstreamer/source_buffer_list.rs | 38 ++++++++ mse/Cargo.toml | 10 ++ mse/lib.rs | 69 +++++++++++++ player/Cargo.toml | 3 + player/lib.rs | 7 ++ servo-media/Cargo.toml | 3 + servo-media/lib.rs | 7 ++ 14 files changed, 454 insertions(+), 1 deletion(-) create mode 100644 backends/gstreamer/media_source.rs create mode 100644 backends/gstreamer/source_buffer.rs create mode 100644 backends/gstreamer/source_buffer_list.rs create mode 100644 mse/Cargo.toml create mode 100644 mse/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 92d68645..44efa988 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1200,6 +1200,29 @@ dependencies = [ "system-deps", ] +[[package]] +name = "gstreamer-mse" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8252729dc0d201d90c95bfc4a066f71cf415f4b93d19d88a50ca0e033aa571e4" +dependencies = [ + "glib", + "gstreamer", + "gstreamer-mse-sys", +] + +[[package]] +name = "gstreamer-mse-sys" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c4eb067d1723819c5a31525b35594973447ae5ab9f31c793565f3765e6219dc" +dependencies = [ + "glib-sys", + "gstreamer-sys", + "libc", + "system-deps", +] + [[package]] name = "gstreamer-play" version = "0.24.2" @@ -2492,6 +2515,7 @@ version = "0.1.0" dependencies = [ "once_cell", "servo-media-audio", + "servo-media-mse", "servo-media-player", "servo-media-streams", "servo-media-traits", @@ -2559,6 +2583,7 @@ dependencies = [ "gstreamer-app", "gstreamer-audio", "gstreamer-base", + "gstreamer-mse", "gstreamer-play", "gstreamer-sdp", "gstreamer-sys", @@ -2573,6 +2598,7 @@ dependencies = [ "servo-media-gstreamer-render", "servo-media-gstreamer-render-android", "servo-media-gstreamer-render-unix", + "servo-media-mse", "servo-media-player", "servo-media-streams", "servo-media-traits", @@ -2617,6 +2643,10 @@ dependencies = [ "servo-media-player", ] +[[package]] +name = "servo-media-mse" +version = "0.1.0" + [[package]] name = "servo-media-player" version = "0.1.0" @@ -2624,6 +2654,7 @@ dependencies = [ "ipc-channel", "serde", "serde_derive", + "servo-media-mse", "servo-media-streams", "servo-media-traits", ] diff --git a/Cargo.toml b/Cargo.toml index 11b630c4..16b0d520 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "backends/gstreamer/render-unix", "examples", "examples/android/lib", + "mse", "player", "servo-media", "servo-media-derive", @@ -19,11 +20,12 @@ members = [ [workspace.dependencies] glib = "0.21" glib-sys = "0.21" -gst = { package = "gstreamer", version = "0.24" } +gst = { package = "gstreamer", version = "0.24", features = ["v1_26"] } gst-app = { package = "gstreamer-app", version = "0.24" } gst-audio = { package = "gstreamer-audio", version = "0.24" } gst-base = { package = "gstreamer-base", version = "0.24" } gst-gl = { package = "gstreamer-gl", version = "0.24" } +gst-mse = { package = "gstreamer-mse", version = "0.24" } gst-play = { package = "gstreamer-play", version = "0.24" } gst-sdp = { package = "gstreamer-sdp", version = "0.24" } gst-video = { package = "gstreamer-video", version = "0.24" } diff --git a/backends/gstreamer/Cargo.toml b/backends/gstreamer/Cargo.toml index 69b744ab..781ef5a0 100644 --- a/backends/gstreamer/Cargo.toml +++ b/backends/gstreamer/Cargo.toml @@ -18,6 +18,7 @@ gst-app = { workspace = true } gst-audio = { workspace = true } gst-video = { workspace = true } gst-base = { workspace = true } +gst-mse = { workspace = true } gst-play = { workspace = true } gst-webrtc = { workspace = true } gst-sdp = { workspace = true } @@ -29,6 +30,7 @@ once_cell = "1.18.0" servo-media = { path = "../../servo-media" } servo-media-audio = { path = "../../audio" } servo-media-gstreamer-render = { path = "render" } +servo-media-mse = { path = "../../mse" } servo-media-player = { path = "../../player" } servo-media-streams = { path = "../../streams" } servo-media-traits = { path = "../../traits" } diff --git a/backends/gstreamer/lib.rs b/backends/gstreamer/lib.rs index 37cf63ef..288caeb0 100644 --- a/backends/gstreamer/lib.rs +++ b/backends/gstreamer/lib.rs @@ -4,14 +4,18 @@ pub mod audio_stream_reader; mod datachannel; mod device_monitor; pub mod media_capture; +pub mod media_source; pub mod media_stream; mod media_stream_source; pub mod player; mod registry_scanner; mod render; mod source; +mod source_buffer; +mod source_buffer_list; pub mod webrtc; +use crate::media_source::GStreamerMediaSource; use device_monitor::GStreamerDeviceMonitor; use gst::prelude::*; use ipc_channel::ipc::IpcSender; @@ -25,6 +29,7 @@ use servo_media_audio::context::{AudioContext, AudioContextOptions}; use servo_media_audio::decoder::AudioDecoder; use servo_media_audio::sink::AudioSinkError; use servo_media_audio::{AudioBackend, AudioStreamReader}; +use servo_media_mse::MediaSource; use servo_media_player::audio::AudioRenderer; use servo_media_player::context::PlayerGLContext; use servo_media_player::video::VideoFrameRenderer; @@ -301,6 +306,10 @@ impl Backend for GStreamerBackend { fn get_device_monitor(&self) -> Box { Box::new(GStreamerDeviceMonitor::new()) } + + fn create_mse_source(&self) -> Option> { + Some(Box::new(GStreamerMediaSource::new())) + } } impl AudioBackend for GStreamerBackend { diff --git a/backends/gstreamer/media_source.rs b/backends/gstreamer/media_source.rs new file mode 100644 index 00000000..46683b3e --- /dev/null +++ b/backends/gstreamer/media_source.rs @@ -0,0 +1,118 @@ +use crate::source_buffer::GStreamerSourceBuffer; +use crate::source_buffer_list::GStreamerSourceBufferList; +use gst::ClockTime; +use gst_mse::{MediaSource as GstMediaSource, MediaSourceEOSError}; +use servo_media_mse::{EosError, MediaSource, ReadyState, SourceBuffer, SourceBufferList}; + +#[repr(transparent)] +pub struct GStreamerMediaSource { + inner: GstMediaSource, +} + +impl GStreamerMediaSource { + pub fn new() -> Self { + GStreamerMediaSource { + inner: GstMediaSource::new(), + } + } + + pub(crate) fn inner(&self) -> &GstMediaSource { + &self.inner + } + + pub(crate) fn from_ref(inner: &GstMediaSource) -> &Self { + unsafe { &*(inner as *const GstMediaSource as *const GStreamerMediaSource) } + } +} + +impl MediaSource for GStreamerMediaSource { + fn add_source_buffer(&self, ty: &str) -> Box { + let buffer = self.inner.add_source_buffer(ty).unwrap(); + let buffer = GStreamerSourceBuffer::from(buffer); + Box::new(buffer) + } + + fn remove_source_buffer(&self, source_buffer: &dyn SourceBuffer) { + let gst_source_buffer = source_buffer + .as_any() + .downcast_ref::() + .expect("Expected a GStreamer SourceBuffer"); + self.inner.remove_source_buffer(gst_source_buffer.inner()); + } + + fn live_seekable_range(&self) -> (Option, Option) { + let range = self.inner.live_seekable_range(); + ( + Some(range.start().seconds_f64()), + Some(range.end().seconds_f64()), + ) + } + + fn set_live_seekable_range(&self, start: Option, end: Option) { + let start = start.map(ClockTime::from_seconds_f64); + let end = end.map(ClockTime::from_seconds_f64); + self.inner.set_live_seekable_range(start, end); + } + + fn clear_live_seekable_range(&self) { + self.inner.clear_live_seekable_range(); + } + + fn active_source_buffers(&self) -> Box { + let list: GStreamerSourceBufferList = self.inner.active_source_buffers().into(); + Box::new(list) + } + + fn source_buffers(&self) -> Box { + let list: GStreamerSourceBufferList = self.inner.source_buffers().into(); + Box::new(list) + } + + fn duration(&self) -> Option { + self.inner.duration().map(|d| d.seconds_f64()) + } + + fn set_duration(&self, duration: f64) { + self.inner + .set_duration(ClockTime::from_seconds_f64(duration)); + } + + fn end_of_stream(&self, error: Option) { + let _ = match error { + Some(EosError::Network) => self.inner.end_of_stream(MediaSourceEOSError::Network), + Some(EosError::Decode) => self.inner.end_of_stream(MediaSourceEOSError::Decode), + None => self.inner.end_of_stream(MediaSourceEOSError::None), + }; + } + + fn ready_state(&self) -> ReadyState { + match self.inner.ready_state() { + gst_mse::MediaSourceReadyState::Closed => ReadyState::Closed, + gst_mse::MediaSourceReadyState::Open => ReadyState::Open, + gst_mse::MediaSourceReadyState::Ended => ReadyState::Ended, + _ => unreachable!(), + } + } + + fn on_source_open(&self, f: Box) { + self.inner.connect_on_source_open(move |s| { + f(Self::from_ref(s)); + }); + } + + fn on_source_ended(&self, f: Box) { + self.inner.connect_on_source_ended(move |s| { + f(Self::from_ref(s)); + }); + } + + fn on_source_close(&self, f: Box) { + self.inner.connect_on_source_close(move |s| { + f(Self::from_ref(s)); + }); + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} diff --git a/backends/gstreamer/player.rs b/backends/gstreamer/player.rs index 1757e83d..6f2f1778 100644 --- a/backends/gstreamer/player.rs +++ b/backends/gstreamer/player.rs @@ -1,4 +1,5 @@ use super::BACKEND_BASE_TIME; +use crate::media_source::GStreamerMediaSource; use crate::media_stream::GStreamerMediaStream; use crate::media_stream_source::{register_servo_media_stream_src, ServoMediaStreamSrc}; use crate::render::GStreamerRender; @@ -9,9 +10,11 @@ use glib::prelude::*; use gst; use gst::prelude::*; use gst_app; +use gst_mse::MseSrc; use gst_play; use gst_play::prelude::*; use ipc_channel::ipc::{channel, IpcReceiver, IpcSender}; +use servo_media_mse::MediaSource; use servo_media_player::audio::AudioRenderer; use servo_media_player::context::PlayerGLContext; use servo_media_player::metadata::Metadata; @@ -108,6 +111,7 @@ impl AsRef<[f32]> for GStreamerAudioChunk { enum PlayerSource { Seekable(ServoSrc), Stream(ServoMediaStreamSrc), + Mse(MseSrc), } struct PlayerInner { @@ -313,6 +317,19 @@ impl PlayerInner { self.player.set_video_track_enabled(enabled); Ok(()) } + + fn connect_media_source(&self, source: &dyn MediaSource) -> Result<(), PlayerError> { + debug_assert!(self.stream_type == StreamType::MSE); + if let Some(ref src) = self.source { + if let PlayerSource::Mse(src) = src { + if let Some(gst_source) = source.as_any().downcast_ref::() { + gst_source.inner().attach(src); + return Ok(()); + } + } + } + Err(PlayerError::SetStreamFailed) + } } macro_rules! notify( @@ -528,6 +545,7 @@ impl GStreamerPlayer { })?; "servosrc://".to_value() }, + StreamType::MSE => "mse://".to_value(), }; player.set_property("uri", &uri); @@ -774,6 +792,16 @@ impl GStreamerPlayer { }); PlayerSource::Stream(media_stream_src) }, + StreamType::MSE => { + let mse_src = source + .dynamic_cast::() + .expect("Source element is expected to be a MseSrc!"); + let sender_clone = sender.clone(); + is_ready_clone.call_once(|| { + let _ = notify!(sender_clone, Ok(())); + }); + PlayerSource::Mse(mse_src) + }, }; inner.set_src(source); @@ -874,6 +902,13 @@ impl Player for GStreamerPlayer { let mut inner = inner.as_ref().unwrap().lock().unwrap(); inner.set_video_track(stream_index, enabled) } + + fn connect_media_source(&self, source: &dyn MediaSource) -> Result<(), PlayerError> { + self.setup()?; + let inner = self.inner.borrow(); + let inner = inner.as_ref().unwrap().lock().unwrap(); + inner.connect_media_source(source) + } } impl MediaInstance for GStreamerPlayer { diff --git a/backends/gstreamer/source_buffer.rs b/backends/gstreamer/source_buffer.rs new file mode 100644 index 00000000..15e22e22 --- /dev/null +++ b/backends/gstreamer/source_buffer.rs @@ -0,0 +1,119 @@ +use gst::{Buffer, ClockTime}; +use gst_mse::{SourceBuffer as GstSourceBuffer, SourceBufferAppendMode}; +use servo_media_mse::{AppendMode, SourceBuffer}; +use std::any::Any; + +pub struct GStreamerSourceBuffer { + inner: GstSourceBuffer, +} + +impl GStreamerSourceBuffer { + pub fn inner(&self) -> &GstSourceBuffer { + &self.inner + } +} + +impl From for GStreamerSourceBuffer { + fn from(inner: GstSourceBuffer) -> Self { + GStreamerSourceBuffer { inner } + } +} + +impl SourceBuffer for GStreamerSourceBuffer { + fn append_mode(&self) -> AppendMode { + match self.inner.append_mode() { + SourceBufferAppendMode::Segments => AppendMode::Segments, + SourceBufferAppendMode::Sequence => AppendMode::Sequence, + _ => unreachable!(), + } + } + + fn set_append_mode(&self, mode: AppendMode) { + let gst_mode = match mode { + AppendMode::Segments => SourceBufferAppendMode::Segments, + AppendMode::Sequence => SourceBufferAppendMode::Sequence, + }; + self.inner.set_append_mode(gst_mode); + } + + fn updating(&self) -> bool { + self.inner.is_updating() + } + + fn abort(&self) -> Result<(), ()> { + self.inner.abort().map_err(|_| ()) + } + + fn timestamp_offset(&self) -> Option { + self.inner.timestamp_offset().map(|t| t.seconds_f64()) + } + + fn set_timestamp_offset(&self, offset: f64) { + self.inner + .set_timestamp_offset(ClockTime::from_seconds_f64(offset)); + } + + fn append_window_start(&self) -> Option { + self.inner.append_window_start().map(|t| t.seconds_f64()) + } + + fn set_append_window_start(&self, start: f64) { + self.inner + .set_append_window_start(ClockTime::from_seconds_f64(start)); + } + + fn append_window_end(&self) -> Option { + self.inner.append_window_end().map(|t| t.seconds_f64()) + } + + fn set_append_window_end(&self, end: f64) { + self.inner + .set_append_window_end(ClockTime::from_seconds_f64(end)); + } + + fn append_buffer(&self, data: Vec) { + let buffer = Buffer::from_slice(data); + self.inner.append_buffer(buffer); + } + + fn remove(&self, start: f64, end: f64) { + self.inner.remove( + ClockTime::from_seconds_f64(start), + ClockTime::from_seconds_f64(end), + ); + } + + fn on_update_start(&self, f: Box) { + self.inner.connect_on_update_start(move |_b| { + f(); + }); + } + + fn on_update(&self, f: Box) { + self.inner.connect_on_update(move |_b| { + f(); + }); + } + + fn on_update_end(&self, f: Box) { + self.inner.connect_on_update_end(move |_b| { + f(); + }); + } + + fn on_error(&self, f: Box) { + self.inner.connect_on_error(move |_b| { + f(); + }); + } + + fn on_abort(&self, f: Box) { + self.inner.connect_on_abort(move |_b| { + f(); + }); + } + + fn as_any(&self) -> &dyn Any { + self + } +} diff --git a/backends/gstreamer/source_buffer_list.rs b/backends/gstreamer/source_buffer_list.rs new file mode 100644 index 00000000..e45f1cb0 --- /dev/null +++ b/backends/gstreamer/source_buffer_list.rs @@ -0,0 +1,38 @@ +use crate::source_buffer::GStreamerSourceBuffer; +use gst_mse::{SourceBuffer as GstSourceBuffer, SourceBufferList as GstSourceBufferList}; +use servo_media_mse::{SourceBuffer, SourceBufferList}; + +pub struct GStreamerSourceBufferList { + inner: GstSourceBufferList, +} + +impl From for GStreamerSourceBufferList { + fn from(inner: GstSourceBufferList) -> Self { + GStreamerSourceBufferList { inner } + } +} + +impl SourceBufferList for GStreamerSourceBufferList { + fn length(&self) -> u32 { + self.inner.length() + } + + fn index(&self, index: u32) -> Option> { + self.inner.index(index).map(|buffer| { + let source_buffer = GStreamerSourceBuffer::from(buffer); + Box::new(source_buffer) as Box + }) + } + + fn on_add_source_buffer(&self, f: Box) { + self.inner.connect_on_sourcebuffer_added(move |_l| { + f(); + }); + } + + fn on_remove_source_buffer(&self, f: Box) { + self.inner.connect_on_sourcebuffer_removed(move |_l| { + f(); + }); + } +} diff --git a/mse/Cargo.toml b/mse/Cargo.toml new file mode 100644 index 00000000..467fafd9 --- /dev/null +++ b/mse/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "servo-media-mse" +version = "0.1.0" +edition = "2024" + +[lib] +name = "servo_media_mse" +path = "lib.rs" + +[dependencies] diff --git a/mse/lib.rs b/mse/lib.rs new file mode 100644 index 00000000..d8274696 --- /dev/null +++ b/mse/lib.rs @@ -0,0 +1,69 @@ +use std::any::Any; + +pub enum AppendMode { + Segments, + Sequence, +} + +pub enum EosError { + Network, + Decode, +} + +pub enum ReadyState { + Closed, + Open, + Ended, +} + +pub trait SourceBuffer: Any + Send { + fn append_mode(&self) -> AppendMode; + fn set_append_mode(&self, mode: AppendMode); + fn updating(&self) -> bool; + fn abort(&self) -> Result<(), ()>; + fn timestamp_offset(&self) -> Option; + fn set_timestamp_offset(&self, offset: f64); + fn append_window_start(&self) -> Option; + fn set_append_window_start(&self, start: f64); + fn append_window_end(&self) -> Option; + fn set_append_window_end(&self, end: f64); + + fn append_buffer(&self, data: Vec); + fn remove(&self, start: f64, end: f64); + + fn on_update_start(&self, f: Box); + fn on_update(&self, f: Box); + fn on_update_end(&self, f: Box); + fn on_error(&self, f: Box); + fn on_abort(&self, f: Box); + + fn as_any(&self) -> &dyn Any; +} + +pub trait SourceBufferList: Send { + fn length(&self) -> u32; + fn index(&self, index: u32) -> Option>; + + fn on_add_source_buffer(&self, f: Box); + fn on_remove_source_buffer(&self, f: Box); +} + +pub trait MediaSource: Send { + fn add_source_buffer(&self, ty: &str) -> Box; + fn remove_source_buffer(&self, source_buffer: &dyn SourceBuffer); + fn live_seekable_range(&self) -> (Option, Option); + fn set_live_seekable_range(&self, start: Option, end: Option); + fn clear_live_seekable_range(&self); + fn active_source_buffers(&self) -> Box; + fn source_buffers(&self) -> Box; + fn duration(&self) -> Option; + fn set_duration(&self, duration: f64); + fn end_of_stream(&self, error: Option); + fn ready_state(&self) -> ReadyState; + + fn on_source_open(&self, f: Box); + fn on_source_ended(&self, f: Box); + fn on_source_close(&self, f: Box); + + fn as_any(&self) -> &dyn Any; +} diff --git a/player/Cargo.toml b/player/Cargo.toml index d35db592..2b006148 100644 --- a/player/Cargo.toml +++ b/player/Cargo.toml @@ -20,3 +20,6 @@ path = "../streams" [dependencies.servo-media-traits] path = "../traits" + +[dependencies.servo-media-mse] +path = "../mse" diff --git a/player/lib.rs b/player/lib.rs index 75caa4ff..00f77956 100644 --- a/player/lib.rs +++ b/player/lib.rs @@ -10,6 +10,7 @@ pub mod metadata; pub mod video; use ipc_channel::ipc::{self, IpcSender}; +use servo_media_mse::MediaSource; use servo_media_traits::MediaInstance; use std::ops::Range; use streams::registry::MediaStreamId; @@ -87,6 +88,8 @@ pub enum StreamType { Stream, /// The stream is seekable. Seekable, + /// The stream is user controlled + MSE, } pub trait Player: Send + MediaInstance { @@ -117,4 +120,8 @@ pub trait Player: Send + MediaInstance { fn render_use_gl(&self) -> bool; fn set_audio_track(&self, stream_index: i32, enabled: bool) -> Result<(), PlayerError>; fn set_video_track(&self, stream_index: i32, enabled: bool) -> Result<(), PlayerError>; + + fn connect_media_source(&self, source: &dyn MediaSource) -> Result<(), PlayerError> { + Err(PlayerError::SetStreamFailed) + } } diff --git a/servo-media/Cargo.toml b/servo-media/Cargo.toml index b385dca8..b7e0c30e 100644 --- a/servo-media/Cargo.toml +++ b/servo-media/Cargo.toml @@ -15,6 +15,9 @@ once_cell = "1.18.0" [dependencies.servo-media-audio] path = "../audio" +[dependencies.servo-media-mse] +path = "../mse" + [dependencies.servo-media-player] path = "../player" diff --git a/servo-media/lib.rs b/servo-media/lib.rs index a97f96ac..648cfc22 100644 --- a/servo-media/lib.rs +++ b/servo-media/lib.rs @@ -1,4 +1,5 @@ pub extern crate servo_media_audio as audio; +pub extern crate servo_media_mse as mse; pub extern crate servo_media_player as player; pub extern crate servo_media_streams as streams; pub extern crate servo_media_traits as traits; @@ -27,6 +28,7 @@ use streams::registry::MediaStreamId; use streams::{MediaOutput, MediaSocket, MediaStreamType}; use webrtc::{WebRtcController, WebRtcSignaller}; +use mse::MediaSource; use once_cell::sync::OnceCell; pub struct ServoMedia(Box); @@ -89,6 +91,11 @@ pub trait Backend: Send + Sync { fn resume(&self, _id: &ClientContextId) {} fn get_device_monitor(&self) -> Box; + + /// MSE + fn create_mse_source(&self) -> Option> { + None + } } #[derive(Clone, Copy, Debug, PartialEq)] From 3f9382a0d0ca6511281fd8c16f1787a0abdf5f13 Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Wed, 19 Nov 2025 16:52:15 -0800 Subject: [PATCH 2/2] try fix CI Signed-off-by: Ashwin Naren --- .github/workflows/rust.yml | 5 ++--- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b29202c7..3e90e56b 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -17,7 +17,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-22.04] + os: [ubuntu-latest] rust: [stable] target: ["default"] steps: @@ -36,7 +36,7 @@ jobs: gstreamer1.0-tools \ libasound2-plugins \ libfaad2 \ - libffi7 \ + libffi8 \ libfftw3-single3 \ libges-1.0-dev \ libgstreamer-plugins-bad1.0-dev\ @@ -49,7 +49,6 @@ jobs: libopus0 \ liborc-0.4-0 \ liborc-0.4-dev \ - libpulsedsp \ libsamplerate0 \ libspeexdsp1 \ libtdb1 \ diff --git a/Cargo.toml b/Cargo.toml index 16b0d520..35a84946 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ members = [ [workspace.dependencies] glib = "0.21" glib-sys = "0.21" -gst = { package = "gstreamer", version = "0.24", features = ["v1_26"] } +gst = { package = "gstreamer", version = "0.24", features = ["v1_24"] } gst-app = { package = "gstreamer-app", version = "0.24" } gst-audio = { package = "gstreamer-audio", version = "0.24" } gst-base = { package = "gstreamer-base", version = "0.24" }