diff --git a/Cargo.lock b/Cargo.lock index 24361a534..60e76f8eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -875,18 +875,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "gst-mse-sys" -version = "0.0.1" -dependencies = [ - "glib-sys", - "gstreamer-sys", - "libc", - "shell-words", - "system-deps", - "tempfile", -] - [[package]] name = "gstreamer" version = "0.24.0" @@ -1254,6 +1242,27 @@ dependencies = [ "tempfile", ] +[[package]] +name = "gstreamer-mse" +version = "0.24.0" +dependencies = [ + "glib", + "gstreamer", + "gstreamer-mse-sys", +] + +[[package]] +name = "gstreamer-mse-sys" +version = "0.24.0" +dependencies = [ + "glib-sys", + "gstreamer-sys", + "libc", + "shell-words", + "system-deps", + "tempfile", +] + [[package]] name = "gstreamer-net" version = "0.24.0" diff --git a/Cargo.toml b/Cargo.toml index f0e13a31f..d6024d309 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ default-members = [ "gstreamer-controller", "gstreamer-editing-services", "gstreamer-mpegts", + "gstreamer-mse", "gstreamer-net", "gstreamer-pbutils", "gstreamer-play", @@ -88,6 +89,7 @@ members = [ "gstreamer-gl/wayland", "gstreamer-gl/x11", "gstreamer-mpegts", + "gstreamer-mse", "gstreamer-net", "gstreamer-pbutils", "gstreamer-play", @@ -163,6 +165,7 @@ gst-gl = { package = "gstreamer-gl", path = "./gstreamer-gl" } gst-gl-egl = { package = "gstreamer-gl-egl", path = "./gstreamer-gl/egl" } gst-gl-x11 = { package = "gstreamer-gl-x11", path = "./gstreamer-gl/x11" } gst-net = { package = "gstreamer-net", path = "./gstreamer-net" } +gst-mse = { package = "gstreamer-mse", path = "./gstreamer-mse" } gst-pbutils = { package = "gstreamer-pbutils", path = "./gstreamer-pbutils" } gst-play = { package = "gstreamer-play", path = "./gstreamer-play" } gst-player = { package = "gstreamer-player", path = "./gstreamer-player" } diff --git a/gstreamer-mse/COPYRIGHT b/gstreamer-mse/COPYRIGHT new file mode 120000 index 000000000..dc5f40a22 --- /dev/null +++ b/gstreamer-mse/COPYRIGHT @@ -0,0 +1 @@ +../COPYRIGHT \ No newline at end of file diff --git a/gstreamer-mse/Cargo.toml b/gstreamer-mse/Cargo.toml new file mode 100644 index 000000000..9dc3904bf --- /dev/null +++ b/gstreamer-mse/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "gstreamer-mse" +authors = ["Sebastian Dröge ", "Simon Wülker "] +description = "Rust bindings for GStreamer MSE library" +license = "MIT OR Apache-2.0" +readme = "README.md" +documentation = "https://gstreamer.pages.freedesktop.org/gstreamer-rs/stable/latest/docs/gstreamer_mse/" +keywords = ["gstreamer", "multimedia", "audio", "video", "gnome"] +version.workspace = true +categories.workspace = true +repository.workspace = true +homepage.workspace = true +edition.workspace = true +rust-version.workspace = true + +[dependencies] +gstreamer-mse-sys.workspace = true +gst = { workspace = true, features = ["v1_26"] } +glib.workspace = true + +[package.metadata.docs.rs] +all-features = true +rustc-args = ["--cfg", "docsrs"] +rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] + diff --git a/gstreamer-mse/Gir.toml b/gstreamer-mse/Gir.toml new file mode 100644 index 000000000..f1b2fcc5f --- /dev/null +++ b/gstreamer-mse/Gir.toml @@ -0,0 +1,59 @@ +[options] +girs_directories = ["../gir-files", "../gst-gir-files"] +library = "GstMse" +version = "1.0" +min_cfg_version = "1.26" +work_mode = "normal" +target_path = "." +concurrency = "send+sync" +generate_safety_asserts = true +single_version_file = true +generate_display_trait = false +deprecate_by_min_version = true +trust_return_value_nullability = true + +generate = [ + "GstMse.MediaSourceEOSError", + "GstMse.MediaSourceError", + "GstMse.MediaSourceReadyState", + "GstMse.MseSrc", + "GstMse.MseSrcPad", + "GstMse.MseSrcReadyState", + "GstMse.SourceBuffer", + "GstMse.SourceBufferAppendMode", + "GstMse.SourceBufferList", +] + +manual = [ + "GstMse.MediaSourceRange", + "GstMse.SourceBufferInterval", + "Gst.Element", + "Gst.Pad", +] + +[[object]] +name = "Gst.Buffer" +status = "manual" +ref_mode = "ref" + +[[object]] +name = "Gst.ClockTime" +status = "manual" +conversion_type = "Option" + +[[object]] +name = "GLib.Error" +status = "manual" + +[[object]] +name = "Gst.Object" +status = "manual" +ref_mode = "ref" + +[[object]] +name = "GstMse.MediaSource" +status = "generate" + [[object.property]] + name = "position" + # pass GstClockTime instead of u64 for setter + generate = ["notify"] diff --git a/gstreamer-mse/LICENSE-APACHE b/gstreamer-mse/LICENSE-APACHE new file mode 120000 index 000000000..965b606f3 --- /dev/null +++ b/gstreamer-mse/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/gstreamer-mse/LICENSE-MIT b/gstreamer-mse/LICENSE-MIT new file mode 120000 index 000000000..76219eb72 --- /dev/null +++ b/gstreamer-mse/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/gstreamer-mse/README.md b/gstreamer-mse/README.md new file mode 100644 index 000000000..adb1c1b3e --- /dev/null +++ b/gstreamer-mse/README.md @@ -0,0 +1,220 @@ +# gstreamer-rs [![crates.io](https://img.shields.io/crates/v/gstreamer-mse.svg)](https://crates.io/crates/gstreamer-mse) [![pipeline status](https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/badges/main/pipeline.svg)](https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/commits/main) + +[GStreamer](https://gstreamer.freedesktop.org/) (MSE library) bindings for Rust. +Documentation can be found [here](https://gstreamer.pages.freedesktop.org/gstreamer-rs/stable/latest/docs/gstreamer_mse/). + +These bindings are providing a safe API that can be used to interface with +GStreamer, e.g. for writing GStreamer-based applications and GStreamer plugins. + +The bindings are mostly autogenerated with [gir](https://github.com/gtk-rs/gir/) +based on the [GObject-Introspection](https://wiki.gnome.org/Projects/GObjectIntrospection/) +API metadata provided by the GStreamer project. + +## Table of Contents +- [gstreamer-rs ](#gstreamer-rs--) + - [Table of Contents](#table-of-contents) + - [Installation](#installation) + - [Linux/BSDs](#linuxbsds) + - [macOS](#macos) + - [GStreamer Binaries](#gstreamer-binaries) + - [Homebrew](#homebrew) + - [Windows](#windows) + - [GStreamer Binaries](#gstreamer-binaries-1) + - [MSYS2 / pacman](#msys2--pacman) + - [Getting Started](#getting-started) + - [LICENSE](#license) + - [Contribution](#contribution) + + + +## Installation + +To build the GStreamer bindings or anything depending on them, you need to +have at least GStreamer 1.14 and gst-plugins-base 1.14 installed. In addition, +some of the examples/tutorials require various GStreamer plugins to be +available, which can be found in gst-plugins-base, gst-plugins-good, +gst-plugins-bad, gst-plugins-ugly and/or gst-libav. + + + +### Linux/BSDs + +You need to install the above mentioned packages with your distributions +package manager, or build them from source. + +On Debian/Ubuntu they can be installed with + +```console +$ apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \ + gstreamer1.0-plugins-base gstreamer1.0-plugins-good \ + gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly \ + gstreamer1.0-libav libgstrtspserver-1.0-dev libges-1.0-dev +``` + +The minimum required version of the above libraries is >= 1.14. If you +build the gstreamer-player sub-crate, or any of the examples that +depend on gstreamer-player, you must ensure that in addition to the above +packages, `libgstreamer-plugins-bad1.0-dev` is installed. See the `Cargo.toml` +files for the full details, + +```console +$ apt-get install libgstreamer-plugins-bad1.0-dev +``` + +Package names on other distributions should be similar. +Please submit a pull request with instructions for yours. + + + +### macOS + +You can install GStreamer and the plugins via [Homebrew](https://brew.sh/) or +by installing the [binaries](https://gstreamer.freedesktop.org/data/pkg/osx/) +provided by the GStreamer project. + +We recommend using the official GStreamer binaries over Homebrew, especially +as GStreamer in Homebrew is [currently broken](https://github.com/orgs/Homebrew/discussions/3740#discussioncomment-3804964). + +#### GStreamer Binaries + +You need to download the *two* `.pkg` files from the GStreamer website and +install them, e.g. `gstreamer-1.0-1.20.4-universal.pkg` and +`gstreamer-1.0-devel-1.20.4-universal.pkg`. + +After installation, you also need to set the `PATH` environment variable as +follows + +```console +$ export PATH="/Library/Frameworks/GStreamer.framework/Versions/1.0/bin${PATH:+:$PATH}" +``` + +Also note that the `pkg-config` from GStreamer should be the first one in +the `PATH` as other versions have all kinds of quirks that will cause +problems. + +#### Homebrew + +Homebrew only installs various plugins if explicitly enabled, so some extra +`--with-*` flags may be required. + +```console +$ brew install gstreamer gst-plugins-base gst-plugins-good \ + gst-plugins-bad gst-plugins-ugly gst-libav gst-rtsp-server \ + gst-editing-services --with-orc --with-libogg --with-opus \ + --with-pango --with-theora --with-libvorbis --with-libvpx \ + --enable-gtk3 +``` + +Make sure the version of these libraries is >= 1.14. + + + +### Windows + +You can install GStreamer and the plugins via [MSYS2](http://www.msys2.org/) +with `pacman` or by installing the +[binaries](https://gstreamer.freedesktop.org/data/pkg/windows/) provided by +the GStreamer project. + +We recommend using the official GStreamer binaries over MSYS2. + +#### GStreamer Binaries + +You need to download the *two* `.msi` files for your platform from the +GStreamer website and install them, e.g. `gstreamer-1.0-x86_64-1.20.4.msi` and +`gstreamer-1.0-devel-x86_64-1.20.4.msi`. Make sure to select the version that +matches your Rust toolchain, i.e. MinGW or MSVC. + +After installation set the ``PATH` environment variable as follows: + +```console +# For a UNIX-style shell: +$ export PATH="c:/gstreamer/1.0/msvc_x86_64/bin${PATH:+:$PATH}" + +# For cmd.exe: +$ set PATH=C:\gstreamer\1.0\msvc_x86_64\bin;%PATH% +``` + +Make sure to update the path to where you have actually installed GStreamer +and for the corresponding toolchain. + +Also note that the `pkg-config.exe` from GStreamer should be the first one in +the `PATH` as other versions have all kinds of quirks that will cause +problems. + +#### MSYS2 / pacman + +```console +$ pacman -S glib2-devel pkg-config \ + mingw-w64-x86_64-gstreamer mingw-w64-x86_64-gst-plugins-base \ + mingw-w64-x86_64-gst-plugins-good mingw-w64-x86_64-gst-plugins-bad \ + mingw-w64-x86_64-gst-plugins-ugly mingw-w64-x86_64-gst-libav \ + mingw-w64-x86_64-gst-rtsp-server +``` + +Make sure the version of these libraries is >= 1.14. + +Note that the version of `pkg-config` included in `MSYS2` is +[known to have problems](https://github.com/rust-lang/pkg-config-rs/issues/51#issuecomment-346300858) +compiling GStreamer, so you may need to install another version. One option +would be [`pkg-config-lite`](https://sourceforge.net/projects/pkgconfiglite/). + + + +## Getting Started + +The API reference can be found +[here](https://gstreamer.pages.freedesktop.org/gstreamer-rs/stable/latest/docs/gstreamer/), however it is +only the Rust API reference and does not explain any of the concepts. + +For getting started with GStreamer development, the best would be to follow +the [documentation](https://gstreamer.freedesktop.org/documentation/) on the +GStreamer website, especially the [Application Development +Manual](https://gstreamer.freedesktop.org/documentation/application-development/). +While being C-centric, it explains all the fundamental concepts of GStreamer +and the code examples should be relatively easily translatable to Rust. The +API is basically the same, function/struct names are the same and everything +is only more convenient (hopefully) and safer. + +In addition there are +[tutorials](https://gstreamer.freedesktop.org/documentation/tutorials/) on the +GStreamer website. Many of them were ported to Rust already and the code can +be found in the +[tutorials](https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/tree/main/tutorials) +directory. + +Some further examples for various aspects of GStreamer and how to use it from +Rust can be found in the +[examples](https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/tree/main/examples) +directory. + +Various GStreamer plugins written in Rust can be found in the +[gst-plugins-rs](https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs) +repository. + + + +## LICENSE + +gstreamer-rs and all crates contained in here are licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or + http://opensource.org/licenses/MIT) + +at your option. + +GStreamer itself is licensed under the Lesser General Public License version +2.1 or (at your option) any later version: +https://www.gnu.org/licenses/lgpl-2.1.html + + + +## Contribution + +Any kinds of contributions are welcome as a pull request. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in gstreamer-rs by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. diff --git a/gstreamer-mse/src/auto/media_source.rs b/gstreamer-mse/src/auto/media_source.rs new file mode 100644 index 000000000..bd70f789d --- /dev/null +++ b/gstreamer-mse/src/auto/media_source.rs @@ -0,0 +1,454 @@ +// This file was generated by gir (https://github.com/gtk-rs/gir) +// from gir-files (https://github.com/gtk-rs/gir-files) +// from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git) +// DO NOT EDIT + +use crate::{ + ffi, MediaSourceEOSError, MediaSourceRange, MediaSourceReadyState, MseSrc, SourceBuffer, + SourceBufferList, +}; +use glib::{ + object::ObjectType as _, + prelude::*, + signal::{connect_raw, SignalHandlerId}, + translate::*, +}; +use std::boxed::Box as Box_; + +glib::wrapper! { + #[doc(alias = "GstMediaSource")] + pub struct MediaSource(Object) @extends gst::Object; + + match fn { + type_ => || ffi::gst_media_source_get_type(), + } +} + +impl MediaSource { + #[doc(alias = "gst_media_source_new")] + pub fn new() -> MediaSource { + assert_initialized_main_thread!(); + unsafe { from_glib_full(ffi::gst_media_source_new()) } + } + + #[doc(alias = "gst_media_source_add_source_buffer")] + pub fn add_source_buffer(&self, type_: &str) -> Result { + unsafe { + let mut error = std::ptr::null_mut(); + let ret = ffi::gst_media_source_add_source_buffer( + self.to_glib_none().0, + type_.to_glib_none().0, + &mut error, + ); + if error.is_null() { + Ok(from_glib_full(ret)) + } else { + Err(from_glib_full(error)) + } + } + } + + #[doc(alias = "gst_media_source_attach")] + pub fn attach(&self, element: &MseSrc) { + unsafe { + ffi::gst_media_source_attach(self.to_glib_none().0, element.to_glib_none().0); + } + } + + #[doc(alias = "gst_media_source_clear_live_seekable_range")] + pub fn clear_live_seekable_range(&self) -> Result<(), glib::Error> { + unsafe { + let mut error = std::ptr::null_mut(); + let is_ok = + ffi::gst_media_source_clear_live_seekable_range(self.to_glib_none().0, &mut error); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } + } + + #[doc(alias = "gst_media_source_detach")] + pub fn detach(&self) { + unsafe { + ffi::gst_media_source_detach(self.to_glib_none().0); + } + } + + #[doc(alias = "gst_media_source_end_of_stream")] + pub fn end_of_stream(&self, eos_error: MediaSourceEOSError) -> Result<(), glib::Error> { + unsafe { + let mut error = std::ptr::null_mut(); + let is_ok = ffi::gst_media_source_end_of_stream( + self.to_glib_none().0, + eos_error.into_glib(), + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } + } + + #[doc(alias = "gst_media_source_get_active_source_buffers")] + #[doc(alias = "get_active_source_buffers")] + #[doc(alias = "active-source-buffers")] + pub fn active_source_buffers(&self) -> SourceBufferList { + unsafe { + from_glib_full(ffi::gst_media_source_get_active_source_buffers( + self.to_glib_none().0, + )) + } + } + + #[doc(alias = "gst_media_source_get_duration")] + #[doc(alias = "get_duration")] + pub fn duration(&self) -> Option { + unsafe { from_glib(ffi::gst_media_source_get_duration(self.to_glib_none().0)) } + } + + #[doc(alias = "gst_media_source_get_live_seekable_range")] + #[doc(alias = "get_live_seekable_range")] + pub fn live_seekable_range(&self) -> MediaSourceRange { + unsafe { + let mut range = MediaSourceRange::uninitialized(); + ffi::gst_media_source_get_live_seekable_range( + self.to_glib_none().0, + range.to_glib_none_mut().0, + ); + range + } + } + + #[doc(alias = "gst_media_source_get_position")] + #[doc(alias = "get_position")] + pub fn position(&self) -> Option { + unsafe { from_glib(ffi::gst_media_source_get_position(self.to_glib_none().0)) } + } + + #[doc(alias = "gst_media_source_get_ready_state")] + #[doc(alias = "get_ready_state")] + #[doc(alias = "ready-state")] + pub fn ready_state(&self) -> MediaSourceReadyState { + unsafe { from_glib(ffi::gst_media_source_get_ready_state(self.to_glib_none().0)) } + } + + #[doc(alias = "gst_media_source_get_source_buffers")] + #[doc(alias = "get_source_buffers")] + #[doc(alias = "source-buffers")] + pub fn source_buffers(&self) -> SourceBufferList { + unsafe { + from_glib_full(ffi::gst_media_source_get_source_buffers( + self.to_glib_none().0, + )) + } + } + + #[doc(alias = "gst_media_source_remove_source_buffer")] + pub fn remove_source_buffer(&self, buffer: &SourceBuffer) -> Result<(), glib::Error> { + unsafe { + let mut error = std::ptr::null_mut(); + let is_ok = ffi::gst_media_source_remove_source_buffer( + self.to_glib_none().0, + buffer.to_glib_none().0, + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } + } + + #[doc(alias = "gst_media_source_set_duration")] + #[doc(alias = "duration")] + pub fn set_duration( + &self, + duration: impl Into>, + ) -> Result<(), glib::Error> { + unsafe { + let mut error = std::ptr::null_mut(); + let is_ok = ffi::gst_media_source_set_duration( + self.to_glib_none().0, + duration.into().into_glib(), + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } + } + + #[doc(alias = "gst_media_source_set_live_seekable_range")] + pub fn set_live_seekable_range( + &self, + start: impl Into>, + end: impl Into>, + ) -> Result<(), glib::Error> { + unsafe { + let mut error = std::ptr::null_mut(); + let is_ok = ffi::gst_media_source_set_live_seekable_range( + self.to_glib_none().0, + start.into().into_glib(), + end.into().into_glib(), + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } + } + + #[doc(alias = "gst_media_source_is_type_supported")] + pub fn is_type_supported(type_: &str) -> bool { + assert_initialized_main_thread!(); + unsafe { + from_glib(ffi::gst_media_source_is_type_supported( + type_.to_glib_none().0, + )) + } + } + + #[doc(alias = "on-source-close")] + pub fn connect_on_source_close( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn on_source_close_trampoline< + F: Fn(&MediaSource) + Send + Sync + 'static, + >( + this: *mut ffi::GstMediaSource, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"on-source-close".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + on_source_close_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "on-source-ended")] + pub fn connect_on_source_ended( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn on_source_ended_trampoline< + F: Fn(&MediaSource) + Send + Sync + 'static, + >( + this: *mut ffi::GstMediaSource, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"on-source-ended".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + on_source_ended_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "on-source-open")] + pub fn connect_on_source_open( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn on_source_open_trampoline< + F: Fn(&MediaSource) + Send + Sync + 'static, + >( + this: *mut ffi::GstMediaSource, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"on-source-open".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + on_source_open_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "active-source-buffers")] + pub fn connect_active_source_buffers_notify( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn notify_active_source_buffers_trampoline< + F: Fn(&MediaSource) + Send + Sync + 'static, + >( + this: *mut ffi::GstMediaSource, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"notify::active-source-buffers".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + notify_active_source_buffers_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "duration")] + pub fn connect_duration_notify( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn notify_duration_trampoline< + F: Fn(&MediaSource) + Send + Sync + 'static, + >( + this: *mut ffi::GstMediaSource, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"notify::duration".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + notify_duration_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "position")] + pub fn connect_position_notify( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn notify_position_trampoline< + F: Fn(&MediaSource) + Send + Sync + 'static, + >( + this: *mut ffi::GstMediaSource, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"notify::position".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + notify_position_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "ready-state")] + pub fn connect_ready_state_notify( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn notify_ready_state_trampoline< + F: Fn(&MediaSource) + Send + Sync + 'static, + >( + this: *mut ffi::GstMediaSource, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"notify::ready-state".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + notify_ready_state_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "source-buffers")] + pub fn connect_source_buffers_notify( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn notify_source_buffers_trampoline< + F: Fn(&MediaSource) + Send + Sync + 'static, + >( + this: *mut ffi::GstMediaSource, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"notify::source-buffers".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + notify_source_buffers_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } +} + +impl Default for MediaSource { + fn default() -> Self { + Self::new() + } +} + +unsafe impl Send for MediaSource {} +unsafe impl Sync for MediaSource {} diff --git a/gstreamer-mse/src/auto/mod.rs b/gstreamer-mse/src/auto/mod.rs new file mode 100644 index 000000000..7900847b9 --- /dev/null +++ b/gstreamer-mse/src/auto/mod.rs @@ -0,0 +1,26 @@ +// This file was generated by gir (https://github.com/gtk-rs/gir) +// from gir-files (https://github.com/gtk-rs/gir-files) +// from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git) +// DO NOT EDIT + +mod media_source; +pub use self::media_source::MediaSource; + +mod mse_src; +pub use self::mse_src::MseSrc; + +mod mse_src_pad; +pub use self::mse_src_pad::MseSrcPad; + +mod source_buffer; +pub use self::source_buffer::SourceBuffer; + +mod source_buffer_list; +pub use self::source_buffer_list::SourceBufferList; + +mod enums; +pub use self::enums::MediaSourceEOSError; +pub use self::enums::MediaSourceError; +pub use self::enums::MediaSourceReadyState; +pub use self::enums::MseSrcReadyState; +pub use self::enums::SourceBufferAppendMode; diff --git a/gstreamer-mse/src/auto/mse_src.rs b/gstreamer-mse/src/auto/mse_src.rs new file mode 100644 index 000000000..886b54384 --- /dev/null +++ b/gstreamer-mse/src/auto/mse_src.rs @@ -0,0 +1,228 @@ +// This file was generated by gir (https://github.com/gtk-rs/gir) +// from gir-files (https://github.com/gtk-rs/gir-files) +// from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git) +// DO NOT EDIT + +use crate::{ffi, MseSrcReadyState}; +use glib::{ + prelude::*, + signal::{connect_raw, SignalHandlerId}, + translate::*, +}; +use std::boxed::Box as Box_; + +glib::wrapper! { + #[doc(alias = "GstMseSrc")] + pub struct MseSrc(Object) @extends gst::Element, gst::Object; + + match fn { + type_ => || ffi::gst_mse_src_get_type(), + } +} + +impl MseSrc { + #[doc(alias = "gst_mse_src_get_duration")] + #[doc(alias = "get_duration")] + pub fn duration(&self) -> Option { + unsafe { from_glib(ffi::gst_mse_src_get_duration(self.to_glib_none().0)) } + } + + #[doc(alias = "gst_mse_src_get_n_audio")] + #[doc(alias = "get_n_audio")] + #[doc(alias = "n-audio")] + pub fn n_audio(&self) -> u32 { + unsafe { ffi::gst_mse_src_get_n_audio(self.to_glib_none().0) } + } + + #[doc(alias = "gst_mse_src_get_n_text")] + #[doc(alias = "get_n_text")] + #[doc(alias = "n-text")] + pub fn n_text(&self) -> u32 { + unsafe { ffi::gst_mse_src_get_n_text(self.to_glib_none().0) } + } + + #[doc(alias = "gst_mse_src_get_n_video")] + #[doc(alias = "get_n_video")] + #[doc(alias = "n-video")] + pub fn n_video(&self) -> u32 { + unsafe { ffi::gst_mse_src_get_n_video(self.to_glib_none().0) } + } + + #[doc(alias = "gst_mse_src_get_position")] + #[doc(alias = "get_position")] + pub fn position(&self) -> Option { + unsafe { from_glib(ffi::gst_mse_src_get_position(self.to_glib_none().0)) } + } + + #[doc(alias = "gst_mse_src_get_ready_state")] + #[doc(alias = "get_ready_state")] + #[doc(alias = "ready-state")] + pub fn ready_state(&self) -> MseSrcReadyState { + unsafe { from_glib(ffi::gst_mse_src_get_ready_state(self.to_glib_none().0)) } + } + + pub fn set_duration(&self, duration: u64) { + ObjectExt::set_property(self, "duration", duration) + } + + #[doc(alias = "duration")] + pub fn connect_duration_notify( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn notify_duration_trampoline( + this: *mut ffi::GstMseSrc, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"notify::duration".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + notify_duration_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "n-audio")] + pub fn connect_n_audio_notify( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn notify_n_audio_trampoline( + this: *mut ffi::GstMseSrc, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"notify::n-audio".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + notify_n_audio_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "n-text")] + pub fn connect_n_text_notify( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn notify_n_text_trampoline( + this: *mut ffi::GstMseSrc, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"notify::n-text".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + notify_n_text_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "n-video")] + pub fn connect_n_video_notify( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn notify_n_video_trampoline( + this: *mut ffi::GstMseSrc, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"notify::n-video".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + notify_n_video_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "position")] + pub fn connect_position_notify( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn notify_position_trampoline( + this: *mut ffi::GstMseSrc, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"notify::position".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + notify_position_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "ready-state")] + pub fn connect_ready_state_notify( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn notify_ready_state_trampoline< + F: Fn(&MseSrc) + Send + Sync + 'static, + >( + this: *mut ffi::GstMseSrc, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"notify::ready-state".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + notify_ready_state_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } +} + +unsafe impl Send for MseSrc {} +unsafe impl Sync for MseSrc {} diff --git a/gstreamer-mse/src/auto/mse_src_pad.rs b/gstreamer-mse/src/auto/mse_src_pad.rs new file mode 100644 index 000000000..cc85a2baa --- /dev/null +++ b/gstreamer-mse/src/auto/mse_src_pad.rs @@ -0,0 +1,20 @@ +// This file was generated by gir (https://github.com/gtk-rs/gir) +// from gir-files (https://github.com/gtk-rs/gir-files) +// from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git) +// DO NOT EDIT + +use crate::ffi; + +glib::wrapper! { + #[doc(alias = "GstMseSrcPad")] + pub struct MseSrcPad(Object) @extends gst::Pad, gst::Object; + + match fn { + type_ => || ffi::gst_mse_src_pad_get_type(), + } +} + +impl MseSrcPad {} + +unsafe impl Send for MseSrcPad {} +unsafe impl Sync for MseSrcPad {} diff --git a/gstreamer-mse/src/auto/source_buffer.rs b/gstreamer-mse/src/auto/source_buffer.rs new file mode 100644 index 000000000..0f020c4f0 --- /dev/null +++ b/gstreamer-mse/src/auto/source_buffer.rs @@ -0,0 +1,572 @@ +// This file was generated by gir (https://github.com/gtk-rs/gir) +// from gir-files (https://github.com/gtk-rs/gir-files) +// from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git) +// DO NOT EDIT + +use crate::{ffi, SourceBufferAppendMode}; +use glib::{ + object::ObjectType as _, + prelude::*, + signal::{connect_raw, SignalHandlerId}, + translate::*, +}; +use std::boxed::Box as Box_; + +glib::wrapper! { + #[doc(alias = "GstSourceBuffer")] + pub struct SourceBuffer(Object) @extends gst::Object; + + match fn { + type_ => || ffi::gst_source_buffer_get_type(), + } +} + +impl SourceBuffer { + #[doc(alias = "gst_source_buffer_abort")] + pub fn abort(&self) -> Result<(), glib::Error> { + unsafe { + let mut error = std::ptr::null_mut(); + let is_ok = ffi::gst_source_buffer_abort(self.to_glib_none().0, &mut error); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } + } + + #[doc(alias = "gst_source_buffer_append_buffer")] + pub fn append_buffer(&self, buf: &gst::Buffer) -> Result<(), glib::Error> { + unsafe { + let mut error = std::ptr::null_mut(); + let is_ok = ffi::gst_source_buffer_append_buffer( + self.to_glib_none().0, + buf.to_glib_none().0, + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } + } + + #[doc(alias = "gst_source_buffer_change_content_type")] + pub fn change_content_type(&self, type_: &str) -> Result<(), glib::Error> { + unsafe { + let mut error = std::ptr::null_mut(); + let is_ok = ffi::gst_source_buffer_change_content_type( + self.to_glib_none().0, + type_.to_glib_none().0, + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } + } + + #[doc(alias = "gst_source_buffer_get_append_mode")] + #[doc(alias = "get_append_mode")] + #[doc(alias = "append-mode")] + pub fn append_mode(&self) -> SourceBufferAppendMode { + unsafe { + from_glib(ffi::gst_source_buffer_get_append_mode( + self.to_glib_none().0, + )) + } + } + + #[doc(alias = "gst_source_buffer_get_append_window_end")] + #[doc(alias = "get_append_window_end")] + #[doc(alias = "append-window-end")] + pub fn append_window_end(&self) -> Option { + unsafe { + from_glib(ffi::gst_source_buffer_get_append_window_end( + self.to_glib_none().0, + )) + } + } + + #[doc(alias = "gst_source_buffer_get_append_window_start")] + #[doc(alias = "get_append_window_start")] + #[doc(alias = "append-window-start")] + pub fn append_window_start(&self) -> Option { + unsafe { + from_glib(ffi::gst_source_buffer_get_append_window_start( + self.to_glib_none().0, + )) + } + } + + //#[doc(alias = "gst_source_buffer_get_buffered")] + //#[doc(alias = "get_buffered")] + //pub fn buffered(&self) -> Result { + // unsafe { TODO: call ffi:gst_source_buffer_get_buffered() } + //} + + #[doc(alias = "gst_source_buffer_get_content_type")] + #[doc(alias = "get_content_type")] + #[doc(alias = "content-type")] + pub fn content_type(&self) -> glib::GString { + unsafe { + from_glib_full(ffi::gst_source_buffer_get_content_type( + self.to_glib_none().0, + )) + } + } + + #[doc(alias = "gst_source_buffer_get_timestamp_offset")] + #[doc(alias = "get_timestamp_offset")] + #[doc(alias = "timestamp-offset")] + pub fn timestamp_offset(&self) -> Option { + unsafe { + from_glib(ffi::gst_source_buffer_get_timestamp_offset( + self.to_glib_none().0, + )) + } + } + + #[doc(alias = "gst_source_buffer_get_updating")] + #[doc(alias = "get_updating")] + #[doc(alias = "updating")] + pub fn is_updating(&self) -> bool { + unsafe { from_glib(ffi::gst_source_buffer_get_updating(self.to_glib_none().0)) } + } + + #[doc(alias = "gst_source_buffer_remove")] + pub fn remove( + &self, + start: impl Into>, + end: impl Into>, + ) -> Result<(), glib::Error> { + unsafe { + let mut error = std::ptr::null_mut(); + let is_ok = ffi::gst_source_buffer_remove( + self.to_glib_none().0, + start.into().into_glib(), + end.into().into_glib(), + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } + } + + #[doc(alias = "gst_source_buffer_set_append_mode")] + #[doc(alias = "append-mode")] + pub fn set_append_mode(&self, mode: SourceBufferAppendMode) -> Result<(), glib::Error> { + unsafe { + let mut error = std::ptr::null_mut(); + let is_ok = ffi::gst_source_buffer_set_append_mode( + self.to_glib_none().0, + mode.into_glib(), + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } + } + + #[doc(alias = "gst_source_buffer_set_append_window_end")] + pub fn set_append_window_end( + &self, + end: impl Into>, + ) -> Result<(), glib::Error> { + unsafe { + let mut error = std::ptr::null_mut(); + let is_ok = ffi::gst_source_buffer_set_append_window_end( + self.to_glib_none().0, + end.into().into_glib(), + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } + } + + #[doc(alias = "gst_source_buffer_set_append_window_start")] + pub fn set_append_window_start( + &self, + start: impl Into>, + ) -> Result<(), glib::Error> { + unsafe { + let mut error = std::ptr::null_mut(); + let is_ok = ffi::gst_source_buffer_set_append_window_start( + self.to_glib_none().0, + start.into().into_glib(), + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } + } + + #[doc(alias = "gst_source_buffer_set_timestamp_offset")] + #[doc(alias = "timestamp-offset")] + pub fn set_timestamp_offset( + &self, + offset: impl Into>, + ) -> Result<(), glib::Error> { + unsafe { + let mut error = std::ptr::null_mut(); + let is_ok = ffi::gst_source_buffer_set_timestamp_offset( + self.to_glib_none().0, + offset.into().into_glib(), + &mut error, + ); + debug_assert_eq!(is_ok == glib::ffi::GFALSE, !error.is_null()); + if error.is_null() { + Ok(()) + } else { + Err(from_glib_full(error)) + } + } + } + + #[doc(alias = "content-type")] + pub fn set_content_type(&self, content_type: Option<&str>) { + ObjectExt::set_property(self, "content-type", content_type) + } + + #[doc(alias = "on-abort")] + pub fn connect_on_abort(&self, f: F) -> SignalHandlerId { + unsafe extern "C" fn on_abort_trampoline( + this: *mut ffi::GstSourceBuffer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"on-abort".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + on_abort_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "on-error")] + pub fn connect_on_error(&self, f: F) -> SignalHandlerId { + unsafe extern "C" fn on_error_trampoline( + this: *mut ffi::GstSourceBuffer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"on-error".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + on_error_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "on-update")] + pub fn connect_on_update(&self, f: F) -> SignalHandlerId { + unsafe extern "C" fn on_update_trampoline( + this: *mut ffi::GstSourceBuffer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"on-update".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + on_update_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "on-update-end")] + pub fn connect_on_update_end( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn on_update_end_trampoline< + F: Fn(&SourceBuffer) + Send + Sync + 'static, + >( + this: *mut ffi::GstSourceBuffer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"on-update-end".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + on_update_end_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "on-update-start")] + pub fn connect_on_update_start( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn on_update_start_trampoline< + F: Fn(&SourceBuffer) + Send + Sync + 'static, + >( + this: *mut ffi::GstSourceBuffer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"on-update-start".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + on_update_start_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "append-mode")] + pub fn connect_append_mode_notify( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn notify_append_mode_trampoline< + F: Fn(&SourceBuffer) + Send + Sync + 'static, + >( + this: *mut ffi::GstSourceBuffer, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"notify::append-mode".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + notify_append_mode_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "append-window-end")] + pub fn connect_append_window_end_notify( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn notify_append_window_end_trampoline< + F: Fn(&SourceBuffer) + Send + Sync + 'static, + >( + this: *mut ffi::GstSourceBuffer, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"notify::append-window-end".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + notify_append_window_end_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "append-window-start")] + pub fn connect_append_window_start_notify( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn notify_append_window_start_trampoline< + F: Fn(&SourceBuffer) + Send + Sync + 'static, + >( + this: *mut ffi::GstSourceBuffer, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"notify::append-window-start".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + notify_append_window_start_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "buffered")] + pub fn connect_buffered_notify( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn notify_buffered_trampoline< + F: Fn(&SourceBuffer) + Send + Sync + 'static, + >( + this: *mut ffi::GstSourceBuffer, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"notify::buffered".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + notify_buffered_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "content-type")] + pub fn connect_content_type_notify( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn notify_content_type_trampoline< + F: Fn(&SourceBuffer) + Send + Sync + 'static, + >( + this: *mut ffi::GstSourceBuffer, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"notify::content-type".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + notify_content_type_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "timestamp-offset")] + pub fn connect_timestamp_offset_notify( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn notify_timestamp_offset_trampoline< + F: Fn(&SourceBuffer) + Send + Sync + 'static, + >( + this: *mut ffi::GstSourceBuffer, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"notify::timestamp-offset".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + notify_timestamp_offset_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "updating")] + pub fn connect_updating_notify( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn notify_updating_trampoline< + F: Fn(&SourceBuffer) + Send + Sync + 'static, + >( + this: *mut ffi::GstSourceBuffer, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"notify::updating".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + notify_updating_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } +} + +unsafe impl Send for SourceBuffer {} +unsafe impl Sync for SourceBuffer {} diff --git a/gstreamer-mse/src/auto/source_buffer_list.rs b/gstreamer-mse/src/auto/source_buffer_list.rs new file mode 100644 index 000000000..055072757 --- /dev/null +++ b/gstreamer-mse/src/auto/source_buffer_list.rs @@ -0,0 +1,125 @@ +// This file was generated by gir (https://github.com/gtk-rs/gir) +// from gir-files (https://github.com/gtk-rs/gir-files) +// from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git) +// DO NOT EDIT + +use crate::{ffi, SourceBuffer}; +use glib::{ + object::ObjectType as _, + prelude::*, + signal::{connect_raw, SignalHandlerId}, + translate::*, +}; +use std::boxed::Box as Box_; + +glib::wrapper! { + #[doc(alias = "GstSourceBufferList")] + pub struct SourceBufferList(Object) @extends gst::Object; + + match fn { + type_ => || ffi::gst_source_buffer_list_get_type(), + } +} + +impl SourceBufferList { + #[doc(alias = "gst_source_buffer_list_get_length")] + #[doc(alias = "get_length")] + pub fn length(&self) -> u32 { + unsafe { ffi::gst_source_buffer_list_get_length(self.to_glib_none().0) } + } + + #[doc(alias = "gst_source_buffer_list_index")] + pub fn index(&self, index: u32) -> Option { + unsafe { + from_glib_full(ffi::gst_source_buffer_list_index( + self.to_glib_none().0, + index, + )) + } + } + + #[doc(alias = "on-sourcebuffer-added")] + pub fn connect_on_sourcebuffer_added( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn on_sourcebuffer_added_trampoline< + F: Fn(&SourceBufferList) + Send + Sync + 'static, + >( + this: *mut ffi::GstSourceBufferList, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"on-sourcebuffer-added".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + on_sourcebuffer_added_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "on-sourcebuffer-removed")] + pub fn connect_on_sourcebuffer_removed( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn on_sourcebuffer_removed_trampoline< + F: Fn(&SourceBufferList) + Send + Sync + 'static, + >( + this: *mut ffi::GstSourceBufferList, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"on-sourcebuffer-removed".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + on_sourcebuffer_removed_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "length")] + pub fn connect_length_notify( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn notify_length_trampoline< + F: Fn(&SourceBufferList) + Send + Sync + 'static, + >( + this: *mut ffi::GstSourceBufferList, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + c"notify::length".as_ptr() as *const _, + Some(std::mem::transmute::<*const (), unsafe extern "C" fn()>( + notify_length_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } +} + +unsafe impl Send for SourceBufferList {} +unsafe impl Sync for SourceBufferList {} diff --git a/gstreamer-mse/src/auto/versions.txt b/gstreamer-mse/src/auto/versions.txt new file mode 100644 index 000000000..5cb83d42b --- /dev/null +++ b/gstreamer-mse/src/auto/versions.txt @@ -0,0 +1,3 @@ +Generated by gir (https://github.com/gtk-rs/gir @ 491114ad76bd) +from gir-files (https://github.com/gtk-rs/gir-files @ 56728a5eb215) +from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ ce667eb782d7) diff --git a/gstreamer-mse/src/lib.rs b/gstreamer-mse/src/lib.rs new file mode 100644 index 000000000..e673b0e16 --- /dev/null +++ b/gstreamer-mse/src/lib.rs @@ -0,0 +1,33 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +#![cfg_attr(docsrs, feature(doc_cfg))] +#![allow(clippy::missing_safety_doc)] +#![allow(clippy::manual_c_str_literals)] +#![doc = include_str!("../README.md")] + +pub use gstreamer_mse_sys as ffi; + +macro_rules! assert_initialized_main_thread { + () => { + if !gst::INITIALIZED.load(std::sync::atomic::Ordering::SeqCst) { + gst::assert_initialized(); + } + }; +} + +macro_rules! skip_assert_initialized { + () => {}; +} + +#[allow(unused_imports)] +mod auto; +#[allow(unused_imports)] +pub use auto::*; + +mod source_buffer_interval; +pub use source_buffer_interval::SourceBufferInterval; + +mod media_source; + +mod media_source_range; +pub use media_source_range::MediaSourceRange; diff --git a/gstreamer-mse/src/media_source.rs b/gstreamer-mse/src/media_source.rs new file mode 100644 index 000000000..bee34e334 --- /dev/null +++ b/gstreamer-mse/src/media_source.rs @@ -0,0 +1,12 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use glib::prelude::*; +use glib::translate::IntoGlib; + +use crate::MediaSource; + +impl MediaSource { + pub fn set_position(&self, position: impl Into>) { + ObjectExt::set_property(self, "position", position.into().into_glib()) + } +} diff --git a/gstreamer-mse/src/media_source_range.rs b/gstreamer-mse/src/media_source_range.rs new file mode 100644 index 000000000..c5083a552 --- /dev/null +++ b/gstreamer-mse/src/media_source_range.rs @@ -0,0 +1,61 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use std::fmt; + +use crate::ffi; + +glib::wrapper! { + #[doc(alias = "GstMediaSourceRange")] + pub struct MediaSourceRange(BoxedInline); + + match fn {} +} + +impl MediaSourceRange { + pub fn new(start: gst::ClockTime, end: gst::ClockTime) -> Self { + skip_assert_initialized!(); + + let inner = ffi::GstMediaSourceRange { + start: start.nseconds(), + end: end.nseconds(), + }; + + Self { inner } + } + + pub fn start(&self) -> gst::ClockTime { + gst::ClockTime::from_nseconds(self.inner.start) + } + + pub fn set_start(&mut self, start: gst::ClockTime) { + self.inner.start = start.nseconds(); + } + + pub fn end(&self) -> gst::ClockTime { + gst::ClockTime::from_nseconds(self.inner.end) + } + + pub fn set_end(&mut self, end: gst::ClockTime) { + self.inner.end = end.nseconds(); + } +} + +unsafe impl Send for MediaSourceRange {} +unsafe impl Sync for MediaSourceRange {} + +impl PartialEq for MediaSourceRange { + fn eq(&self, other: &Self) -> bool { + self.inner.start == other.inner.start && self.inner.end == other.inner.end + } +} + +impl Eq for MediaSourceRange {} + +impl fmt::Debug for MediaSourceRange { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("MediaSourceRange") + .field("start", &self.start()) + .field("end", &self.end()) + .finish() + } +} diff --git a/gstreamer-mse/src/source_buffer_interval.rs b/gstreamer-mse/src/source_buffer_interval.rs new file mode 100644 index 000000000..bb9d2d901 --- /dev/null +++ b/gstreamer-mse/src/source_buffer_interval.rs @@ -0,0 +1,61 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use std::fmt; + +use crate::ffi; + +glib::wrapper! { + #[doc(alias = "GstSourceBufferInterval")] + pub struct SourceBufferInterval(BoxedInline); + + match fn {} +} + +impl SourceBufferInterval { + pub fn new(start: gst::ClockTime, end: gst::ClockTime) -> Self { + skip_assert_initialized!(); + + let inner = ffi::GstSourceBufferInterval { + start: start.nseconds(), + end: end.nseconds(), + }; + + Self { inner } + } + + pub fn start(&self) -> gst::ClockTime { + gst::ClockTime::from_nseconds(self.inner.start) + } + + pub fn set_start(&mut self, start: gst::ClockTime) { + self.inner.start = start.nseconds(); + } + + pub fn end(&self) -> gst::ClockTime { + gst::ClockTime::from_nseconds(self.inner.end) + } + + pub fn set_end(&mut self, end: gst::ClockTime) { + self.inner.end = end.nseconds(); + } +} + +unsafe impl Send for SourceBufferInterval {} +unsafe impl Sync for SourceBufferInterval {} + +impl PartialEq for SourceBufferInterval { + fn eq(&self, other: &Self) -> bool { + self.inner.start == other.inner.start && self.inner.end == other.inner.end + } +} + +impl Eq for SourceBufferInterval {} + +impl fmt::Debug for SourceBufferInterval { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("SourceBufferInterval") + .field("start", &self.start()) + .field("end", &self.end()) + .finish() + } +} diff --git a/gstreamer-mse/sys/COPYRIGHT b/gstreamer-mse/sys/COPYRIGHT new file mode 120000 index 000000000..dc5f40a22 --- /dev/null +++ b/gstreamer-mse/sys/COPYRIGHT @@ -0,0 +1 @@ +../COPYRIGHT \ No newline at end of file diff --git a/gstreamer-mse/sys/Cargo.toml b/gstreamer-mse/sys/Cargo.toml index 757d3875f..9451a3185 100644 --- a/gstreamer-mse/sys/Cargo.toml +++ b/gstreamer-mse/sys/Cargo.toml @@ -1,7 +1,5 @@ [package] name = "gstreamer-mse-sys" -version = "0.0.1" -edition = "2021" build = "build.rs" authors = ["Sebastian Dröge ", "Simon Wülker "] description = "FFI bindings to libgstmse-1.0" @@ -9,6 +7,24 @@ documentation = "https://gstreamer.pages.freedesktop.org/gstreamer-rs/stable/lat keywords = ["ffi", "gstreamer", "gnome", "multimedia"] readme = "README.md" +[package.version] +workspace = true + +[package.categories] +workspace = true + +[package.repository] +workspace = true + +[package.homepage] +workspace = true + +[package.edition] +workspace = true + +[package.rust-version] +workspace = true + [package.metadata.system-deps.gstreamer_mse_1_0] name = "gstreamer-mse-1.0" version = "1.26" @@ -38,4 +54,3 @@ shell-words = "1.0.0" tempfile = "3" [features] -v1_24 = [] diff --git a/gstreamer-mse/sys/LICENSE-APACHE b/gstreamer-mse/sys/LICENSE-APACHE new file mode 120000 index 000000000..965b606f3 --- /dev/null +++ b/gstreamer-mse/sys/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/gstreamer-mse/sys/LICENSE-MIT b/gstreamer-mse/sys/LICENSE-MIT new file mode 120000 index 000000000..76219eb72 --- /dev/null +++ b/gstreamer-mse/sys/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/gstreamer-mse/sys/README.md b/gstreamer-mse/sys/README.md new file mode 100644 index 000000000..614ff3669 --- /dev/null +++ b/gstreamer-mse/sys/README.md @@ -0,0 +1,220 @@ +# gstreamer-rs [![crates.io](https://img.shields.io/crates/v/gstreamer-mse-sys.svg)](https://crates.io/crates/gstreamer-mse-sys) [![pipeline status](https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/badges/main/pipeline.svg)](https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/commits/main) + +[GStreamer](https://gstreamer.freedesktop.org/) (MSE library) bindings for Rust. +Documentation can be found [here](https://gstreamer.pages.freedesktop.org/gstreamer-rs/stable/latest/docs/gstreamer_mse/). + +These bindings are providing a safe API that can be used to interface with +GStreamer, e.g. for writing GStreamer-based applications and GStreamer plugins. + +The bindings are mostly autogenerated with [gir](https://github.com/gtk-rs/gir/) +based on the [GObject-Introspection](https://wiki.gnome.org/Projects/GObjectIntrospection/) +API metadata provided by the GStreamer project. + +## Table of Contents +- [gstreamer-rs ](#gstreamer-rs--) + - [Table of Contents](#table-of-contents) + - [Installation](#installation) + - [Linux/BSDs](#linuxbsds) + - [macOS](#macos) + - [GStreamer Binaries](#gstreamer-binaries) + - [Homebrew](#homebrew) + - [Windows](#windows) + - [GStreamer Binaries](#gstreamer-binaries-1) + - [MSYS2 / pacman](#msys2--pacman) + - [Getting Started](#getting-started) + - [LICENSE](#license) + - [Contribution](#contribution) + + + +## Installation + +To build the GStreamer bindings or anything depending on them, you need to +have at least GStreamer 1.14 and gst-plugins-base 1.14 installed. In addition, +some of the examples/tutorials require various GStreamer plugins to be +available, which can be found in gst-plugins-base, gst-plugins-good, +gst-plugins-bad, gst-plugins-ugly and/or gst-libav. + + + +### Linux/BSDs + +You need to install the above mentioned packages with your distributions +package manager, or build them from source. + +On Debian/Ubuntu they can be installed with + +```console +$ apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \ + gstreamer1.0-plugins-base gstreamer1.0-plugins-good \ + gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly \ + gstreamer1.0-libav libgstrtspserver-1.0-dev libges-1.0-dev +``` + +The minimum required version of the above libraries is >= 1.14. If you +build the gstreamer-player sub-crate, or any of the examples that +depend on gstreamer-player, you must ensure that in addition to the above +packages, `libgstreamer-plugins-bad1.0-dev` is installed. See the `Cargo.toml` +files for the full details, + +```console +$ apt-get install libgstreamer-plugins-bad1.0-dev +``` + +Package names on other distributions should be similar. +Please submit a pull request with instructions for yours. + + + +### macOS + +You can install GStreamer and the plugins via [Homebrew](https://brew.sh/) or +by installing the [binaries](https://gstreamer.freedesktop.org/data/pkg/osx/) +provided by the GStreamer project. + +We recommend using the official GStreamer binaries over Homebrew, especially +as GStreamer in Homebrew is [currently broken](https://github.com/orgs/Homebrew/discussions/3740#discussioncomment-3804964). + +#### GStreamer Binaries + +You need to download the *two* `.pkg` files from the GStreamer website and +install them, e.g. `gstreamer-1.0-1.20.4-universal.pkg` and +`gstreamer-1.0-devel-1.20.4-universal.pkg`. + +After installation, you also need to set the `PATH` environment variable as +follows + +```console +$ export PATH="/Library/Frameworks/GStreamer.framework/Versions/1.0/bin${PATH:+:$PATH}" +``` + +Also note that the `pkg-config` from GStreamer should be the first one in +the `PATH` as other versions have all kinds of quirks that will cause +problems. + +#### Homebrew + +Homebrew only installs various plugins if explicitly enabled, so some extra +`--with-*` flags may be required. + +```console +$ brew install gstreamer gst-plugins-base gst-plugins-good \ + gst-plugins-bad gst-plugins-ugly gst-libav gst-rtsp-server \ + gst-editing-services --with-orc --with-libogg --with-opus \ + --with-pango --with-theora --with-libvorbis --with-libvpx \ + --enable-gtk3 +``` + +Make sure the version of these libraries is >= 1.14. + + + +### Windows + +You can install GStreamer and the plugins via [MSYS2](http://www.msys2.org/) +with `pacman` or by installing the +[binaries](https://gstreamer.freedesktop.org/data/pkg/windows/) provided by +the GStreamer project. + +We recommend using the official GStreamer binaries over MSYS2. + +#### GStreamer Binaries + +You need to download the *two* `.msi` files for your platform from the +GStreamer website and install them, e.g. `gstreamer-1.0-x86_64-1.20.4.msi` and +`gstreamer-1.0-devel-x86_64-1.20.4.msi`. Make sure to select the version that +matches your Rust toolchain, i.e. MinGW or MSVC. + +After installation set the ``PATH` environment variable as follows: + +```console +# For a UNIX-style shell: +$ export PATH="c:/gstreamer/1.0/msvc_x86_64/bin${PATH:+:$PATH}" + +# For cmd.exe: +$ set PATH=C:\gstreamer\1.0\msvc_x86_64\bin;%PATH% +``` + +Make sure to update the path to where you have actually installed GStreamer +and for the corresponding toolchain. + +Also note that the `pkg-config.exe` from GStreamer should be the first one in +the `PATH` as other versions have all kinds of quirks that will cause +problems. + +#### MSYS2 / pacman + +```console +$ pacman -S glib2-devel pkg-config \ + mingw-w64-x86_64-gstreamer mingw-w64-x86_64-gst-plugins-base \ + mingw-w64-x86_64-gst-plugins-good mingw-w64-x86_64-gst-plugins-bad \ + mingw-w64-x86_64-gst-plugins-ugly mingw-w64-x86_64-gst-libav \ + mingw-w64-x86_64-gst-rtsp-server +``` + +Make sure the version of these libraries is >= 1.14. + +Note that the version of `pkg-config` included in `MSYS2` is +[known to have problems](https://github.com/rust-lang/pkg-config-rs/issues/51#issuecomment-346300858) +compiling GStreamer, so you may need to install another version. One option +would be [`pkg-config-lite`](https://sourceforge.net/projects/pkgconfiglite/). + + + +## Getting Started + +The API reference can be found +[here](https://gstreamer.pages.freedesktop.org/gstreamer-rs/stable/latest/docs/gstreamer/), however it is +only the Rust API reference and does not explain any of the concepts. + +For getting started with GStreamer development, the best would be to follow +the [documentation](https://gstreamer.freedesktop.org/documentation/) on the +GStreamer website, especially the [Application Development +Manual](https://gstreamer.freedesktop.org/documentation/application-development/). +While being C-centric, it explains all the fundamental concepts of GStreamer +and the code examples should be relatively easily translatable to Rust. The +API is basically the same, function/struct names are the same and everything +is only more convenient (hopefully) and safer. + +In addition there are +[tutorials](https://gstreamer.freedesktop.org/documentation/tutorials/) on the +GStreamer website. Many of them were ported to Rust already and the code can +be found in the +[tutorials](https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/tree/main/tutorials) +directory. + +Some further examples for various aspects of GStreamer and how to use it from +Rust can be found in the +[examples](https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/tree/main/examples) +directory. + +Various GStreamer plugins written in Rust can be found in the +[gst-plugins-rs](https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs) +repository. + + + +## LICENSE + +gstreamer-rs and all crates contained in here are licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or + http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or + http://opensource.org/licenses/MIT) + +at your option. + +GStreamer itself is licensed under the Lesser General Public License version +2.1 or (at your option) any later version: +https://www.gnu.org/licenses/lgpl-2.1.html + + + +## Contribution + +Any kinds of contributions are welcome as a pull request. + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in gstreamer-rs by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions.