Compare commits

..

36 commits
main ... 0.10.3

Author SHA1 Message Date
Sebastian Dröge 4e4d226b71 Update Cargo.lock 2023-03-02 13:30:12 +02:00
Sebastian Dröge b903503a01 Update CHANGELOG.md for 0.10.3 2023-03-02 13:30:01 +02:00
Sebastian Dröge 20b18c23e6 Update versions to 0.10.3 2023-03-02 13:27:22 +02:00
Sebastian Dröge 160033cb5b Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1115>
2023-03-02 11:00:23 +02:00
Vivia Nikolaidou 3dde725560 ndisinkcombiner: Properly handle caps changes
We are caching one video buffer, so previously we were changing the src
caps one buffer too early.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1115>
2023-03-02 11:00:23 +02:00
Sebastian Dröge 1113e0aa51 deny: Update to allow socket2 0.4
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1115>
2023-03-02 11:00:23 +02:00
Sebastian Dröge 9b33184018 threadshare: Update to socket2 0.5
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1115>
2023-03-02 11:00:23 +02:00
Thibault Saunier 528f46a510 webrtcsink: Move RUNTIME to the crate so it can be reused
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1115>
2023-03-02 11:00:23 +02:00
Thibault Saunier 8b8f10691c webrtc: Enhance debug messages when using unknown peer ID
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1115>
2023-03-02 11:00:23 +02:00
Guillaume Desmottes 6936bf65c4 tracers: queue_levels: add appsrc support
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1115>
2023-03-02 10:50:19 +02:00
Sebastian Dröge b3cb41eae3 livesync: Correctly calculate fallback buffer duration from framerate
Numerator and denominator were switched.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1115>
2023-03-02 10:50:19 +02:00
Matthew Waters 84bae8b9cf webrtcsink: also support nvvidconv in lieu of nvvideoconvert
nvvideoconvert may not exist and nvvidconv might on some Jetson
platforms.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1115>
2023-03-02 10:50:19 +02:00
Sebastian Dröge eb296a0e63 gtk4: Set sync point on the video frame after mapping it
Otherwise it is not always ready for use yet in GTK even after waiting
on the sync point, and a fully transparent texture is rendered instead.

Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/320

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1105>
2023-02-25 12:52:17 +02:00
Sebastian Dröge b64da48508 Update Cargo.lock 2023-02-23 10:16:33 +02:00
Sebastian Dröge 37877efc90 Update CHANGELOG.md for 0.10.2 2023-02-23 10:11:52 +02:00
Sebastian Dröge f22b3420b6 Update versions to 0.10.2 2023-02-23 10:07:43 +02:00
Jordan Petridis b17348b08d video/gtk4: Add a flatpak snippet example in the README
Close #155

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1102>
2023-02-23 09:41:07 +02:00
Sebastian Dröge bf0a23d560 gtk4: Attach channel receiver to the default main context from the main thread
It requires acquiring the main context for thread-safety reasons and
that is only possible from the main thread itself.

Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/319

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1100>
2023-02-22 14:22:08 +02:00
Sebastian Dröge 384a9d05f0 gtk4: Don't unnecessarily set the sink to READY to retrieve the context
That's not needed and will cause the GL context messages to be not
distributed inside the pipeline.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1100>
2023-02-22 14:22:06 +02:00
Sebastian Dröge eda0d9abe1 gtk4: Refactor and simplify GL context handling
Create a single, global GDK GL context and the corresponding GStreamer
GL display and wrapped GStreamer GL context when initializing the first
sink and continue using that for all further sinks.

Additionally, don't create a full GStreamer GL context inside the sink
but only distribute the wrapped GL context in the pipeline so that
elements that actually need a full GL context can create one that is
sharing with that one. The sink itself does not need a full GStreamer GL
context.

Then inside the sink check that any GL memory that arrives was created
by a GL context that can share with the wrapped GDK GL context and only
then use it.

And lastly, use the correct GL contexts for a) creating a sync point and
b) actually waiting on it.

Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/318

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1100>
2023-02-22 14:22:05 +02:00
Seungha Yang 3c4d22bc5c mp4mux: Ignore framerate update
like mp4mux in -good does already

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1097>
2023-02-21 16:16:31 +02:00
Seungha Yang 56a25af929 fmp4mux: Ignore framerate update
like mp4mux in -good does already

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1097>
2023-02-21 16:16:25 +02:00
Arun Raghavan 54adcb8482 hlssink3: Allow GIOStream signal handlers to return None
If creating a playlist or fragment stream fails (disk is full, the
directory is removed, ...), we will currently crash because the signal
handler expects a non-None GIOStream. The actual callback is allowed to
return None values and we handle this in the caller, so let's not have
this restriction on the signal handler.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1097>
2023-02-21 16:16:16 +02:00
Sebastian Dröge 46d7c177c2 Update Cargo.lock 2023-02-13 15:03:23 +02:00
Sebastian Dröge 2fc2357306 Add mp4 plugin to README.md 2023-02-13 11:56:50 +02:00
Sebastian Dröge 9cd68ffb5f Update CHANGELOG.md for 0.10.1 2023-02-13 11:53:31 +02:00
Sebastian Dröge 73a5703eeb Update versions to 0.10.1 2023-02-13 11:52:37 +02:00
Sebastian Dröge 7d7f73768b Add CHANGELOG.md for 0.10.0 release
This is the first one and only lists changes from 0.9.0 to 0.10.0
2023-02-12 13:16:21 +02:00
Sebastian Dröge 510cda303a Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1091>
2023-02-11 20:37:29 +02:00
Sebastian Dröge d2cd2ef5ac ci: Don't run cargo update on the stable branch
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1091>
2023-02-11 20:15:03 +02:00
Seungha Yang a505dba3a2 rtpav1pay: Fix Leb128Bytes size parsing
There are multiple ways of encoding the value, and don't assume
that bitstream used the way used in this plugin. Instead, count
the number of used bytes.

Fixes: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/312
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1091>
2023-02-11 19:44:08 +02:00
Sebastian Dröge a4d5a35403 Update to async-tungstenite 0.20
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1091>
2023-02-11 19:44:01 +02:00
Sebastian Dröge 8a6895a73e Add Cargo.lock 2023-02-10 00:36:39 +02:00
Sebastian Dröge f3c1a91fb9 Add versions to local dependencies 2023-02-10 00:36:22 +02:00
Sebastian Dröge 6df679d69f Update to gtk-rs-core 0.17, gtk4-rs 0.6 and gstreamer-rs 0.20 branches 2023-02-10 00:33:25 +02:00
Sebastian Dröge 85bf8d6c63 Update versions to 0.10.0 2023-02-10 00:26:24 +02:00
744 changed files with 25805 additions and 117058 deletions

4
.gitignore vendored
View file

@ -1,7 +1,7 @@
Cargo.lock
target
*~
*.bk
*.swp
.vscode
builddir
.meson-subproject-wrap-hash.txt
builddir

View file

@ -1,4 +1,4 @@
.templates_sha: &templates_sha fddab8aa63e89a8e65214f59860d9c0f030360c9
.templates_sha: &templates_sha 567700e483aabed992d0a4fea84994a0472deff6
include:
- project: 'freedesktop/ci-templates'
@ -14,14 +14,12 @@ include:
file: '/.gitlab-image-tags.yml'
variables:
FDO_UPSTREAM_REPO: gstreamer/gstreamer-rs
FDO_UPSTREAM_REPO: gstreamer/gst-plugins-rs
# We use GStreamer image to build the documentation as it is the simplest way
# to ensure that we are testing against the same thing as GStreamer itself.
# The tag name is included above from the main repo.
GSTREAMER_DOC_IMAGE: "registry.freedesktop.org/gstreamer/gstreamer/amd64/fedora:$FEDORA_TAG-main"
# Use the gstreamer image to trigger the cerbero job, same as the monorepo
CERBERO_TRIGGER_IMAGE: "registry.freedesktop.org/gstreamer/gstreamer/amd64/fedora:$FEDORA_TAG-main"
WINDOWS_BASE: "registry.freedesktop.org/gstreamer/gstreamer-rs/windows"
WINDOWS_RUST_MINIMUM_IMAGE: "$WINDOWS_BASE:$GST_RS_IMG_TAG-main-$GST_RS_MSRV"
WINDOWS_RUST_STABLE_IMAGE: "$WINDOWS_BASE:$GST_RS_IMG_TAG-main-$GST_RS_STABLE"
@ -41,6 +39,7 @@ default:
stages:
- "trigger"
- "prep"
- "lint"
- "test"
- "extras"
@ -52,7 +51,6 @@ trigger:
stage: 'trigger'
variables:
GIT_STRATEGY: none
tags: [ 'placeholder-job' ]
script:
- echo "Trigger job done, now running the pipeline."
rules:
@ -68,26 +66,121 @@ trigger:
when: 'manual'
allow_failure: false
.debian:12:
.debian:11:
variables:
SODIUM_USE_PKG_CONFIG: "true"
after_script:
- rm -rf target
FDO_DISTRIBUTION_VERSION: 'bullseye-slim'
before_script:
- source ./ci/env.sh
- mkdir .cargo && echo -e "[net]\ngit-fetch-with-cli = true" > .cargo/config
.debian:12-stable:
extends: .debian:12
image: "registry.freedesktop.org/gstreamer/gstreamer-rs/debian/bookworm-slim:$GST_RS_STABLE-$GST_RS_IMG_TAG"
.debian:11-stable:
extends: .debian:11
variables:
FDO_DISTRIBUTION_TAG: '$GST_RS_STABLE-${GST_RS_IMG_TAG}_2022-11-05.0'
FDO_BASE_IMAGE: "registry.freedesktop.org/gstreamer/gstreamer-rs/debian/bullseye-slim:$GST_RS_STABLE-$GST_RS_IMG_TAG"
.debian:12-msrv:
extends: .debian:12
image: "registry.freedesktop.org/gstreamer/gstreamer-rs/debian/bookworm-slim:$GST_RS_MSRV-$GST_RS_IMG_TAG"
.debian:11-msrv:
extends: .debian:11
variables:
FDO_DISTRIBUTION_TAG: '$GST_RS_MSRV-${GST_RS_IMG_TAG}_2022-11-05.0'
FDO_BASE_IMAGE: "registry.freedesktop.org/gstreamer/gstreamer-rs/debian/bullseye-slim:$GST_RS_MSRV-$GST_RS_IMG_TAG"
.debian:12-nightly:
extends: .debian:12
image: "registry.freedesktop.org/gstreamer/gstreamer-rs/debian/bookworm-slim:nightly-$GST_RS_IMG_TAG"
.debian:11-nightly:
extends: .debian:11
variables:
FDO_DISTRIBUTION_TAG: 'nightly-${GST_RS_IMG_TAG}_2022-11-05.0'
FDO_BASE_IMAGE: "registry.freedesktop.org/gstreamer/gstreamer-rs/debian/bullseye-slim:nightly-$GST_RS_IMG_TAG"
.build-debian-container:
extends:
- .fdo.container-build@debian
stage: prep
variables:
FDO_DISTRIBUTION_PACKAGES: "libcsound64-dev llvm clang nasm libsodium-dev libwebp-dev python3-pip"
FDO_DISTRIBUTION_EXEC: >-
bash ci/install-dav1d.sh &&
apt clean &&
bash ./ci/install-rust-ext.sh &&
pip install tomli
needs:
- "trigger"
rules:
- if: '$UPDATE_IMG == null'
build-stable:
extends:
- .build-debian-container
- .debian:11-stable
build-msrv:
extends:
- .build-debian-container
- .debian:11-msrv
build-nightly:
extends:
- .build-debian-container
- .debian:11-nightly
# Those jobs are triggered by gstreamer-rs when updating its images
update-stable:
extends: build-stable
rules:
- if: '$UPDATE_IMG == "stable"'
variables:
FDO_FORCE_REBUILD: 1
update-msrv:
extends: build-msrv
rules:
- if: '$UPDATE_IMG == "msrv"'
variables:
FDO_FORCE_REBUILD: 1
update-nightly:
extends: build-nightly
rules:
- if: '$UPDATE_IMG == "nightly"'
variables:
FDO_FORCE_REBUILD: 1
.dist-debian-container:
extends:
- .fdo.distribution-image@debian
variables:
SODIUM_USE_PKG_CONFIG: "true"
after_script:
- rm -rf target
.img-stable:
extends:
- .dist-debian-container
- .debian:11-stable
needs:
- job: 'build-stable'
optional: true
- job: 'update-stable'
optional: true
.img-msrv:
extends:
- .dist-debian-container
- .debian:11-msrv
needs:
- job: 'build-msrv'
optional: true
- job: 'update-msrv'
optional: true
.img-nightly:
extends:
- .dist-debian-container
- .debian:11-nightly
needs:
- job: 'build-nightly'
optional: true
- job: 'update-nightly'
optional: true
.cargo test:
stage: "test"
@ -99,44 +192,48 @@ trigger:
- rustc --version
- cargo build --locked --color=always --workspace --all-targets
- RUST_BACKTRACE=1 G_DEBUG=fatal_warnings cargo test --locked --color=always --workspace --all-targets
- cargo build --locked --color=always --workspace --all-targets --all-features --exclude gst-plugin-gtk4
- RUST_BACKTRACE=1 G_DEBUG=fatal_warnings cargo test --locked --color=always --workspace --all-targets --all-features --exclude gst-plugin-gtk4
- G_DEBUG=fatal_warnings cargo test --locked --color=always --workspace --all-targets
- cargo build --locked --color=always --workspace --all-targets --all-features
- G_DEBUG=fatal_warnings cargo test --locked --color=always --workspace --all-targets --all-features
- cargo build --locked --color=always --workspace --all-targets --no-default-features
- RUST_BACKTRACE=1 G_DEBUG=fatal_warnings cargo test --locked --color=always --workspace --all-targets --no-default-features
- G_DEBUG=fatal_warnings cargo test --locked --color=always --workspace --all-targets --no-default-features
test msrv:
extends:
- '.cargo test'
- '.debian:12-msrv'
needs: [ "trigger" ]
- .img-msrv
rules:
- if: '$UPDATE_IMG == null || $UPDATE_IMG == "msrv"'
test stable:
extends:
- '.cargo test'
- '.debian:12-stable'
needs: [ "trigger" ]
- .img-stable
rules:
- if: '$UPDATE_IMG == null || $UPDATE_IMG == "stable"'
test nightly:
allow_failure: true
extends:
- '.cargo test'
- '.debian:12-nightly'
needs: [ "trigger" ]
- .img-nightly
rules:
- if: '$UPDATE_IMG == null || $UPDATE_IMG == "nightly"'
.meson:
extends: .debian:12-stable
extends: .img-stable
rules:
- if: '$UPDATE_IMG == null || $UPDATE_IMG == "stable"'
variables:
# csound-sys only looks at /usr/lib and /usr/local top levels
CSOUND_LIB_DIR: '/usr/lib/x86_64-linux-gnu/'
meson shared:
extends: .meson
needs: [ "trigger" ]
variables:
CI_ARTIFACTS_URL: "${CI_PROJECT_URL}/-/jobs/${CI_JOB_ID}/artifacts/raw/"
script:
- meson build --default-library=shared --prefix=$(pwd)/install --fatal-meson-warnings
- meson build --default-library=shared --prefix=$(pwd)/install
- ninja -C build install
- ./ci/check-installed.py install
- ninja -C build docs/gst_plugins_cache.json
@ -150,7 +247,6 @@ meson shared:
meson static:
extends: .meson
needs: [ "trigger" ]
script:
- meson build --default-library=static --prefix=$(pwd)/install -Dsodium-source=built-in
- ninja -C build install
@ -172,6 +268,7 @@ documentation:
variables:
MESON_ARGS: >
-Ddoc=enabled
-Domx=disabled
-Dpython=disabled
-Dlibav=disabled
-Dlibnice=disabled
@ -288,96 +385,64 @@ test windows stable:
image: "$WINDOWS_RUST_STABLE_IMAGE"
rustfmt:
extends: '.debian:12-stable'
extends: .img-stable
stage: "lint"
tags: [ 'placeholder-job' ]
needs: []
rules:
- when: 'always'
script:
- cargo fmt --version
- cargo fmt -- --color=always --check
typos:
extends: '.debian:12-stable'
stage: "lint"
tags: [ 'placeholder-job' ]
needs: []
script:
- typos
gstwebrtc-api lint:
image: node:lts
stage: "lint"
tags: [ 'placeholder-job' ]
needs: []
script:
- cd net/webrtc/gstwebrtc-api
- npm install
- npm run check
check commits:
extends: '.debian:12-stable'
extends: .img-stable
stage: "lint"
tags: [ 'placeholder-job' ]
needs: []
rules:
- when: 'always'
script:
- ci-fairy check-commits --textwidth 0 --no-signed-off-by
- ci/check-for-symlinks.sh
- ci/check-meson-version.sh
clippy:
extends: '.debian:12-stable'
needs:
- "trigger"
- "test stable"
extends: .img-stable
stage: 'extras'
variables:
# csound-sys only looks at /usr/lib and /usr/local top levels
CSOUND_LIB_DIR: '/usr/lib/x86_64-linux-gnu/'
rules:
- when: 'always'
script:
- cargo clippy --locked --color=always --all --all-targets -- -D warnings -A unknown-lints
- cargo clippy --locked --color=always --all --all-features --all-targets --exclude gst-plugin-gtk4 -- -D warnings -A unknown-lints
- cargo clippy --locked --color=always --all --all-targets --no-default-features -- -D warnings -A unknown-lints
- cargo clippy --locked --color=always --all --all-features --all-targets -- -D warnings -A unknown-lints
deny:
extends: .debian:12-stable
extends: .img-stable
stage: 'extras'
needs:
- "trigger"
- "test stable"
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
script:
- cargo update --color=always
- cargo deny --color=always --workspace --all-features check all
- cargo deny check
outdated:
extends: '.debian:12-stable'
extends: .img-stable
allow_failure: true
needs:
- "trigger"
- "test stable"
stage: 'extras'
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
script:
- cargo update --color=always
- cargo outdated --color=always --root-deps-only --exit-code 1 -v
- cargo outdated --root-deps-only --exit-code 1 -v
coverage:
allow_failure: true
extends:
- '.debian:12-stable'
needs:
- "trigger"
- "test stable"
- .img-stable
stage: 'extras'
rules:
- when: 'always'
variables:
RUSTFLAGS: "-Cinstrument-coverage"
LLVM_PROFILE_FILE: "gst-plugins-rs-%p-%m.profraw"
# csound-sys only looks at /usr/lib and /usr/local top levels
CSOUND_LIB_DIR: '/usr/lib/x86_64-linux-gnu/'
script:
- cargo test --locked --color=always --all --all-features --exclude gst-plugin-gtk4
- cargo test --locked --color=always --all --all-features
# generate html report
- grcov . --binary-path ./target/debug/ -s . -t html --branch --ignore-not-existing --ignore "*target*" --ignore "*/build.rs" -o ./coverage/
# generate cobertura report for gitlab integration
@ -392,34 +457,3 @@ coverage:
coverage_report:
coverage_format: cobertura
path: coverage.xml
cerbero trigger:
image: $CERBERO_TRIGGER_IMAGE
needs: [ "trigger" ]
variables:
# We will build this cerbero branch in the cerbero trigger CI
CERBERO_UPSTREAM_BRANCH: 'main'
script:
- ci/cerbero/trigger_cerbero_pipeline.py
rules:
# Never run post merge
- if: '$CI_PROJECT_NAMESPACE == "gstreamer"'
when: never
# Don't run if the only changes are files that cargo-c does not read
- if:
changes:
- "CHANGELOG.md"
- "README.md"
- "deny.toml"
- "rustfmt.toml"
- "typos.toml"
- "*.py"
- "*.sh"
- "Makefile"
- "meson.build"
- "meson_options.txt"
- "**/meson.build"
- "ci/*.sh"
- "ci/*.py"
when: never
- when: always

View file

@ -1,33 +0,0 @@
### Describe your issue
<!-- a clear and concise summary of the bug. -->
<!-- For any GStreamer usage question, please contact the community using the #gstreamer channel on IRC https://www.oftc.net/ or the mailing list on https://gstreamer.freedesktop.org/lists/ -->
#### Expected Behavior
<!-- What did you expect to happen -->
#### Observed Behavior
<!-- What actually happened -->
#### Setup
- **Operating System:**
- **Device:** Computer / Tablet / Mobile / Virtual Machine <!-- Delete as appropriate !-->
- **gst-plugins-rs Version:**
- **GStreamer Version:**
- **Command line:**
### Steps to reproduce the bug
<!-- please fill in exact steps which reproduce the bug on your system, for example: -->
1. open terminal
2. type `command`
### How reproducible is the bug?
<!-- The reproducibility of the bug is Always/Intermittent/Only once after doing a very specific set of steps-->
### Screenshots if relevant
### Solutions you have tried
### Related non-duplicate issues
### Additional Information
<!-- Any other information such as logs. Make use of <details> for long output -->

View file

@ -5,359 +5,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.12.7] - 2024-06-19
### Fixed
- aws, spotifyaudiosrc, reqwesthttpsrc, webrtchttp: Fix race condition when unlocking
- rtp: Allow any payload type for the AV1 RTP payloader/depayloader
- rtp: Various fixes to the AV1 RTP payloader/depayloader to work correctly
with Chrome and Pion
- meson: Various fixes to the meson-based build system around cargo
- webrtcsink: Use correct property names for configuring `av1enc`
- webrtcsink: Avoid lock poisoning when setting encoder properties
### Added
- ndi: Support for NDI SDK v6
- webrtcsink: Support for AV1 via `nvav1enc`, `av1enc` or `rav1enc`
### Changed
- Update to async-tungstenite 0.26
## [0.12.6] - 2024-05-23
### Fixed
- Various Rust 1.78 clippy warnings.
- gtk4paintablesink: Fix plugin description.
### Added
- fmp4mux / mp4mux: Add support for adding AV1 header OBUs into the MP4
headers.
- fmp4mux / mp4mux: Take track language from the tags if provided.
- gtk4paintablesink: Add GST_GTK4_WINDOW_FULLSCREEN environment variable to
create a fullscreen window for debugging purposes.
- gtk4paintablesink: Also create a window automatically when called from
gst-play-1.0.
- webrtc: Add support for insecure TLS connections.
- webrtcsink: Add VP9 parser after the encoder.
### Changed
- webrtcsink: Improve error when no discovery pipeline runs.
- rtpgccbwe: Improve debug output in various places.
## [0.12.5] - 2024-04-29
### Fixed
- hrtfrender: Use a bitmask instead of an int in the caps for the channel-mask.
- rtpgccbwe: Don't log an error when pushing a buffer list fails while stopping.
- webrtcsink: Don't panic in bitrate handling with unsupported encoders.
- webrtcsink: Don't panic if unsupported input caps are used.
- webrtcsrc: Allow a `None` producer-id in `request-encoded-filter` signal.
### Added
- aws: New property to support path-style addressing.
- fmp4mux / mp4mux: Support FLAC instead (f)MP4.
- gtk4: Support directly importing dmabufs with GTK 4.14.
- gtk4: Add force-aspect-ratio property similar to other video sinks.
## [0.12.4] - 2024-04-08
### Fixed
- aws: Use fixed behaviour version to ensure that updates to the AWS SDK don't
change any defaults configurations in unexpected ways.
- onvifmetadataparse: Fix possible deadlock on shutdown.
- webrtcsink: Set `perfect-timestamp=true` on audio encoders to work around
bugs in Chrome's audio decoders.
- Various clippy warnings.
### Changed
- reqwest: Update to reqwest 0.12.
- webrtchttp: Update to reqwest 0.12.
## [0.12.3] - 2024-03-21
### Fixed
- gtk4paintablesink: Fix scaling of texture position.
- janusvrwebrtcsink: Handle 64 bit numerical room ids.
- janusvrwebrtcsink: Don't include deprecated audio/video fields in publish
messages.
- janusvrwebrtcsink: Handle various other messages to avoid printing errors.
- livekitwebrtc: Fix shutdown behaviour.
- rtpgccbwe: Don't forward buffer lists with buffers from different SSRCs to
avoid breaking assumptions in rtpsession.
- sccparse: Ignore invalid timecodes during seeking.
- webrtcsink: Don't try parsing audio caps as video caps.
### Changed
- webrtc: Allow resolution and framerate changes.
- webrtcsrc: Make produce-peer-id optional.
### Added
- livekitwebrtcsrc: Add new LiveKit source element.
- regex: Add support for configuring regex behaviour.
- spotifyaudiosrc: Document how to use with non-Facebook accounts.
- webrtcsrc: Add `do-retransmission` property.
## [0.12.2] - 2024-02-26
### Fixed
- rtpgccbwe: Don't reset PTS/DTS to `None` as otherwise `rtpsession` won't be
able to generate valid RTCP.
- webrtcsink: Fix usage with 1.22.
### Added
- janusvrwebrtcsink: Add `secret-key` property.
- janusvrwebrtcsink: Allow for string room ids and add `string-ids` property.
- textwrap: Don't split on all whitespaces, especially not on non-breaking
whitespace.
## [0.12.1] - 2024-02-13
### Added
- gtk4: Create a window for testing purposes when running in `gst-launch-1.0`
or if `GST_GTK4_WINDOW=1` is set.
- webrtcsink: Add `msid` property.
## [0.12.0] - 2024-02-08
### Changed
- ndi: `ndisrc` passes received data downstream without an additional copy, if
possible.
- webrtc: Cleanups to webrtcsrc/sink default signalling protocol, JavaScript
implementation and server implementation.
- webrtc: `whipwebrtcsink` is renamed to `whipclientsink` and deprecate old
`whipsink`.
### Fixed
- gtk4: Fix Windows build when using EGL.
- gtk4: Fix ARGB pre-multiplication with GTK 4.14. This requires building with
the `gtk_v4_10` or even better `gtk_v4_14` feature.
- gtk4: Fix segfault if GTK3 is used in the same process.
- gtk4: Always draw background behind the video frame and not only when
borders have to be added to avoid glitches.
- livekitwebrtcsink: Add high-quality layer for video streams.
- webrtc: Fix potential hang and fd leak in signalling server.
- webrtc: Fix closing of WebSockets.
- webrtchttp: Allow setting `None` for audio/video caps for WHEP.
### Added
- New `awss3putobjectsink` that works similar to `awss3sink` but with a
different upload strategy.
- New `hlscmafsink` element for writing HLS streams with CMAF/ISOBMFF
fragments.
- New `inter` plugin with `intersink` / `intersrc` elements that allow to
connect different pipelines in the same process.
- New `janusvrwebrtcsink` element for the Janus VideoRoom API.
- New `rtspsrc2` element.
- New `whipserversrc` element.
- gtk4: New `background-color` property for setting the color of the
background of the frame and the borders, if any.
- gtk4: New `scale-filter` property for defining how to scale the frames.
- livesync: Add support for image formats.
- ndi: Closed Caption support in `ndisrc` / `ndisink`.
- textwrap: Add support for gaps.
- tracers: Optionally only show late buffers in `buffer-lateness` tracer.
- webrtc: Add support for custom headers.
- webrtcsink: New `payloader-setup` signal to configure payloader elements.
- webrtcsrc: Support for navigation events.
## [0.11.3] - 2023-12-18
### Fixed
- ndi: Mark a private type as such and remove a wrong `Clone` impl of internal types.
- uriplaylistbin: Fix a minor clippy warning.
- fallbacksrc: Fix error during badly timed timeout scheduling.
- webrtcsink: Fail gracefully if webrtcbin pads can't be requested instead of
panicking.
- threadshare: Fix deadlock in `ts-udpsrc` `notify::used-socket` signal
emission.
### Changed
- Update to AWS SDK 1.0.
- Update to windows-sys 0.52.
- Update to async-tungstenite 0.24.
- Update to bitstream-io 2.0.
- tttocea608: De-duplicate some functions.
- gtk4: Use async-channel instead of deprecated GLib main context channel.
## [0.11.2] - 2023-11-11
### Fixed
- filesink / s3sink: Set `sync=false` to allow processing faster than
real-time.
- hlssink3: Various minor bugfixes and cleanups.
- livesync: Various minor bugfixes and cleanups that should make the element
work more reliable.
- s3sink: Fix handling of non-ASCII characters in URIs and keys.
- sccparse: Parse SCC files that are incorrectly created by CCExtractor.
- ndisrc: Assume > 8 channels are unpositioned.
- rtpav1depay: Skip unexpected leading fragments instead of repeatedly warning
about the stream possibly being corrupted.
- rtpav1depay: Don't push stale temporal delimiters downstream but wait until
a complete OBU is collected.
- whipwebrtcsink: Use correct URL during redirects.
- webrtcsink: Make sure to not miss any ICE candidates.
- webrtcsink: Fix deadlock when calling `set-local-description`.
- webrtcsrc: Fix reference cycles that prevented the element from being freed.
- webrtcsrc: Define signaller property as `CONSTRUCT_ONLY` to make it actually
possible to set different signallers.
- webrtc: Update livekit signaller to livekit 0.2.
- meson: Various fixes to the meson-based build system.
### Added
- audiornnoise: Attach audio level meta to output buffers.
- hlssink3: Allow adding `EXT-X-PROGRAM-DATE-TIME` tag to the manifest.
- webrtcsrc: Add `turn-servers` property.
### Changed
- aws/webrtc: Update to AWS SDK 0.57/0.35.
## [0.11.1] - 2023-10-04
### Fixed
- fallbackswitch: Fix various deadlocks.
- webrtcsink: Gracefully fail if adding the TWCC RTP header extension fails.
- webrtcsink: Fix codec selection discovery.
- webrtcsink: Add support for D3D11 memory and qsvh264enc.
- onvifmetadataparse: Skip metadata frames with unrepresentable UTC times.
- gtk4paintablesink: Pre-multiply alpha when creating GL textures with alpha.
- gtk4paintablesink: Only support RGBA/RGB in the GL code path.
- webrtchttp: Respect HTTP redirects.
- fmp4mux: Specify unit of fragment-duration property.
### Changed
- threadshare: Port to polling 3.1.
## [0.11.0] - 2023-08-10
### Changed
- Updated MSRV to 1.70.
- Compatible with gtk-rs 0.18 and gstreamer-rs 0.21.
- awstranscriber: Move to HTTP2-based API via the aws-sdk-transcribestreaming
crate instead of our own implementation around the WebSocket API.
### Added
- webrtcsink: Add AWS KVS signaller and corresponding aws-kvs-webrtcsink
element.
- awstranscriber / transcriberbin: Add support for translations and outputting
transcriptions from a single audio stream in multiple languages at once.
- gstwebrtc-api: JavaScript API for interacting with the default signalling
protocol used by webrtcsink / webrtcsrc.
- cea608to708: New element for converting CEA608 to CEA708 closed captions.
- webrtcsink: Expose the signaller as property and allow implementing a
custom signaller by connecting signal handlers to the default signaller.
- webrtcsink: Add support for pre-encoded streams.
- togglerecord: Add support for non-live input streams.
- webrtcsink: New whipwebrtcsink that implements WHIP around webrtcsink.
The existing whipsink still exists but will sooner or later be deprecated.
- webrtcsink: Add LiveKit signaller and corresponding livekitwebrtcsink
element.
## [0.10.11] - 2023-07-20
### Fixed
- fallbackswitch: Fix pad health calculation and notifies.
- fallbackswitch: Change the threshold for trailing buffers.
- webrtcsink: Fix pipeline when input caps contain a max-framerate field.
- webrtcsink: Set VP8/VP9 payloader properties based on payloader element
factory name.
- webrtcsink: Set config-interval=-1 and aggregate-mode=zero-latency for
H264/5 payloaders.
- webrtcsink: Translate force-keyunit events to custom force-IDR API of NVIDIA
encoders.
- webrtcsink: Configure only 4 threads instead of 12 for x264enc for Chrome
compatibility.
- fmp4mux: Fix draining in chunk mode if keyframes are after the desired
fragment end.
## [0.10.10] - 2023-07-05
### Fixed
- livesync: Improve EOS handling to be in sync with `queue`'s behaviour.
- livesync: Wait for the end timestamp of the previous buffer before looking
at queue to actually make use of the available latency.
- webrtcsink: Avoid panic on unprepare from an async tokio context.
- webrtc/signalling: Fix race condition in message ordering.
- webrtcsink: Use the correct property types when configuring `nvvideoconvert`.
- videofx: Minimize dependencies of the image crate.
- togglerecord: Fix segment clipping to actually work as intended.
### Added
- gtk4paintablesink: Support for WGL/EGL on Windows.
- gtk4paintablesink: Add Python example application to the repository.
## [0.10.9] - 2023-06-19
### Fixed
- mp4mux/fmp4mux: Fix byte order in Opus extension box.
- webrtcsrc: Add twcc extension to the codec-preferences when present.
- webrtcsink: Don't try using cudaconvert if it is not present.
- mccparse: Don't offset the first timecode to a zero PTS.
- Correctly use MPL as license specifier instead of MPL-2 for plugins that
compile with GStreamer < 1.20.
### Added
- fallbackswitch: Add `stop-on-eos` property.
## [0.10.8] - 2023-06-07
### Fixed
- fmp4mux: Use updated start PTS when checking if a stream is filled instead
of a stale one.
- fmp4mux: Fix various issues with stream gaps, especially in the beginning.
- fmp4mux: Fix waiting in live pipelines.
- uriplaylistbin: Prevent deadlocks during property notifications.
- webrtcsink: Fix panics during `twcc-stats` callback and related issues.
- awstranscriber: Handle stream disconts correctly.
- roundedcorners: Fix caps negotiation to not use I420 if a border radius is
configured.
- whipsink: Use the correct pad template to request pads from the internal
webrtcbin.
- fallbacksrc: Don't apply fallback audio caps to the main stream.
- webrtcsrc: Fix caps handling during transceiver creation.
### Changed
- rtpgccbwe: Improve packet handling.
## [0.10.7] - 2023-05-09
### Fixed
- ffv1dec: Drop rank until the implementation is feature-complete.
- spotifyaudiosrc: Check cached credentials before use and fix usage of
credentials cache.
- tttocea608: Specify raw CEA608 field.
- gtk4paintablesink: Fix compilation on non-Linux UNIX systems.
- webrtcsrc: Don't set stun-server to the empty string if none was set.
- webrtcsink: Abort statistics collection before stopping the signaller.
- rtpgccbwe: Don't process empty lists.
### Changed
- ndi: Update to libloading 0.8.
- aws: Update to AWS SDK 0.55/0.27.
- webrtcsink: Order pads by serial number.
- Update to async-tungstenite 0.22.
### Added
- webrtcsink/webrtcsrc: Add `request-encoded-filter` signal to add support for
inserting custom filters between encoder/payloader or depayloader/decoder.
This allows interacting with the "insertable streams" API from Chrome.
## [0.10.6] - 2023-04-06
### Fixed
- webrtcsink: Fix max/min-bitrate property blurb/nick.
- uriplaylistbin: Add missing queues to example.
- tttocea608: Fix pushing of caps events that sometimes contained unfixed caps.
- tttocea608: Fix disappearing text after special character in non-popon mode.
- transcriberbin: Fix deadlock on construction.
- transcriberbin: Fix initial bin setup.
- fallbacksrc: Handle incompatible downstream caps without panicking.
- ndisrc: Fix copying of raw video frames with different NDI/GStreamer strides.
- livesync: Correctly assume zero upstream latency if latency query fails.
### Added
- webrtcsink: Add `ice-transport-policy` property that proxies the same
`webrtcbin` property.
## [0.10.5] - 2023-03-19
### Fixed
- gtk4: Fix build with OpenGL support on macOS.
- threadshare: Fix symbol conflicts when statically linking the plugin.
## [0.10.4] - 2023-03-14
### Fixed
- fmp4mux: Return a running time from `AggregatorImpl::next_time()` to fix
waiting in live pipelines.
- fmp4mux: Fix `hls_live` example to set properties on the right element.
- uriplaylistbin: Reset element when switching back to `NULL` state.
- livesync: Handle variable framerates correctly in fallback buffer duration
calculation.
- meson: Fix GStreamer version feature detection.
### Added
- webrtc: New `webrtc` element.
## [0.10.3] - 2023-03-02
### Added
- tracers: `queue_levels` tracer now also supports printing the `appsrc` levels.
@ -431,7 +78,7 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
- fmp4mux: Add support for CMAF-style chunking, e.g. low-latency / LL HLS and DASH
- gtk4: Support for rendering GL textures on X11/EGL, X11/GLX, Wayland and macOS
- hlssink3: Allow generating i-frame-only playlist
- livesync: New element that allows maintaining a contiguous live stream
- livesync: New element that alllows maintaining a contiguous live stream
without gaps from a potentially unstable source.
- mp4mux: New non-fragmented MP4 muxer element
- spotifyaudiosrc: Support configurable bitrate
@ -448,28 +95,7 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
- webrtcsink: Make the `turn-server` property a `turn-servers` list
- webrtcsink: Move from async-std to tokio
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.12.7...HEAD
[0.12.7]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.12.6...0.12.7
[0.12.6]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.12.5...0.12.6
[0.12.5]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.12.4...0.12.5
[0.12.4]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.12.3...0.12.4
[0.12.3]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.12.2...0.12.3
[0.12.2]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.12.1...0.12.2
[0.12.1]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.12.0...0.12.1
[0.12.0]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.11.3...0.12.0
[0.11.3]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.11.2...0.11.3
[0.11.2]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.11.1...0.11.2
[0.11.1]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.11.0...0.11.1
[0.11.0]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.10.11...0.11.0
[0.10.11]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.10.10...0.10.11
[0.10.10]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.10.9...0.10.10
[0.10.9]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.10.8...0.10.9
[0.10.8]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.10.7...0.10.8
[0.10.7]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.10.6...0.10.7
[0.10.6]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.10.5...0.10.6
[0.10.5]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.10.4...0.10.5
[0.10.4]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.10.3...0.10.4
[0.10.3]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.10.2...0.10.3
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.10.2...HEAD
[0.10.2]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.10.1...0.10.2
[0.10.1]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.10.0...0.10.1
[0.10.0]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.9.0...0.10.0

4850
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,4 @@
[workspace]
resolver = "2"
members = [
"tutorial",
@ -12,11 +11,8 @@ members = [
"audio/spotify",
"generic/file",
"generic/originalbuffer",
"generic/sodium",
"generic/threadshare",
"generic/inter",
"generic/gopbuffer",
"mux/flavors",
"mux/fmp4",
@ -29,12 +25,10 @@ members = [
"net/raptorq",
"net/reqwest",
"net/rtp",
"net/rtsp",
"net/webrtchttp",
"net/webrtc",
"net/webrtc/protocol",
"net/webrtc/signalling",
"net/quinn",
"text/ahead",
"text/json",
@ -68,10 +62,7 @@ default-members = [
"audio/claxon",
"audio/lewton",
"generic/originalbuffer",
"generic/threadshare",
"generic/inter",
"generic/gopbuffer",
"mux/fmp4",
"mux/mp4",
@ -82,13 +73,11 @@ default-members = [
"net/raptorq",
"net/reqwest",
"net/rtp",
"net/rtsp",
"net/webrtchttp",
"net/webrtc",
"net/webrtc/protocol",
"net/webrtc/signalling",
"net/ndi",
"net/quinn",
"text/ahead",
"text/json",
@ -117,39 +106,3 @@ panic = 'unwind'
[profile.dev]
opt-level = 1
[workspace.package]
version = "0.13.0-alpha.1"
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
edition = "2021"
rust-version = "1.71"
[workspace.dependencies]
once_cell = "1"
glib = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "master" }
gio = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "master" }
cairo-rs = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "master", features=["use_glib"] }
pango = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "master" }
pangocairo = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "master" }
gtk = { package = "gtk4", git = "https://github.com/gtk-rs/gtk4-rs", branch = "master"}
gdk-wayland = { package = "gdk4-wayland", git = "https://github.com/gtk-rs/gtk4-rs", branch = "master"}
gdk-x11 = { package = "gdk4-x11", git = "https://github.com/gtk-rs/gtk4-rs", branch = "master"}
gdk-win32 = { package = "gdk4-win32", git = "https://github.com/gtk-rs/gtk4-rs", branch = "master"}
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-allocators = { package = "gstreamer-allocators", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-app = { package = "gstreamer-app", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-gl = { package = "gstreamer-gl", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-gl-egl = { package = "gstreamer-gl-egl", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-gl-wayland = { package = "gstreamer-gl-wayland", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-gl-x11 = { package = "gstreamer-gl-x11", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-net = { package = "gstreamer-net", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-pbutils = { package = "gstreamer-pbutils", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-plugin-version-helper = { path="./version-helper" }
gst-rtp = { package = "gstreamer-rtp", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-sdp = { package = "gstreamer-sdp", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-utils = { package = "gstreamer-utils", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-video = { package = "gstreamer-video", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-webrtc = { package = "gstreamer-webrtc", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }

View file

@ -33,9 +33,6 @@ You will find the following plugins in this repository:
- `onvif`: Various elements for parsing, RTP (de)payloading, overlaying of ONVIF timed metadata.
- `quinn`: Transfer data over the network using QUIC
- `quinnquicsink`/`quinnquicsrc`: Send and receive data using QUIC
- `raptorq`: Encoder/decoder element for RaptorQ RTP FEC mechanism.
- `reqwest`: An HTTP source element based on the [reqwest](https://github.com/seanmonstar/reqwest) library.

View file

@ -1,27 +1,27 @@
[package]
name = "gst-plugin-audiofx"
version.workspace = true
version = "0.10.3"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
repository.workspace = true
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
license = "MPL-2.0"
description = "GStreamer Rust Audio Effects Plugin"
edition.workspace = true
rust-version.workspace = true
edition = "2021"
rust-version = "1.63"
[dependencies]
gst = { workspace = true, features = ["v1_20"] }
gst-base = { workspace = true, features = ["v1_20"] }
gst-audio = { workspace = true, features = ["v1_20"] }
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20", features = ["v1_16"] }
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20", features = ["v1_16"] }
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20", features = ["v1_16"] }
anyhow = "1"
byte-slice-cast = "1.0"
num-traits = "0.2"
once_cell = "1.0"
ebur128 = "0.1"
hrtf = "0.8"
nnnoiseless = { version = "0.5", default-features = false }
smallvec = "1"
atomic_refcell = "0.1"
rayon = "1.5"
once_cell.workspace = true
[lib]
name = "gstrsaudiofx"
@ -29,11 +29,11 @@ crate-type = ["cdylib", "rlib"]
path = "src/lib.rs"
[dev-dependencies]
gst-check = { workspace = true, features = ["v1_18"] }
gst-app.workspace = true
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20", features = ["v1_18"] }
gst-app = { package = "gstreamer-app", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
[build-dependencies]
gst-plugin-version-helper.workspace = true
gst-plugin-version-helper = { path="../../version-helper", version = "0.7" }
[features]
static = []
@ -41,7 +41,7 @@ capi = []
doc = ["gst/v1_18"]
[package.metadata.capi]
min_version = "0.9.21"
min_version = "0.8.0"
[package.metadata.capi.header]
enabled = false
@ -49,7 +49,6 @@ enabled = false
[package.metadata.capi.library]
install_subdir = "gstreamer-1.0"
versioning = false
import_library = false
[package.metadata.capi.pkg_config]
requires_private = "gstreamer-1.0, gstreamer-base-1.0, gstreamer-audio-1.0, gobject-2.0, glib-2.0, gmodule-2.0"

View file

@ -1,373 +0,0 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

View file

@ -0,0 +1 @@
../../LICENSE-MPL-2.0

View file

@ -36,7 +36,7 @@ fn run() -> Result<(), Error> {
let uri = &args[1];
let hrir = &args[2];
let pipeline = gst::parse::launch(&format!(
let pipeline = gst::parse_launch(&format!(
"uridecodebin uri={uri} ! audioconvert ! audio/x-raw,channels=1 !
hrtfrender hrir-file={hrir} name=hrtf ! audioresample ! autoaudiosink"
))?

View file

@ -11,8 +11,8 @@ use gst::prelude::*;
use gst::subclass::prelude::*;
use gst_audio::subclass::prelude::*;
use std::cmp;
use std::sync::Mutex;
use std::{cmp, u64};
use byte_slice_cast::*;

View file

@ -20,7 +20,7 @@ pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
gst::Element::register(
Some(plugin),
"rsaudioecho",
gst::Rank::NONE,
gst::Rank::None,
AudioEcho::static_type(),
)
}

View file

@ -18,6 +18,7 @@ use gst::subclass::prelude::*;
use std::mem;
use std::sync::Mutex;
use std::u64;
use byte_slice_cast::*;
@ -347,9 +348,9 @@ impl State {
true_peak
);
// Difference between targeted and calculated LUFS loudness as a linear scalefactor.
// Difference between targetted and calculated LUFS loudness as a linear scalefactor.
let offset = f64::powf(10., (self.target_i - global) / 20.);
// What the new peak would be after adjusting for the targeted loudness.
// What the new peak would be after adjusting for the targetted loudness.
let offset_tp = true_peak * offset;
// If the new peak would be more quiet than targeted one, take it. Otherwise only go as
@ -704,7 +705,7 @@ impl State {
self.process_fill_final_frame(imp, num_samples, FRAME_SIZE);
}
// Now repeatedly run the limiter, output the output gain, update the gains, copy further
// Now repeatadly run the limiter, output the output gain, update the gains, copy further
// data from the buf to limiter_buf until we have output everything.
//
// At this point we have to output 3s - (FRAME_SIZE - num_samples)
@ -781,8 +782,8 @@ impl State {
self.offset
);
let mut outbuf =
gst::Buffer::with_size(mem::size_of_val(src)).map_err(|_| gst::FlowError::Error)?;
let mut outbuf = gst::Buffer::with_size(src.len() * mem::size_of::<f64>())
.map_err(|_| gst::FlowError::Error)?;
{
let outbuf = outbuf.get_mut().unwrap();
let mut dst = outbuf.map_writable().map_err(|_| gst::FlowError::Error)?;
@ -854,15 +855,15 @@ impl State {
smp_cnt += LIMITER_LOOKAHEAD + peak_delta - LIMITER_ATTACK_WINDOW;
gst::debug!(
CAT,
imp: imp,
"Found peak {} at sample {}, going to attack state at sample {} (gain reduction {}-{})",
peak_value,
smp_cnt + LIMITER_ATTACK_WINDOW,
smp_cnt,
self.gain_reduction[0],
self.gain_reduction[1]
);
CAT,
imp: imp,
"Found peak {} at sample {}, going to attack state at sample {} (gain reduction {}-{})",
peak_value,
smp_cnt + LIMITER_ATTACK_WINDOW,
smp_cnt,
self.gain_reduction[0],
self.gain_reduction[1]
);
} else {
// Process all samples, no peak found
smp_cnt = nb_samples;
@ -991,15 +992,15 @@ impl State {
self.sustain_cnt = None;
gst::debug!(
CAT,
imp: imp,
"Found new peak {} at sample {}, restarting attack state at sample {} (gain reduction {}-{})",
peak_value,
smp_cnt + LIMITER_ATTACK_WINDOW,
smp_cnt,
self.gain_reduction[0],
self.gain_reduction[1],
);
CAT,
imp: imp,
"Found new peak {} at sample {}, restarting attack state at sample {} (gain reduction {}-{})",
peak_value,
smp_cnt + LIMITER_ATTACK_WINDOW,
smp_cnt,
self.gain_reduction[0],
self.gain_reduction[1],
);
} else {
// If the slope is lower we can't simply reduce the slope as we would
// then have a lower gain reduction than needed at the previous peak.
@ -1040,15 +1041,15 @@ impl State {
self.sustain_cnt = Some(self.env_cnt);
gst::debug!(
CAT,
imp: imp,
"Found new peak {} at sample {}, adjusting attack state at sample {} (gain reduction {}-{})",
peak_value,
smp_cnt + LIMITER_ATTACK_WINDOW,
smp_cnt,
self.gain_reduction[0],
self.gain_reduction[1],
);
CAT,
imp: imp,
"Found new peak {} at sample {}, adjusting attack state at sample {} (gain reduction {}-{})",
peak_value,
smp_cnt + LIMITER_ATTACK_WINDOW,
smp_cnt,
self.gain_reduction[0],
self.gain_reduction[1],
);
}
return smp_cnt;
} else {
@ -1150,25 +1151,25 @@ impl State {
self.gain_reduction[1] = gain_reduction;
gst::debug!(
CAT,
imp: imp,
"Found new peak {} at sample {}, going back to attack state at sample {} (gain reduction {}-{})",
peak_value,
smp_cnt + LIMITER_ATTACK_WINDOW,
smp_cnt,
self.gain_reduction[0],
self.gain_reduction[1],
);
CAT,
imp: imp,
"Found new peak {} at sample {}, going back to attack state at sample {} (gain reduction {}-{})",
peak_value,
smp_cnt + LIMITER_ATTACK_WINDOW,
smp_cnt,
self.gain_reduction[0],
self.gain_reduction[1],
);
} else {
gst::debug!(
CAT,
imp: imp,
"Found new peak {} at sample {}, going sustain further at sample {} (gain reduction {})",
peak_value,
smp_cnt + LIMITER_ATTACK_WINDOW,
smp_cnt,
self.gain_reduction[1],
);
CAT,
imp: imp,
"Found new peak {} at sample {}, going sustain further at sample {} (gain reduction {})",
peak_value,
smp_cnt + LIMITER_ATTACK_WINDOW,
smp_cnt,
self.gain_reduction[1],
);
// We need to sustain until the peak at least
self.sustain_cnt = Some(LIMITER_LOOKAHEAD);
}
@ -1258,26 +1259,26 @@ impl State {
self.gain_reduction[1] = gain_reduction;
gst::debug!(
CAT,
imp: imp,
"Found new peak {} at sample {}, going back to attack state at sample {} (gain reduction {}-{})",
peak_value,
smp_cnt + LIMITER_ATTACK_WINDOW,
smp_cnt,
self.gain_reduction[0],
self.gain_reduction[1],
);
CAT,
imp: imp,
"Found new peak {} at sample {}, going back to attack state at sample {} (gain reduction {}-{})",
peak_value,
smp_cnt + LIMITER_ATTACK_WINDOW,
smp_cnt,
self.gain_reduction[0],
self.gain_reduction[1],
);
} else {
self.gain_reduction[1] = current_gain_reduction;
gst::debug!(
CAT,
imp: imp,
"Going from release to sustain state at sample {} because of low peak {} at sample {} (gain reduction {})",
smp_cnt,
peak_value,
smp_cnt + LIMITER_ATTACK_WINDOW,
self.gain_reduction[1]
);
CAT,
imp: imp,
"Going from release to sustain state at sample {} because of low peak {} at sample {} (gain reduction {})",
smp_cnt,
peak_value,
smp_cnt + LIMITER_ATTACK_WINDOW,
self.gain_reduction[1]
);
self.limiter_state = LimiterState::Sustain;
}
@ -1689,7 +1690,7 @@ impl ObjectSubclass for AudioLoudNorm {
fn with_class(klass: &Self::Class) -> Self {
let templ = klass.pad_template("sink").unwrap();
let sinkpad = gst::Pad::builder_from_template(&templ)
let sinkpad = gst::Pad::builder_with_template(&templ, Some("sink"))
.chain_function(|pad, parent, buffer| {
Self::catch_panic_pad_function(
parent,
@ -1704,7 +1705,7 @@ impl ObjectSubclass for AudioLoudNorm {
.build();
let templ = klass.pad_template("src").unwrap();
let srcpad = gst::Pad::builder_from_template(&templ)
let srcpad = gst::Pad::builder_with_template(&templ, Some("src"))
.query_function(|pad, parent, query| {
Self::catch_panic_pad_function(parent, || false, |this| this.src_query(pad, query))
})

View file

@ -19,7 +19,7 @@ pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
gst::Element::register(
Some(plugin),
"audioloudnorm",
gst::Rank::NONE,
gst::Rank::None,
AudioLoudNorm::static_type(),
)
}

View file

@ -129,13 +129,10 @@ impl AudioRNNoise {
buffer.set_duration(duration);
buffer.set_pts(pts);
let (level, has_voice) = {
let mut out_map = buffer.map_writable().map_err(|_| gst::FlowError::Error)?;
let out_data = out_map.as_mut_slice_of::<f32>().unwrap();
self.process(state, &settings, in_data, out_data)
};
let mut out_map = buffer.map_writable().map_err(|_| gst::FlowError::Error)?;
let out_data = out_map.as_mut_slice_of::<f32>().unwrap();
gst_audio::AudioLevelMeta::add(buffer, level, has_voice);
self.process(state, &settings, in_data, out_data);
}
self.obj().src_pad().push(buffer)
@ -163,13 +160,10 @@ impl AudioRNNoise {
buffer.set_duration(duration);
buffer.set_pts(pts);
let (level, has_voice) = {
let mut out_map = buffer.map_writable().map_err(|_| gst::FlowError::Error)?;
let out_data = out_map.as_mut_slice_of::<f32>().unwrap();
self.process(state, &settings, in_data, out_data)
};
let mut out_map = buffer.map_writable().map_err(|_| gst::FlowError::Error)?;
let out_data = out_map.as_mut_slice_of::<f32>().unwrap();
gst_audio::AudioLevelMeta::add(buffer, level, has_voice);
self.process(state, &settings, in_data, out_data);
}
Ok(GenerateOutputSuccess::Buffer(buffer))
@ -181,10 +175,9 @@ impl AudioRNNoise {
settings: &Settings,
input_plane: &[f32],
output_plane: &mut [f32],
) -> (u8, bool) {
) {
let channels = state.in_info.channels() as usize;
let size = FRAME_SIZE * channels;
let mut has_voice = false;
for (out_frame, in_frame) in output_plane.chunks_mut(size).zip(input_plane.chunks(size)) {
for (index, item) in in_frame.iter().enumerate() {
@ -214,15 +207,11 @@ impl AudioRNNoise {
);
}
gst::trace!(CAT, imp: self, "Voice activity: {}", vad);
gst::debug!(CAT, imp: self, "Voice activity: {}", vad);
if vad < settings.vad_threshold {
out_frame.fill(0.0);
} else {
// Upon voice activity nnoiseless never really reports a 1.0
// VAD, so we use a hardcoded value close to 1.0 here.
if vad >= 0.98 {
has_voice = true;
}
for (index, item) in out_frame.iter_mut().enumerate() {
let channel_index = index % channels;
let channel_denoiser = &state.denoisers[channel_index];
@ -231,19 +220,6 @@ impl AudioRNNoise {
}
}
}
let rms = output_plane.iter().copied().map(|x| x * x).sum::<f32>();
let level = (20.0 * f32::log10(rms + f32::EPSILON)) as u8;
gst::trace!(
CAT,
imp: self,
"rms: {}, level: {}, has_voice : {} ", rms,
level,
has_voice
);
(level, has_voice)
}
}

View file

@ -20,7 +20,7 @@ pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
gst::Element::register(
Some(plugin),
"audiornnoise",
gst::Rank::NONE,
gst::Rank::None,
AudioRNNoise::static_type(),
)
}

View file

@ -12,6 +12,7 @@ use gst::subclass::prelude::*;
use gst_audio::subclass::prelude::*;
use gst_base::prelude::*;
use std::i32;
use std::sync::atomic;
use std::sync::Mutex;

View file

@ -22,7 +22,7 @@ pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
gst::Element::register(
Some(plugin),
"ebur128level",
gst::Rank::NONE,
gst::Rank::None,
EbuR128Level::static_type(),
)
}

View file

@ -649,7 +649,7 @@ impl BaseTransformImpl for HrtfRender {
if direction == gst::PadDirection::Sink {
s.set("channels", 2);
s.set("channel-mask", gst::Bitmask(0x3));
s.set("channel-mask", 0x3);
} else {
let settings = self.settings.lock().unwrap();
if let Some(objs) = &settings.spatial_objects {

View file

@ -19,7 +19,7 @@ pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
gst::Element::register(
Some(plugin),
"hrtfrender",
gst::Rank::NONE,
gst::Rank::None,
HrtfRender::static_type(),
)
}

View file

@ -39,7 +39,7 @@ fn run_test(
};
let pipeline = if let Some(second_input) = second_input {
gst::parse::launch(&format!(
gst::parse_launch(&format!(
"audiotestsrc {first_input} num-buffers={num_buffers} samplesperbuffer={samples_per_buffer} ! {format} ! audiomixer name=mixer output-buffer-duration={output_buffer_duration} ! {format} ! audioloudnorm ! appsink name=sink audiotestsrc {second_input} num-buffers={num_buffers} samplesperbuffer={samples_per_buffer} ! {format} ! mixer.",
first_input = first_input,
second_input = second_input,
@ -49,7 +49,7 @@ fn run_test(
format = format,
))
} else {
gst::parse::launch(&format!(
gst::parse_launch(&format!(
"audiotestsrc {first_input} num-buffers={num_buffers} samplesperbuffer={samples_per_buffer} ! {format} ! audioloudnorm ! appsink name=sink",
))
}
@ -198,7 +198,7 @@ fn basic_two_channels() {
#[test]
fn silence() {
run_test("wave=silence", None, 1000, 1024, 1, f64::NEG_INFINITY);
run_test("wave=silence", None, 1000, 1024, 1, std::f64::NEG_INFINITY);
}
#[test]
@ -228,7 +228,7 @@ fn below_threshold() {
1000,
1024,
1,
f64::NEG_INFINITY,
std::f64::NEG_INFINITY,
);
}

View file

@ -156,7 +156,7 @@ fn test_hrtfrender_explicit_spatial_objects() {
}
#[test]
// Caps negotiation should fail if we have mismatch between input channels and
// Caps negotation should fail if we have mismatch between input channels and
// of objects that we set via property. In this test case input has 6 channels
// but the number of spatial objects set is 2.
fn test_hrtfrender_caps_negotiation_fail() {

View file

@ -1,23 +1,23 @@
[package]
name = "gst-plugin-claxon"
version.workspace = true
version = "0.10.3"
authors = ["Ruben Gonzalez <rgonzalez@fluendo.com>"]
repository.workspace = true
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
license = "MIT OR Apache-2.0"
description = "GStreamer Claxon FLAC Decoder Plugin"
edition.workspace = true
rust-version.workspace = true
edition = "2021"
rust-version = "1.63"
[dependencies]
gst.workspace = true
gst-audio.workspace = true
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
claxon = { version = "0.4" }
byte-slice-cast = "1.0"
atomic_refcell = "0.1"
once_cell.workspace = true
once_cell = "1"
[dev-dependencies]
gst-check.workspace = true
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
[lib]
name = "gstclaxon"
@ -25,7 +25,7 @@ crate-type = ["cdylib", "rlib"]
path = "src/lib.rs"
[build-dependencies]
gst-plugin-version-helper.workspace = true
gst-plugin-version-helper = { path="../../version-helper", version = "0.7" }
[features]
static = []
@ -33,7 +33,7 @@ capi = []
doc = ["gst/v1_18"]
[package.metadata.capi]
min_version = "0.9.21"
min_version = "0.8.0"
[package.metadata.capi.header]
enabled = false
@ -41,7 +41,6 @@ enabled = false
[package.metadata.capi.library]
install_subdir = "gstreamer-1.0"
versioning = false
import_library = false
[package.metadata.capi.pkg_config]
requires_private = "gstreamer-1.0, gstreamer-audio-1.0, gobject-2.0, glib-2.0, gmodule-2.0"

View file

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

1
audio/claxon/LICENSE-APACHE Symbolic link
View file

@ -0,0 +1 @@
../../LICENSE-APACHE

View file

@ -1,23 +0,0 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

1
audio/claxon/LICENSE-MIT Symbolic link
View file

@ -0,0 +1 @@
../../LICENSE-MIT

View file

@ -209,12 +209,12 @@ impl ClaxonDec {
indata: &[u8],
) -> Result<gst::FlowSuccess, gst::FlowError> {
let streaminfo = claxon_streaminfo(indata).map_err(|e| {
gst::element_imp_error!(self, gst::StreamError::Decode, ["{e}"]);
gst::element_imp_error!(self, gst::StreamError::Decode, [e]);
gst::FlowError::Error
})?;
let audio_info = gstaudioinfo(&streaminfo).map_err(|e| {
gst::element_imp_error!(self, gst::StreamError::Decode, ["{e}"]);
gst::element_imp_error!(self, gst::StreamError::Decode, [&e]);
gst::FlowError::Error
})?;

View file

@ -21,7 +21,7 @@ pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
gst::Element::register(
Some(plugin),
"claxondec",
gst::Rank::MARGINAL,
gst::Rank::Marginal,
ClaxonDec::static_type(),
)
}

View file

@ -1,23 +1,23 @@
[package]
name = "gst-plugin-csound"
version.workspace = true
version = "0.10.3"
authors = ["Natanael Mojica <neithanmo@gmail.com>"]
repository.workspace = true
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
license = "MPL-2.0"
edition.workspace = true
rust-version.workspace = true
edition = "2021"
rust-version = "1.63"
description = "GStreamer Audio Filter plugin based on Csound"
[dependencies]
gst.workspace = true
gst-base.workspace = true
gst-audio.workspace = true
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
csound = "0.1.8"
once_cell = "1.0"
byte-slice-cast = "1.0"
once_cell.workspace = true
[dev-dependencies]
gst-check.workspace = true
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
[lib]
name = "gstcsound"
@ -29,7 +29,7 @@ name = "csound-effect"
path = "examples/effect_example.rs"
[build-dependencies]
gst-plugin-version-helper.workspace = true
gst-plugin-version-helper = { path = "../../version-helper", version = "0.7" }
[features]
static = []
@ -37,7 +37,7 @@ capi = []
doc = ["gst/v1_18"]
[package.metadata.capi]
min_version = "0.9.21"
min_version = "0.8.0"
[package.metadata.capi.header]
enabled = false
@ -45,7 +45,6 @@ enabled = false
[package.metadata.capi.library]
install_subdir = "gstreamer-1.0"
versioning = false
import_library = false
[package.metadata.capi.pkg_config]
requires_private = "gstreamer-1.0, gstreamer-base-1.0, gstreamer-audio-1.0, gobject-2.0, glib-2.0, gmodule-2.0, csound"

View file

@ -1,373 +0,0 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

View file

@ -0,0 +1 @@
../../LICENSE-MPL-2.0

View file

@ -19,7 +19,7 @@ const AUDIO_SINK: &str = "audioconvert ! autoaudiosink";
// from the global accumulator(gasig), then reads these buffers at a fixed delay time, creating the adelL, adelM and adelR buffers,
// also, It multiplies the audio samples in the right channel by 0.5 * kdel, being kdel a line of values starting at 0.5 at increments of 0.001.
// Finally, those buffers are mixed with the accumulator, and an audio envelop is applied(aseg) to them.
// The result is similar to an audio echo in which the buffered samples are read at different delay times and also modified in frequency(right channel),
// The result is similar to an audio echo in which the buffered samples are read at different delay times and also modified in frecuency(right channel),
// this creates an space effect using just one channel audio input.
const CSD: &str = "
<CsoundSynthesizer>
@ -75,16 +75,16 @@ const CSD: &str = "
fn create_pipeline() -> Result<gst::Pipeline, Box<dyn Error>> {
let pipeline = gst::Pipeline::default();
let audio_src = gst::parse::bin_from_description(AUDIO_SRC, true)?.upcast();
let audio_src = gst::parse_bin_from_description(AUDIO_SRC, true)?.upcast();
let audio_sink = gst::parse::bin_from_description(AUDIO_SINK, true)?.upcast();
let audio_sink = gst::parse_bin_from_description(AUDIO_SINK, true)?.upcast();
let csoundfilter = gst::ElementFactory::make("csoundfilter")
.property("csd-text", CSD)
.build()
.unwrap();
pipeline.add_many([&audio_src, &csoundfilter, &audio_sink])?;
pipeline.add_many(&[&audio_src, &csoundfilter, &audio_sink])?;
audio_src.link_pads(Some("src"), &csoundfilter, Some("sink"))?;
csoundfilter.link_pads(Some("src"), &audio_sink, Some("sink"))?;

View file

@ -17,6 +17,7 @@ use gst_base::subclass::prelude::*;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Mutex;
use std::{f64, i32};
use byte_slice_cast::*;
@ -126,9 +127,9 @@ impl CsoundFilter {
let spout = csound.get_spout().unwrap();
let in_chunks = idata.chunks_exact(spin.len());
let out_chunks = odata.chunks_exact_mut(spout.len());
let out_chuncks = odata.chunks_exact_mut(spout.len());
let mut end_score = false;
for (ichunk, ochunk) in in_chunks.zip(out_chunks) {
for (ichunk, ochunk) in in_chunks.zip(out_chuncks) {
spin.copy_from_slice(ichunk);
end_score = csound.perform_ksmps();
spout.copy_to_slice(ochunk);
@ -143,11 +144,11 @@ impl CsoundFilter {
if let Some(ref location) = settings.location {
csound
.compile_csd(location)
.map_err(|e| error_msg!(gst::LibraryError::Failed, ["{e}"]))?;
.map_err(|e| error_msg!(gst::LibraryError::Failed, [e]))?;
} else if let Some(ref text) = settings.csd_text {
csound
.compile_csd_text(text)
.map_err(|e| error_msg!(gst::LibraryError::Failed, ["{e}"]))?;
.map_err(|e| error_msg!(gst::LibraryError::Failed, [e]))?;
} else {
return Err(error_msg!(
gst::LibraryError::Failed,
@ -253,7 +254,7 @@ impl CsoundFilter {
);
// Get the required amount of bytes to be read from
// the adapter to fill an output buffer of size output_size
// the adapter to fill an ouput buffer of size output_size
let bytes_to_read = state.bytes_to_read(output_size);
let indata = state
@ -469,7 +470,7 @@ impl BaseTransformImpl for CsoundFilter {
csound.set_score_offset_seconds(settings.offset);
if let Err(e) = csound.start() {
return Err(error_msg!(gst::LibraryError::Failed, ["{e}"]));
return Err(error_msg!(gst::LibraryError::Failed, [e]));
}
Ok(())
@ -553,7 +554,7 @@ impl BaseTransformImpl for CsoundFilter {
// Flush previous state
if self.state.lock().unwrap().is_some() {
self.drain()
.map_err(|e| loggable_error!(CAT, "Error flushing previous state data {:?}", e))?;
.map_err(|e| loggable_error!(CAT, "Error flusing previous state data {:?}", e))?;
}
let in_info = gst_audio::AudioInfo::from_caps(incaps)

View file

@ -19,7 +19,7 @@ pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
gst::Element::register(
Some(plugin),
"csoundfilter",
gst::Rank::NONE,
gst::Rank::None,
CsoundFilter::static_type(),
)
}

View file

@ -1,23 +1,23 @@
[package]
name = "gst-plugin-lewton"
version.workspace = true
version = "0.10.3"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
repository.workspace = true
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
license = "MIT OR Apache-2.0"
description = "GStreamer lewton Vorbis Decoder Plugin"
edition.workspace = true
rust-version.workspace = true
edition = "2021"
rust-version = "1.63"
[dependencies]
gst.workspace = true
gst-audio.workspace = true
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
lewton = { version = "0.10", default-features = false }
byte-slice-cast = "1.0"
atomic_refcell = "0.1"
once_cell.workspace = true
once_cell = "1.0"
[dev-dependencies]
gst-check.workspace = true
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
[lib]
name = "gstlewton"
@ -25,7 +25,7 @@ crate-type = ["cdylib", "rlib"]
path = "src/lib.rs"
[build-dependencies]
gst-plugin-version-helper.workspace = true
gst-plugin-version-helper = { path="../../version-helper", version = "0.7" }
[features]
static = []
@ -33,7 +33,7 @@ capi = []
doc = ["gst/v1_18"]
[package.metadata.capi]
min_version = "0.9.21"
min_version = "0.8.0"
[package.metadata.capi.header]
enabled = false
@ -41,7 +41,6 @@ enabled = false
[package.metadata.capi.library]
install_subdir = "gstreamer-1.0"
versioning = false
import_library = false
[package.metadata.capi.pkg_config]
requires_private = "gstreamer-1.0, gstreamer-audio-1.0, gobject-2.0, glib-2.0, gmodule-2.0"

View file

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

1
audio/lewton/LICENSE-APACHE Symbolic link
View file

@ -0,0 +1 @@
../../LICENSE-APACHE

View file

@ -1,23 +0,0 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

1
audio/lewton/LICENSE-MIT Symbolic link
View file

@ -0,0 +1 @@
../../LICENSE-MIT

View file

@ -132,7 +132,7 @@ impl AudioDecoderImpl for LewtonDec {
reorder_map: None,
});
let state = state_guard.as_mut().unwrap();
let mut state = state_guard.as_mut().unwrap();
let s = caps.structure(0).unwrap();
if let Ok(Some(streamheaders)) = s.get_optional::<gst::ArrayRef>("streamheader") {

View file

@ -21,7 +21,7 @@ pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
gst::Element::register(
Some(plugin),
"lewtondec",
gst::Rank::MARGINAL,
gst::Rank::Marginal,
LewtonDec::static_type(),
)
}

View file

@ -1,22 +1,22 @@
[package]
name = "gst-plugin-spotify"
version.workspace = true
version = "0.10.3"
authors = ["Guillaume Desmottes <guillaume@desmottes.be>"]
repository.workspace = true
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
license = "MPL-2.0"
description = "GStreamer Spotify Plugin"
edition.workspace = true
rust-version.workspace = true
edition = "2021"
rust-version = "1.63"
[dependencies]
gst.workspace = true
gst-base.workspace = true
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
once_cell = "1.0"
librespot = { version = "0.4", default-features = false }
tokio = "1.0"
futures = "0.3"
anyhow = "1.0"
url = "2.3"
once_cell.workspace = true
[lib]
name = "gstspotify"
@ -24,7 +24,7 @@ crate-type = ["cdylib", "rlib"]
path = "src/lib.rs"
[build-dependencies]
gst-plugin-version-helper.workspace = true
gst-plugin-version-helper = { path="../../version-helper", version = "0.7" }
[features]
static = []
@ -32,7 +32,7 @@ capi = []
doc = ["gst/v1_18"]
[package.metadata.capi]
min_version = "0.9.21"
min_version = "0.8.0"
[package.metadata.capi.header]
enabled = false
@ -40,7 +40,6 @@ enabled = false
[package.metadata.capi.library]
install_subdir = "gstreamer-1.0"
versioning = false
import_library = false
[package.metadata.capi.pkg_config]
requires_private = "gstreamer-1.0, gstreamer-base-1.0, gobject-2.0, glib-2.0, gmodule-2.0"

View file

@ -1,373 +0,0 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

View file

@ -0,0 +1 @@
../../LICENSE-MPL-2.0

View file

@ -8,11 +8,10 @@ to respect their legal/licensing restrictions.
## Spotify Credentials
This plugin requires a [Spotify Premium](https://www.spotify.com/premium/) account.
If your account is linked with Facebook, you'll need to setup
a [device username and password](https://www.spotify.com/us/account/set-device-password/).
This plugin requires a [Spotify Premium](https://www.spotify.com/premium/) account configured
with a [device password](https://www.spotify.com/us/account/set-device-password/).
Those username and password are then set using the `username` and `password` properties.
You can then set the device username and password using the `username` and `password` properties.
You may also want to cache credentials and downloaded files, see the `cache-` properties on the element.

View file

@ -1,194 +0,0 @@
// Copyright (C) 2021 Guillaume Desmottes <guillaume@desmottes.be>
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// <https://mozilla.org/MPL/2.0/>.
//
// SPDX-License-Identifier: MPL-2.0
use anyhow::bail;
use gst::glib;
use gst::prelude::*;
use librespot::core::{
cache::Cache, config::SessionConfig, session::Session, spotify_id::SpotifyId,
};
use librespot::discovery::Credentials;
#[derive(Default, Debug, Clone)]
pub struct Settings {
username: String,
password: String,
cache_credentials: String,
cache_files: String,
cache_max_size: u64,
pub track: String,
}
impl Settings {
pub fn properties() -> Vec<glib::ParamSpec> {
vec![glib::ParamSpecString::builder("username")
.nick("Username")
.blurb("Spotify username, Facebook accounts need a device username from https://www.spotify.com/us/account/set-device-password/")
.default_value(Some(""))
.mutable_ready()
.build(),
glib::ParamSpecString::builder("password")
.nick("Password")
.blurb("Spotify password, Facebook accounts need a device password from https://www.spotify.com/us/account/set-device-password/")
.default_value(Some(""))
.mutable_ready()
.build(),
glib::ParamSpecString::builder("cache-credentials")
.nick("Credentials cache")
.blurb("Directory where to cache Spotify credentials")
.default_value(Some(""))
.mutable_ready()
.build(),
glib::ParamSpecString::builder("cache-files")
.nick("Files cache")
.blurb("Directory where to cache downloaded files from Spotify")
.default_value(Some(""))
.mutable_ready()
.build(),
glib::ParamSpecUInt64::builder("cache-max-size")
.nick("Cache max size")
.blurb("The max allowed size of the cache, in bytes, or 0 to disable the cache limit")
.default_value(0)
.mutable_ready()
.build(),
glib::ParamSpecString::builder("track")
.nick("Spotify URI")
.blurb("Spotify track URI, in the form 'spotify:track:$SPOTIFY_ID'")
.default_value(Some(""))
.mutable_ready()
.build(),
]
}
pub fn set_property(&mut self, value: &glib::Value, pspec: &glib::ParamSpec) {
match pspec.name() {
"username" => {
self.username = value.get().expect("type checked upstream");
}
"password" => {
self.password = value.get().expect("type checked upstream");
}
"cache-credentials" => {
self.cache_credentials = value.get().expect("type checked upstream");
}
"cache-files" => {
self.cache_files = value.get().expect("type checked upstream");
}
"cache-max-size" => {
self.cache_max_size = value.get().expect("type checked upstream");
}
"track" => {
self.track = value.get().expect("type checked upstream");
}
_ => unimplemented!(),
}
}
pub fn property(&self, pspec: &glib::ParamSpec) -> glib::Value {
match pspec.name() {
"username" => self.username.to_value(),
"password" => self.password.to_value(),
"cache-credentials" => self.cache_credentials.to_value(),
"cache-files" => self.cache_files.to_value(),
"cache-max-size" => self.cache_max_size.to_value(),
"track" => self.track.to_value(),
_ => unimplemented!(),
}
}
pub async fn connect_session<T>(
&self,
src: T,
cat: &gst::DebugCategory,
) -> anyhow::Result<Session>
where
T: IsA<glib::Object>,
{
let credentials_cache = if self.cache_credentials.is_empty() {
None
} else {
Some(&self.cache_credentials)
};
let files_cache = if self.cache_files.is_empty() {
None
} else {
Some(&self.cache_files)
};
let max_size = if self.cache_max_size != 0 {
Some(self.cache_max_size)
} else {
None
};
let cache = Cache::new(credentials_cache, None, files_cache, max_size)?;
if let Some(cached_cred) = cache.credentials() {
if !self.username.is_empty() && self.username != cached_cred.username {
gst::debug!(
cat,
obj: &src,
"ignore cached credentials for user {} which mismatch user {}",
cached_cred.username,
self.username
);
} else {
gst::debug!(
cat,
obj: &src,
"reuse cached credentials for user {}",
cached_cred.username
);
if let Ok((session, _credentials)) = Session::connect(
SessionConfig::default(),
cached_cred,
Some(cache.clone()),
true,
)
.await
{
return Ok(session);
}
}
}
gst::debug!(
cat,
obj: &src,
"credentials not in cache or cached credentials invalid",
);
if self.username.is_empty() {
bail!("username is not set and credentials are not in cache");
}
if self.password.is_empty() {
bail!("password is not set and credentials are not in cache");
}
let cred = Credentials::with_password(&self.username, &self.password);
let (session, _credentials) =
Session::connect(SessionConfig::default(), cred, Some(cache), true).await?;
Ok(session)
}
pub fn track_id(&self) -> anyhow::Result<SpotifyId> {
if self.track.is_empty() {
bail!("track is not set");
}
let track = SpotifyId::from_uri(&self.track).map_err(|_| {
anyhow::anyhow!("failed to create Spotify URI from track {}", self.track)
})?;
Ok(track)
}
}

View file

@ -14,7 +14,6 @@
*/
use gst::glib;
mod common;
mod spotifyaudiosrc;
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {

View file

@ -6,8 +6,9 @@
//
// SPDX-License-Identifier: MPL-2.0
use std::sync::{mpsc, Arc, Mutex};
use std::sync::{mpsc, Arc, Mutex, MutexGuard};
use anyhow::bail;
use futures::future::{AbortHandle, Abortable, Aborted};
use once_cell::sync::Lazy;
use tokio::{runtime, task::JoinHandle};
@ -17,6 +18,10 @@ use gst::prelude::*;
use gst::subclass::prelude::*;
use gst_base::subclass::{base_src::CreateSuccess, prelude::*};
use librespot::core::{
cache::Cache, config::SessionConfig, session::Session, spotify_id::SpotifyId,
};
use librespot::discovery::Credentials;
use librespot::playback::{
audio_backend::{Sink, SinkResult},
config::PlayerConfig,
@ -62,46 +67,27 @@ struct State {
#[derive(Default)]
struct Settings {
common: crate::common::Settings,
username: String,
password: String,
cache_credentials: String,
cache_files: String,
cache_max_size: u64,
track: String,
bitrate: Bitrate,
}
#[derive(Default)]
enum SetupThread {
#[default]
None,
Pending {
thread_handle: Option<std::thread::JoinHandle<Result<anyhow::Result<()>, Aborted>>>,
abort_handle: AbortHandle,
},
Cancelled,
Done,
}
impl SetupThread {
fn abort(&mut self) {
// Cancel setup thread if it is pending and not done yet
if matches!(self, SetupThread::None | SetupThread::Done) {
return;
}
if let SetupThread::Pending {
ref abort_handle, ..
} = *self
{
abort_handle.abort();
}
*self = SetupThread::Cancelled;
}
}
#[derive(Default)]
pub struct SpotifyAudioSrc {
setup_thread: Mutex<SetupThread>,
setup_thread: Mutex<Option<SetupThread>>,
state: Arc<Mutex<Option<State>>>,
settings: Mutex<Settings>,
}
struct SetupThread {
thread_handle: std::thread::JoinHandle<Result<anyhow::Result<()>, Aborted>>,
abort_handle: AbortHandle,
}
#[glib::object_subclass]
impl ObjectSubclass for SpotifyAudioSrc {
const NAME: &'static str = "GstSpotifyAudioSrc";
@ -113,39 +99,118 @@ impl ObjectSubclass for SpotifyAudioSrc {
impl ObjectImpl for SpotifyAudioSrc {
fn properties() -> &'static [glib::ParamSpec] {
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
let mut props = crate::common::Settings::properties();
let default = Settings::default();
props.push(
glib::ParamSpecEnum::builder_with_default::<Bitrate>("bitrate", default.bitrate)
.nick("Spotify bitrate")
.blurb("Spotify audio bitrate in kbit/s")
vec![glib::ParamSpecString::builder("username")
.nick("Username")
.blurb("Spotify device username from https://www.spotify.com/us/account/set-device-password/")
.default_value(Some(""))
.mutable_ready()
.build(),
);
props
glib::ParamSpecString::builder("password")
.nick("Password")
.blurb("Spotify device password from https://www.spotify.com/us/account/set-device-password/")
.default_value(Some(""))
.mutable_ready()
.build(),
glib::ParamSpecString::builder("cache-credentials")
.nick("Credentials cache")
.blurb("Directory where to cache Spotify credentials")
.default_value(Some(""))
.mutable_ready()
.build(),
glib::ParamSpecString::builder("cache-files")
.nick("Files cache")
.blurb("Directory where to cache downloaded files from Spotify")
.default_value(Some(""))
.mutable_ready()
.build(),
glib::ParamSpecUInt64::builder("cache-max-size")
.nick("Cache max size")
.blurb("The max allowed size of the cache, in bytes, or 0 to disable the cache limit")
.default_value(0)
.mutable_ready()
.build(),
glib::ParamSpecString::builder("track")
.nick("Spotify URI")
.blurb("Spotify track URI, in the form 'spotify:track:$SPOTIFY_ID'")
.default_value(Some(""))
.mutable_ready()
.build(),
glib::ParamSpecEnum::builder::<Bitrate>("bitrate")
.nick("Spotify bitrate")
.blurb("Spotify audio bitrate in kbit/s")
.mutable_ready()
.build()
]
});
PROPERTIES.as_ref()
}
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
let mut settings = self.settings.lock().unwrap();
match pspec.name() {
"username" => {
let mut settings = self.settings.lock().unwrap();
settings.username = value.get().expect("type checked upstream");
}
"password" => {
let mut settings = self.settings.lock().unwrap();
settings.password = value.get().expect("type checked upstream");
}
"cache-credentials" => {
let mut settings = self.settings.lock().unwrap();
settings.cache_credentials = value.get().expect("type checked upstream");
}
"cache-files" => {
let mut settings = self.settings.lock().unwrap();
settings.cache_files = value.get().expect("type checked upstream");
}
"cache-max-size" => {
let mut settings = self.settings.lock().unwrap();
settings.cache_max_size = value.get().expect("type checked upstream");
}
"track" => {
let mut settings = self.settings.lock().unwrap();
settings.track = value.get().expect("type checked upstream");
}
"bitrate" => {
let mut settings = self.settings.lock().unwrap();
settings.bitrate = value.get().expect("type checked upstream");
}
_ => settings.common.set_property(value, pspec),
_ => unimplemented!(),
}
}
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
let settings = self.settings.lock().unwrap();
match pspec.name() {
"bitrate" => settings.bitrate.to_value(),
_ => settings.common.property(pspec),
"username" => {
let settings = self.settings.lock().unwrap();
settings.username.to_value()
}
"password" => {
let settings = self.settings.lock().unwrap();
settings.password.to_value()
}
"cache-credentials" => {
let settings = self.settings.lock().unwrap();
settings.cache_credentials.to_value()
}
"cache-files" => {
let settings = self.settings.lock().unwrap();
settings.cache_files.to_value()
}
"cache-max-size" => {
let settings = self.settings.lock().unwrap();
settings.cache_max_size.to_value()
}
"track" => {
let settings = self.settings.lock().unwrap();
settings.track.to_value()
}
"bitrate" => {
let settings = self.settings.lock().unwrap();
settings.bitrate.to_value()
}
_ => unimplemented!(),
}
}
}
@ -196,18 +261,21 @@ impl BaseSrcImpl for SpotifyAudioSrc {
}
{
// If not started yet and not cancelled, start the setup
let mut setup_thread = self.setup_thread.lock().unwrap();
assert!(!matches!(&*setup_thread, SetupThread::Cancelled));
if matches!(&*setup_thread, SetupThread::None) {
self.start_setup(&mut setup_thread);
let setup_thread = self.setup_thread.lock().unwrap();
if setup_thread.is_some() {
// already starting
return Ok(());
}
self.start_setup(setup_thread);
}
Ok(())
}
fn stop(&self) -> Result<(), gst::ErrorMessage> {
// stop the setup if it's not completed yet
self.cancel_setup();
if let Some(state) = self.state.lock().unwrap().take() {
gst::debug!(CAT, imp: self, "stopping");
state.player.stop();
@ -220,17 +288,9 @@ impl BaseSrcImpl for SpotifyAudioSrc {
}
fn unlock(&self) -> Result<(), gst::ErrorMessage> {
let mut setup_thread = self.setup_thread.lock().unwrap();
setup_thread.abort();
Ok(())
}
self.cancel_setup();
fn unlock_stop(&self) -> Result<(), gst::ErrorMessage> {
let mut setup_thread = self.setup_thread.lock().unwrap();
if matches!(&*setup_thread, SetupThread::Cancelled) {
*setup_thread = SetupThread::None;
}
Ok(())
self.parent_unlock()
}
}
@ -245,47 +305,31 @@ impl PushSrcImpl for SpotifyAudioSrc {
};
if !state_set {
// If not started yet and not cancelled, start the setup
let mut setup_thread = self.setup_thread.lock().unwrap();
if matches!(&*setup_thread, SetupThread::Cancelled) {
return Err(gst::FlowError::Flushing);
}
if matches!(&*setup_thread, SetupThread::None) {
self.start_setup(&mut setup_thread);
let setup_thread = self.setup_thread.lock().unwrap();
if setup_thread.is_none() {
// unlock() could potentially cancel the setup, and create() can be called after unlock() without going through start() again.
self.start_setup(setup_thread);
}
}
{
// wait for the setup to be completed
let mut setup_thread = self.setup_thread.lock().unwrap();
if let SetupThread::Pending {
ref mut thread_handle,
..
} = *setup_thread
{
let thread_handle = thread_handle.take().expect("Waiting multiple times");
drop(setup_thread);
let res = thread_handle.join().unwrap();
if let Some(setup) = setup_thread.take() {
let res = setup.thread_handle.join().unwrap();
match res {
Err(_aborted) => {
gst::debug!(CAT, imp: self, "setup has been cancelled");
setup_thread = self.setup_thread.lock().unwrap();
*setup_thread = SetupThread::Cancelled;
return Err(gst::FlowError::Flushing);
}
Ok(Err(err)) => {
gst::error!(CAT, imp: self, "failed to start: {err:?}");
gst::element_imp_error!(self, gst::ResourceError::Settings, ["{err:?}"]);
setup_thread = self.setup_thread.lock().unwrap();
*setup_thread = SetupThread::None;
let details = format!("{err:?}");
gst::error!(CAT, imp: self, "failed to start: {}", details);
gst::element_imp_error!(self, gst::ResourceError::Settings, [&details]);
return Err(gst::FlowError::Error);
}
Ok(Ok(_)) => {
setup_thread = self.setup_thread.lock().unwrap();
*setup_thread = SetupThread::Done;
}
Ok(Ok(_)) => {}
}
}
}
@ -344,10 +388,10 @@ impl URIHandlerImpl for SpotifyAudioSrc {
fn uri(&self) -> Option<String> {
let settings = self.settings.lock().unwrap();
if settings.common.track.is_empty() {
if settings.track.is_empty() {
None
} else {
Some(settings.common.track.clone())
Some(settings.track.clone())
}
}
@ -377,9 +421,7 @@ impl URIHandlerImpl for SpotifyAudioSrc {
}
impl SpotifyAudioSrc {
fn start_setup(&self, setup_thread: &mut SetupThread) {
assert!(matches!(setup_thread, SetupThread::None));
fn start_setup(&self, mut setup_thread: MutexGuard<Option<SetupThread>>) {
let self_ = self.to_owned();
// run the runtime from another thread to prevent the "start a runtime from within a runtime" panic
@ -392,10 +434,10 @@ impl SpotifyAudioSrc {
})
});
*setup_thread = SetupThread::Pending {
thread_handle: Some(thread_handle),
setup_thread.replace(SetupThread {
thread_handle,
abort_handle,
};
});
}
async fn setup(&self) -> anyhow::Result<()> {
@ -408,23 +450,63 @@ impl SpotifyAudioSrc {
}
}
let src = self.obj();
let (credentials, cache, track, bitrate) = {
let settings = self.settings.lock().unwrap();
let (session, track, bitrate) = {
let (common, bitrate) = {
let settings = self.settings.lock().unwrap();
let bitrate = settings.bitrate.into();
(settings.common.clone(), bitrate)
let credentials_cache = if settings.cache_credentials.is_empty() {
None
} else {
Some(&settings.cache_credentials)
};
let session = common.connect_session(src.clone(), &CAT).await?;
let track = common.track_id()?;
let files_cache = if settings.cache_files.is_empty() {
None
} else {
Some(&settings.cache_files)
};
let max_size = if settings.cache_max_size != 0 {
Some(settings.cache_max_size)
} else {
None
};
let cache = Cache::new(credentials_cache, None, files_cache, max_size)?;
let credentials = match cache.credentials() {
Some(cached_cred) => {
gst::debug!(CAT, imp: self, "reuse credentials from cache",);
cached_cred
}
None => {
gst::debug!(CAT, imp: self, "credentials not in cache",);
if settings.username.is_empty() {
bail!("username is not set and credentials are not in cache");
}
if settings.password.is_empty() {
bail!("password is not set and credentials are not in cache");
}
let cred = Credentials::with_password(&settings.username, &settings.password);
cache.save_credentials(&cred);
cred
}
};
if settings.track.is_empty() {
bail!("track is not set")
}
let bitrate = settings.bitrate.into();
gst::debug!(CAT, imp: self, "Requesting bitrate {:?}", bitrate);
(session, track, bitrate)
(credentials, cache, settings.track.clone(), bitrate)
};
let (session, _credentials) =
Session::connect(SessionConfig::default(), credentials, Some(cache), false).await?;
let player_config = PlayerConfig {
passthrough: true,
bitrate,
@ -440,6 +522,11 @@ impl SpotifyAudioSrc {
Box::new(BufferSink { sender })
});
let track = match SpotifyId::from_uri(&track) {
Ok(track) => track,
Err(_) => bail!("Failed to create Spotify URI from track"),
};
player.load(track, true, 0);
let player_channel_handle = RUNTIME.spawn(async move {
@ -468,4 +555,12 @@ impl SpotifyAudioSrc {
Ok(())
}
fn cancel_setup(&self) {
let mut setup_thread = self.setup_thread.lock().unwrap();
if let Some(setup) = setup_thread.take() {
setup.abort_handle.abort();
}
}
}

View file

@ -50,7 +50,7 @@ pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
gst::Element::register(
Some(plugin),
"spotifyaudiosrc",
gst::Rank::PRIMARY,
gst::Rank::Primary,
SpotifyAudioSrc::static_type(),
)
}

View file

@ -7,7 +7,6 @@ import os
import shutil
import subprocess
import sys
import shlex
from argparse import ArgumentParser
from pathlib import Path as P
@ -30,12 +29,6 @@ PARSER.add_argument('--depfile')
PARSER.add_argument('--disable-doc', action="store_true", default=False)
def shlex_join(args):
if hasattr(shlex, 'join'):
return shlex.join(args)
return ' '.join([shlex.quote(arg) for arg in args])
def generate_depfile_for(fpath):
file_stem = fpath.parent / fpath.stem
depfile_content = ""
@ -89,21 +82,6 @@ if __name__ == "__main__":
pkg_config_path.append(str(opts.root_dir / 'meson-uninstalled'))
env['PKG_CONFIG_PATH'] = os.pathsep.join(pkg_config_path)
if 'NASM' in env:
env['PATH'] = os.pathsep.join([os.path.dirname(env['NASM']), env['PATH']])
rustc_target = None
if 'RUSTC' in env:
rustc_cmdline = shlex.split(env['RUSTC'])
# grab target from RUSTFLAGS
rust_flags = rustc_cmdline[1:] + shlex.split(env.get('RUSTFLAGS', ''))
if '--target' in rust_flags:
rustc_target_idx = rust_flags.index('--target')
_ = rust_flags.pop(rustc_target_idx) # drop '--target'
rustc_target = rust_flags.pop(rustc_target_idx)
env['RUSTFLAGS'] = shlex_join(rust_flags)
env['RUSTC'] = rustc_cmdline[0]
features = opts.features
if opts.command == 'build':
cargo_cmd = ['cargo']
@ -122,8 +100,6 @@ if __name__ == "__main__":
print("Unknown command:", opts.command, file=logfile)
sys.exit(1)
if rustc_target:
cargo_cmd += ['--target', rustc_target]
if features:
cargo_cmd += ['--features', ','.join(features)]
cargo_cmd += ['--target-dir', cargo_target_dir]

View file

@ -1,103 +0,0 @@
#!/usr/bin/python3
#
# Copied from gstreamer.git/ci/gitlab/trigger_cerbero_pipeline.py
import time
import os
import sys
import gitlab
CERBERO_PROJECT = 'gstreamer/cerbero'
class Status:
FAILED = 'failed'
MANUAL = 'manual'
CANCELED = 'canceled'
SUCCESS = 'success'
SKIPPED = 'skipped'
CREATED = 'created'
@classmethod
def is_finished(cls, state):
return state in [
cls.FAILED,
cls.MANUAL,
cls.CANCELED,
cls.SUCCESS,
cls.SKIPPED,
]
def fprint(msg):
print(msg, end="")
sys.stdout.flush()
if __name__ == "__main__":
server = os.environ['CI_SERVER_URL']
gl = gitlab.Gitlab(server,
private_token=os.environ.get('GITLAB_API_TOKEN'),
job_token=os.environ.get('CI_JOB_TOKEN'))
def get_matching_user_project(project, branch):
cerbero = gl.projects.get(project)
# Search for matching branches, return only if the branch name matches
# exactly
for b in cerbero.branches.list(search=cerbero_branch, iterator=True):
if branch == b.name:
return cerbero
return None
cerbero = None
# We do not want to run on (often out of date) user upstream branch
if os.environ["CI_COMMIT_REF_NAME"] != os.environ['CERBERO_UPSTREAM_BRANCH']:
try:
cerbero_name = f'{os.environ["CI_PROJECT_NAMESPACE"]}/cerbero'
cerbero_branch = os.environ["CI_COMMIT_REF_NAME"]
cerbero = get_matching_user_project(cerbero_name, cerbero_branch)
except gitlab.exceptions.GitlabGetError:
pass
if cerbero is None:
cerbero_name = CERBERO_PROJECT
cerbero_branch = os.environ["CERBERO_UPSTREAM_BRANCH"]
cerbero = gl.projects.get(cerbero_name)
fprint(f"-> Triggering on branch {cerbero_branch} in {cerbero_name}\n")
# CI_PROJECT_URL is not necessarily the project where the branch we need to
# build resides, for instance merge request pipelines can be run on
# 'gstreamer' namespace. Fetch the branch name in the same way, just in
# case it breaks in the future.
if 'CI_MERGE_REQUEST_SOURCE_PROJECT_URL' in os.environ:
project_url = os.environ['CI_MERGE_REQUEST_SOURCE_PROJECT_URL']
project_branch = os.environ['CI_MERGE_REQUEST_SOURCE_BRANCH_NAME']
else:
project_url = os.environ['CI_PROJECT_URL']
project_branch = os.environ['CI_COMMIT_REF_NAME']
variables = {
"CI_GST_PLUGINS_RS_URL": project_url,
"CI_GST_PLUGINS_RS_REF_NAME": project_branch,
# This tells cerbero CI that this is a pipeline started via the
# trigger API, which means it can use a deps cache instead of
# building from scratch.
"CI_GSTREAMER_TRIGGERED": "true",
}
pipe = cerbero.trigger_pipeline(
token=os.environ['CI_JOB_TOKEN'],
ref=cerbero_branch,
variables=variables,
)
fprint(f'Cerbero pipeline running at {pipe.web_url} ')
while True:
time.sleep(15)
pipe.refresh()
if Status.is_finished(pipe.status):
fprint(f": {pipe.status}\n")
sys.exit(0 if pipe.status == Status.SUCCESS else 1)
else:
fprint(".")

View file

@ -1,12 +0,0 @@
#!/bin/bash
LINKS=`find -type l`
if test -z $LINKS; then
echo "No symlinks found."
else
echo "===> FOUND SYMLINKS!"
echo
echo "$LINKS"
echo
echo "Please replace these with actual files."
exit 1;
fi

View file

@ -1,14 +0,0 @@
#!/bin/bash
MESON_VERSION=`head -n5 meson.build | grep ' version\s*:' | sed -e "s/.*version\s*:\s*'//" -e "s/',.*//"`
CARGO_VERSION=`cat Cargo.toml | grep -A1 workspace.package | grep ^version | sed -e 's/^version = "\(.*\)"/\1/'`
echo "gst-plugins-rs version (meson.build) : $MESON_VERSION"
echo "gst-plugins-rs version (Cargo.toml) : $CARGO_VERSION"
if test "x$MESON_VERSION" != "x$CARGO_VERSION"; then
echo
echo "===> Version mismatch between meson.build and Cargo.toml! <==="
echo
exit 1;
fi

View file

@ -1,6 +1,6 @@
set -e
RELEASE=1.1.0
RELEASE=1.0.0
git clone https://code.videolan.org/videolan/dav1d.git --branch $RELEASE
cd dav1d

View file

@ -3,4 +3,4 @@ source ./ci/env.sh
set -e
export CARGO_HOME='/usr/local/cargo'
cargo install cargo-c --version 0.9.15+cargo-0.67
cargo install cargo-c --version 0.9.14+cargo-0.66

View file

@ -36,7 +36,6 @@ function Run-Tests {
}
$env:G_DEBUG="fatal_warnings"
$env:RUST_BACKTRACE="1"
cargo test --no-fail-fast --color=always --workspace $local_exclude --all-targets $Features
if (!$?) {

View file

@ -22,8 +22,6 @@ RS_PREFIXED = [
'png',
'tracers',
'rtp',
'rtsp',
'inter',
]
OVERRIDE = {

235
deny.toml
View file

@ -10,17 +10,27 @@ ignore = [
"RUSTSEC-2021-0060",
"RUSTSEC-2021-0061",
"RUSTSEC-2021-0145",
# https://github.com/chronotope/chrono/issues/499
"RUSTSEC-2020-0071",
# sodiumoxide is deprecated
"RUSTSEC-2021-0137",
# https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/256
"RUSTSEC-2022-0048",
]
[licenses]
unlicensed = "deny"
allow = [
"MPL-2.0",
"Apache-2.0",
]
default = "deny"
copyleft = "deny"
deny = [
"GPL-1.0",
"GPL-2.0",
"GPL-3.0",
"AGPL-1.0",
"AGPL-3.0",
]
copyleft = "allow"
allow-osi-fsf-free = "either"
confidence-threshold = 0.8
@ -32,22 +42,30 @@ license-files = [
{ path = "LICENSE", hash = 0xbd0eed23 }
]
# Allow AGPL3 from dssim-core, which is optionally used in gst-plugin-videofx
[[licenses.exceptions]]
allow = ["AGPL-3.0"]
name = "dssim-core"
version = "3.2"
# Allow LGPL 2.1 for the threadshare plugin as it includes some LGPL code
[[licenses.exceptions]]
allow = ["LGPL-2.1"]
name = "gst-plugin-threadshare"
[bans]
multiple-versions = "deny"
highlight = "all"
wildcards = "allow"
# ignore duplicated deps because of chrono, cookie, cookie_store, hyper,
# hyperx, reqwest depending on old time
# https://github.com/chronotope/chrono/issues/400
# https://github.com/pfernie/cookie_store/issues/11
# https://github.com/hyperium/hyper/pull/2139
# https://github.com/dekellum/hyperx/issues/21
# https://github.com/seanmonstar/reqwest/issues/934
[[bans.skip]]
name = "time"
version = "0.1"
# ignore duplicated rustc_version dependency because rav1e depends on an old version
[[bans.skip]]
name = "rustc_version"
version = "0.3"
[[bans.skip]]
name = "semver"
version = "0.11"
# ignore duplicated crc dependency because ffv1 depends on an old version
# https://github.com/rust-av/ffv1/issues/21
[[bans.skip]]
@ -70,15 +88,28 @@ version = "0.9"
[[bans.skip]]
name = "hmac"
version = "0.11"
# ignore duplicated wasi dependency because various crates depends on an old version
[[bans.skip]]
name = "zerocopy"
version = "0.6"
name = "wasi"
version = "0.10"
# ignore duplicated spin dependency because various crates depend on an old version
[[bans.skip]]
name = "multimap"
version = "0.8"
name = "spin"
version = "0.5"
# cookie_store depends on older idna
# https://github.com/pfernie/cookie_store/commit/b9c710f45550c5c8997f18a83e6fcc5998cf1726
[[bans.skip]]
name = "nix"
version = "0.23"
name = "idna"
version = "0.2"
# image depends on older gif
# https://github.com/image-rs/image/pull/1826
[[bans.skip]]
name = "gif"
version = "0.11"
# field-offset and nix depend on an older memoffset
# https://github.com/Diggsey/rust-field-offset/pull/23
@ -93,177 +124,23 @@ name = "hermit-abi"
version = "0.1"
[[bans.skip]]
name = "hermit-abi"
version = "0.3"
version = "0.2"
# Various crates depend on an older version of base64
[[bans.skip]]
name = "base64"
version = "0.13"
# Various crates depend on an older version of windows-sys
[[bans.skip]]
name = "base64"
version = "0.21"
name = "windows-sys"
version = "0.42"
# Various crates depend on an older version of socket2
[[bans.skip]]
name = "socket2"
version = "0.4"
# Various crates depend on an older version of bitflags
[[bans.skip]]
name = "bitflags"
version = "1.0"
# tracing-subscriber depends on an older version of regex-syntax
[[bans.skip]]
name = "regex-syntax"
version = "0.6"
# publicsuffix depends on an older version of idna
# https://github.com/rushmorem/publicsuffix/pull/39
[[bans.skip]]
name = "idna"
version = "0.3"
# Various crates depend on an older version of indexmap / hashbrown
[[bans.skip]]
name = "indexmap"
version = "1.0"
[[bans.skip]]
name = "hashbrown"
version = "0.12"
# various livekit dependencies depend on an old version of itertools and sync_wrapper
[[bans.skip]]
name = "itertools"
version = "0.11"
[[bans.skip]]
name = "sync_wrapper"
version = "0.1"
# various rav1e / dssim-core depend on an old version of itertools
[[bans.skip]]
name = "itertools"
version = "0.12"
# matchers depends on an old version of regex-automata
[[bans.skip]]
name = "regex-automata"
version = "0.1"
# Various crates depend on old versions of the windows crates
[[bans.skip]]
name = "windows_x86_64_msvc"
version = "0.48"
[[bans.skip]]
name = "windows_x86_64_gnullvm"
version = "0.48"
[[bans.skip]]
name = "windows_x86_64_gnu"
version = "0.48"
[[bans.skip]]
name = "windows_i686_msvc"
version = "0.48"
[[bans.skip]]
name = "windows_i686_gnu"
version = "0.48"
[[bans.skip]]
name = "windows_aarch64_msvc"
version = "0.48"
[[bans.skip]]
name = "windows_aarch64_gnullvm"
version = "0.48"
[[bans.skip]]
name = "windows-targets"
version = "0.48"
[[bans.skip]]
name = "windows-sys"
version = "0.48"
# Various crates depend on an older version of crypto-bigint
[[bans.skip]]
name = "crypto-bigint"
version = "0.4"
# livekit-api depends on an older version of tokio-tungstenite
[[bans.skip]]
name = "tokio-tungstenite"
version = "0.20"
[[bans.skip]]
name = "tungstenite"
version = "0.20"
# Various crates depend on an older version of http
[[bans.skip]]
name = "http"
version = "0.2"
# proc-macro-crate depends on an older version of toml_edit
# https://github.com/bkchr/proc-macro-crate/pull/50
[[bans.skip]]
name = "toml_edit"
version = "0.21"
[[bans.skip]]
name = "winnow"
version = "0.5"
# Various crates depend on an older version of heck
[[bans.skip]]
name = "heck"
version = "0.4"
# Various crates depend on an older version of hyper / reqwest / headers / etc
[[bans.skip]]
name = "hyper"
version = "0.14"
[[bans.skip]]
name = "hyper-tls"
version = "0.5"
[[bans.skip]]
name = "http-body"
version = "0.4"
[[bans.skip]]
name = "headers-core"
version = "0.2"
[[bans.skip]]
name = "headers"
version = "0.3"
[[bans.skip]]
name = "h2"
version = "0.3"
[[bans.skip]]
name = "reqwest"
version = "0.11"
[[bans.skip]]
name = "rustls-pemfile"
version = "1.0"
[[bans.skip]]
name = "winreg"
version = "0.50"
# The AWS SDK uses old versions of rustls and related crates
[[bans.skip]]
name = "rustls"
version = "0.21"
[[bans.skip]]
name = "rustls-native-certs"
version = "0.6"
[[bans.skip]]
name = "rustls-webpki"
version = "0.101"
# warp depends on an older version of tokio-tungstenite
[[bans.skip]]
name = "tokio-tungstenite"
version = "0.21"
[[bans.skip]]
name = "tungstenite"
version = "0.21"
# various crates depend on an older version of system-deps
[[bans.skip]]
name = "system-deps"
version = "6"
[sources]
unknown-registry = "deny"
unknown-git = "deny"

View file

@ -19,8 +19,6 @@ except ImportError:
PARSER = ArgumentParser()
PARSER.add_argument('src_dir', type=Path)
PARSER.add_argument('plugins', nargs='*')
PARSER.add_argument('--features', action="store_true", help="Get list of features to activate")
PARSER.add_argument('--gst-version', help="Get list of features to activate")
# Map plugin name to directory name, for those that does not match.
@ -29,7 +27,6 @@ RENAMES = {
'rsfile': 'file',
'rsflv': 'flavors',
'rsrtp': 'rtp',
'rsrtsp': 'rtsp',
'rswebp': 'webp',
'rsonvif': 'onvif',
'rstracers': 'tracers',
@ -37,78 +34,31 @@ RENAMES = {
'rswebrtc': 'webrtc',
'rspng': 'png',
'rsvideofx': 'videofx',
'rsinter': 'inter',
'textahead': 'ahead',
'textwrap': 'wrap',
}
class CargoAnalyzer:
def __init__(self):
self.src_dir = None
self.plugins = None
self.features = False
self.gst_version = "1.18"
def extract_version(self, feature_name):
if feature_name.startswith('v'):
verindex = 1
elif feature_name.startswith('gst'):
verindex = 3
else:
return None
(majver, minver) = feature_name[verindex:].split("_")
return (int(majver), int(minver))
def extract_features(self, cargo_data):
features = cargo_data['features']
wanted_features = set()
gst_version_major = int(self.gst_version.split('.')[0])
gst_version_minor = int(self.gst_version.split('.')[1])
for (name, value) in features.items():
version = self.extract_version(name)
if version is None:
continue
(majver, minver) = version
if gst_version_major < majver or gst_version_minor < minver:
continue
wanted_features |= set(value)
if name.startswith("gst"):
# Required for some reason for rswebrtc which has a specific feature
wanted_features |= {f"{cargo_data['package']['name']}/{name}"}
return wanted_features
def run(self):
with (opts.src_dir / 'Cargo.toml').open('rb') as f:
crates = tomllib.load(f)['workspace']['members']
res = set()
for name in opts.plugins:
if name.startswith('gst'):
name = name[3:]
name = RENAMES.get(name, name)
crate_path = None
for crate in crates:
if Path(crate).name == name:
crate_path = opts.src_dir / crate / 'Cargo.toml'
assert crate_path
with crate_path.open('rb') as f:
data = tomllib.load(f)
if opts.features:
res |= self.extract_features(data)
else:
try:
requires = data['package']['metadata']['capi']['pkg_config']['requires_private']
except KeyError:
continue
res.update([i.strip().replace('>', "|>").replace('<', "|<").replace("==", "|==") for i in requires.split(',')])
return res
if __name__ == "__main__":
analyzer = CargoAnalyzer()
opts = PARSER.parse_args(namespace=analyzer)
opts = PARSER.parse_args()
print(','.join(analyzer.run()))
with (opts.src_dir / 'Cargo.toml').open('rb') as f:
crates = tomllib.load(f)['workspace']['members']
deps = set()
for p in opts.plugins:
assert p.startswith('gst')
name = p[3:]
name = RENAMES.get(name, name)
crate_path = None
for crate in crates:
if Path(crate).name == name:
crate_path = opts.src_dir / crate / 'Cargo.toml'
assert crate_path
with crate_path.open('rb') as f:
data = tomllib.load(f)
try:
requires = data['package']['metadata']['capi']['pkg_config']['requires_private']
except KeyError:
continue
deps.update([i.strip().replace('>', "|>").replace('<', "|<").replace("==", "|==") for i in requires.split(',')])
print(','.join(deps))

View file

@ -1,9 +1,5 @@
build_hotdoc = false
if get_option('doc').disabled()
subdir_done()
endif
if meson.is_cross_build()
if get_option('doc').enabled()
error('Documentation enabled but building the doc while cross building is not supported yet.')
@ -103,17 +99,9 @@ foreach plugin_name: list_plugin_res.stdout().split(':')
gst_index: 'plugins/index.md',
include_paths: join_paths(meson.current_source_dir(), '..'),
gst_smart_index: true,
gst_c_source_filters: [
'../target/*/*.rs',
'../target/*/*/*.rs',
'../target/*/*/*/*.rs',
'../target/*/*/*/*/*.rs',
'../target/*/*/*/*/*/*.rs',
],
gst_c_sources: [
'../*/*/*/*.rs',
'../*/*/*/*/*.rs',
'../*/*/*/*/*/*.rs',
],
dependencies: [gst_dep],
gst_order_generated_subpages: true,

File diff suppressed because it is too large Load diff

View file

@ -1,18 +1,18 @@
[package]
name = "gst-plugin-file"
version.workspace = true
version = "0.10.3"
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
repository.workspace = true
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
license = "MIT OR Apache-2.0"
description = "GStreamer Rust File Source/Sink Plugin"
edition.workspace = true
rust-version.workspace = true
edition = "2021"
rust-version = "1.63"
[dependencies]
url = "2"
gst.workspace = true
gst-base.workspace = true
once_cell.workspace = true
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
once_cell = "1.0"
[lib]
name = "gstrsfile"
@ -20,7 +20,7 @@ crate-type = ["cdylib", "rlib"]
path = "src/lib.rs"
[build-dependencies]
gst-plugin-version-helper.workspace = true
gst-plugin-version-helper = { path="../../version-helper", version = "0.7" }
[features]
static = []
@ -28,7 +28,7 @@ capi = []
doc = ["gst/v1_18"]
[package.metadata.capi]
min_version = "0.9.21"
min_version = "0.8.0"
[package.metadata.capi.header]
enabled = false
@ -36,7 +36,6 @@ enabled = false
[package.metadata.capi.library]
install_subdir = "gstreamer-1.0"
versioning = false
import_library = false
[package.metadata.capi.pkg_config]
requires_private = "gstreamer-1.0, gstreamer-base-1.0, gobject-2.0, glib-2.0, gmodule-2.0"

View file

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

1
generic/file/LICENSE-APACHE Symbolic link
View file

@ -0,0 +1 @@
../../LICENSE-APACHE

View file

@ -1,23 +0,0 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

1
generic/file/LICENSE-MIT Symbolic link
View file

@ -0,0 +1 @@
../../LICENSE-MIT

View file

@ -13,7 +13,6 @@
use gst::glib;
use gst::prelude::*;
use gst::subclass::prelude::*;
use gst_base::prelude::*;
use gst_base::subclass::prelude::*;
use std::fs::File;
@ -39,14 +38,15 @@ impl Default for Settings {
}
}
#[derive(Default)]
enum State {
#[default]
Stopped,
Started {
file: File,
position: u64,
},
Started { file: File, position: u64 },
}
impl Default for State {
fn default() -> State {
State::Stopped
}
}
#[derive(Default)]
@ -112,12 +112,6 @@ impl ObjectSubclass for FileSink {
}
impl ObjectImpl for FileSink {
fn constructed(&self) {
self.parent_constructed();
self.obj().set_sync(false);
}
fn properties() -> &'static [glib::ParamSpec] {
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
vec![glib::ParamSpecString::builder("location")

View file

@ -23,7 +23,7 @@ pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
gst::Element::register(
Some(plugin),
"rsfilesink",
gst::Rank::NONE,
gst::Rank::None,
FileSink::static_type(),
)
}

View file

@ -38,14 +38,15 @@ impl Default for Settings {
}
}
#[derive(Default)]
enum State {
#[default]
Stopped,
Started {
file: File,
position: u64,
},
Started { file: File, position: u64 },
}
impl Default for State {
fn default() -> State {
State::Stopped
}
}
#[derive(Default)]

View file

@ -22,7 +22,7 @@ pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
gst::Element::register(
Some(plugin),
"rsfilesrc",
gst::Rank::NONE,
gst::Rank::None,
FileSrc::static_type(),
)
}

View file

@ -1,44 +0,0 @@
[package]
name = "gst-plugin-gopbuffer"
version.workspace = true
authors = ["Matthew Waters <matthew@centricular.com>"]
license = "MPL-2.0"
description = "Store complete groups of pictures at a time"
repository.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
anyhow = "1"
gst = { workspace = true, features = ["v1_18"] }
gst-video = { workspace = true, features = ["v1_18"] }
once_cell.workspace = true
[lib]
name = "gstgopbuffer"
crate-type = ["cdylib", "rlib"]
path = "src/lib.rs"
[dev-dependencies]
gst-app = { workspace = true, features = ["v1_18"] }
gst-check = { workspace = true, features = ["v1_18"] }
[build-dependencies]
gst-plugin-version-helper = { path="../../version-helper" }
[features]
static = []
capi = []
[package.metadata.capi]
min_version = "0.8.0"
[package.metadata.capi.header]
enabled = false
[package.metadata.capi.library]
install_subdir = "gstreamer-1.0"
versioning = false
[package.metadata.capi.pkg_config]
requires_private = "gstreamer-1.0, gstreamer-base-1.0, gstreamer-audio-1.0, gstreamer-video-1.0, gobject-2.0, glib-2.0, gmodule-2.0"

View file

@ -1,373 +0,0 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

View file

@ -1,3 +0,0 @@
fn main() {
gst_plugin_version_helper::info()
}

View file

@ -1,880 +0,0 @@
// Copyright (C) 2023 Matthew Waters <matthew@centricular.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// <https://mozilla.org/MPL/2.0/>.
//
// SPDX-License-Identifier: MPL-2.0
/**
* SECTION:element-gopbuffer
*
* #gopbuffer is an element that can be used to store a minimum duration of data delimited by
* discrete GOPs (Group of Picture). It does this in by differentiation on the DELTA_UNIT
* flag on each input buffer.
*
* One example of the usefulness of #gopbuffer is its ability to store a backlog of data starting
* on a key frame boundary if say the previous 10s seconds of a stream would like to be recorded to
* disk.
*
* ## Example pipeline
*
* |[
* gst-launch videotestsrc ! vp8enc ! gopbuffer minimum-duration=10000000000 ! fakesink
* ]|
*
* Since: plugins-rs-0.13.0
*/
use gst::glib;
use gst::prelude::*;
use gst::subclass::prelude::*;
use std::collections::VecDeque;
use std::sync::Mutex;
use once_cell::sync::Lazy;
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
gst::DebugCategory::new(
"gopbuffer",
gst::DebugColorFlags::empty(),
Some("GopBuffer Element"),
)
});
const DEFAULT_MIN_TIME: gst::ClockTime = gst::ClockTime::from_seconds(1);
const DEFAULT_MAX_TIME: Option<gst::ClockTime> = None;
#[derive(Debug, Clone)]
struct Settings {
min_time: gst::ClockTime,
max_time: Option<gst::ClockTime>,
}
impl Default for Settings {
fn default() -> Self {
Settings {
min_time: DEFAULT_MIN_TIME,
max_time: DEFAULT_MAX_TIME,
}
}
}
#[derive(Debug, Copy, Clone)]
pub(crate) enum DeltaFrames {
/// Only single completely decodable frames
IntraOnly,
/// Frames may depend on past frames
PredictiveOnly,
/// Frames may depend on past or future frames
Bidirectional,
}
impl DeltaFrames {
/// Whether dts is required to order buffers differently from presentation order
pub(crate) fn requires_dts(&self) -> bool {
matches!(self, Self::Bidirectional)
}
/// Whether this coding structure does not allow delta flags on buffers
pub(crate) fn intra_only(&self) -> bool {
matches!(self, Self::IntraOnly)
}
pub(crate) fn from_caps(caps: &gst::CapsRef) -> Option<Self> {
let s = caps.structure(0)?;
Some(match s.name().as_str() {
"video/x-h264" | "video/x-h265" => DeltaFrames::Bidirectional,
"video/x-vp8" | "video/x-vp9" | "video/x-av1" => DeltaFrames::PredictiveOnly,
"image/jpeg" | "image/png" | "video/x-raw" => DeltaFrames::IntraOnly,
_ => return None,
})
}
}
// TODO: add buffer list support
#[derive(Debug)]
enum GopItem {
Buffer(gst::Buffer),
Event(gst::Event),
}
struct Gop {
// all times are in running time
start_pts: gst::ClockTime,
start_dts: Option<gst::Signed<gst::ClockTime>>,
earliest_pts: gst::ClockTime,
final_earliest_pts: bool,
end_pts: gst::ClockTime,
end_dts: Option<gst::Signed<gst::ClockTime>>,
final_end_pts: bool,
// Buffer or event
data: VecDeque<GopItem>,
}
impl Gop {
fn push_on_pad(mut self, pad: &gst::Pad) -> Result<gst::FlowSuccess, gst::FlowError> {
let mut iter = self.data.iter().filter_map(|item| match item {
GopItem::Buffer(buffer) => buffer.pts(),
_ => None,
});
let first_pts = iter.next();
let last_pts = iter.last();
gst::debug!(
CAT,
"pushing gop with start pts {} end pts {}",
first_pts.display(),
last_pts.display(),
);
for item in self.data.drain(..) {
match item {
GopItem::Buffer(buffer) => {
pad.push(buffer)?;
}
GopItem::Event(event) => {
pad.push_event(event);
}
}
}
Ok(gst::FlowSuccess::Ok)
}
}
struct Stream {
sinkpad: gst::Pad,
srcpad: gst::Pad,
sink_segment: Option<gst::FormattedSegment<gst::ClockTime>>,
delta_frames: DeltaFrames,
queued_gops: VecDeque<Gop>,
}
impl Stream {
fn queue_buffer(
&mut self,
buffer: gst::Buffer,
segment: &gst::FormattedSegment<gst::ClockTime>,
) -> Result<gst::FlowSuccess, gst::FlowError> {
let pts_position = buffer.pts().unwrap();
let end_pts_position = pts_position
.opt_add(buffer.duration())
.unwrap_or(pts_position);
let pts = segment
.to_running_time_full(pts_position)
.ok_or_else(|| {
gst::error!(CAT, obj: self.sinkpad, "Couldn't convert PTS to running time");
gst::FlowError::Error
})?
.positive()
.unwrap_or_else(|| {
gst::warning!(CAT, obj: self.sinkpad, "Negative PTSs are not supported");
gst::ClockTime::ZERO
});
let end_pts = segment
.to_running_time_full(end_pts_position)
.ok_or_else(|| {
gst::error!(
CAT,
obj: self.sinkpad,
"Couldn't convert end PTS to running time"
);
gst::FlowError::Error
})?
.positive()
.unwrap_or_else(|| {
gst::warning!(CAT, obj: self.sinkpad, "Negative PTSs are not supported");
gst::ClockTime::ZERO
});
let (dts, end_dts) = if !self.delta_frames.requires_dts() {
(None, None)
} else {
let dts_position = buffer.dts().expect("No dts");
let end_dts_position = buffer
.duration()
.opt_add(dts_position)
.unwrap_or(dts_position);
let dts = segment.to_running_time_full(dts_position).ok_or_else(|| {
gst::error!(CAT, obj: self.sinkpad, "Couldn't convert DTS to running time");
gst::FlowError::Error
})?;
let end_dts = segment
.to_running_time_full(end_dts_position)
.ok_or_else(|| {
gst::error!(
CAT,
obj: self.sinkpad,
"Couldn't convert end DTS to running time"
);
gst::FlowError::Error
})?;
let end_dts = std::cmp::max(end_dts, dts);
(Some(dts), Some(end_dts))
};
if !buffer.flags().contains(gst::BufferFlags::DELTA_UNIT) {
gst::debug!(
CAT,
"New GOP detected with buffer pts {} dts {}",
buffer.pts().display(),
buffer.dts().display()
);
let gop = Gop {
start_pts: pts,
start_dts: dts,
earliest_pts: pts,
final_earliest_pts: false,
end_pts: pts,
end_dts,
final_end_pts: false,
data: VecDeque::from([GopItem::Buffer(buffer)]),
};
self.queued_gops.push_front(gop);
if let Some(prev_gop) = self.queued_gops.get_mut(1) {
gst::debug!(
CAT,
obj: self.sinkpad,
"Updating previous GOP starting at PTS {} to end PTS {}",
prev_gop.earliest_pts,
pts,
);
prev_gop.end_pts = std::cmp::max(prev_gop.end_pts, pts);
prev_gop.end_dts = std::cmp::max(prev_gop.end_dts, dts);
if !self.delta_frames.requires_dts() {
prev_gop.final_end_pts = true;
}
if !prev_gop.final_earliest_pts {
// Don't bother logging this for intra-only streams as it would be for every
// single buffer.
if self.delta_frames.requires_dts() {
gst::debug!(
CAT,
obj: self.sinkpad,
"Previous GOP has final earliest PTS at {}",
prev_gop.earliest_pts
);
}
prev_gop.final_earliest_pts = true;
if let Some(prev_prev_gop) = self.queued_gops.get_mut(2) {
prev_prev_gop.final_end_pts = true;
}
}
}
} else if let Some(gop) = self.queued_gops.front_mut() {
gop.end_pts = std::cmp::max(gop.end_pts, end_pts);
gop.end_dts = gop.end_dts.opt_max(end_dts);
gop.data.push_back(GopItem::Buffer(buffer));
if self.delta_frames.requires_dts() {
let dts = dts.unwrap();
if gop.earliest_pts > pts && !gop.final_earliest_pts {
gst::debug!(
CAT,
obj: self.sinkpad,
"Updating current GOP earliest PTS from {} to {}",
gop.earliest_pts,
pts
);
gop.earliest_pts = pts;
if let Some(prev_gop) = self.queued_gops.get_mut(1) {
if prev_gop.end_pts < pts {
gst::debug!(
CAT,
obj: self.sinkpad,
"Updating previous GOP starting PTS {} end time from {} to {}",
pts,
prev_gop.end_pts,
pts
);
prev_gop.end_pts = pts;
}
}
}
let gop = self.queued_gops.front_mut().unwrap();
// The earliest PTS is known when the current DTS is bigger or equal to the first
// PTS that was observed in this GOP. If there was another frame later that had a
// lower PTS then it wouldn't be possible to display it in time anymore, i.e. the
// stream would be invalid.
if gop.start_pts <= dts && !gop.final_earliest_pts {
gst::debug!(
CAT,
obj: self.sinkpad,
"GOP has final earliest PTS at {}",
gop.earliest_pts
);
gop.final_earliest_pts = true;
if let Some(prev_gop) = self.queued_gops.get_mut(1) {
prev_gop.final_end_pts = true;
}
}
}
} else {
gst::debug!(
CAT,
"dropping buffer before first GOP with pts {} dts {}",
buffer.pts().display(),
buffer.dts().display()
);
}
if let Some((prev_gop, first_gop)) = Option::zip(
self.queued_gops.iter().find(|gop| gop.final_end_pts),
self.queued_gops.back(),
) {
gst::debug!(
CAT,
obj: self.sinkpad,
"Queued full GOPs duration updated to {}",
prev_gop.end_pts.saturating_sub(first_gop.earliest_pts),
);
}
gst::debug!(
CAT,
obj: self.sinkpad,
"Queued duration updated to {}",
Option::zip(self.queued_gops.front(), self.queued_gops.back())
.map(|(end, start)| end.end_pts.saturating_sub(start.start_pts))
.unwrap_or(gst::ClockTime::ZERO)
);
Ok(gst::FlowSuccess::Ok)
}
fn oldest_gop(&mut self) -> Option<Gop> {
self.queued_gops.pop_back()
}
fn peek_oldest_gop(&self) -> Option<&Gop> {
self.queued_gops.back()
}
fn peek_second_oldest_gop(&self) -> Option<&Gop> {
if self.queued_gops.len() <= 1 {
return None;
}
self.queued_gops.get(self.queued_gops.len() - 2)
}
fn drain_all(&mut self) -> impl Iterator<Item = Gop> + '_ {
self.queued_gops.drain(..)
}
fn flush(&mut self) {
self.queued_gops.clear();
}
}
#[derive(Default)]
struct State {
streams: Vec<Stream>,
}
impl State {
fn stream_from_sink_pad(&self, pad: &gst::Pad) -> Option<&Stream> {
self.streams.iter().find(|stream| &stream.sinkpad == pad)
}
fn stream_from_sink_pad_mut(&mut self, pad: &gst::Pad) -> Option<&mut Stream> {
self.streams
.iter_mut()
.find(|stream| &stream.sinkpad == pad)
}
fn stream_from_src_pad(&self, pad: &gst::Pad) -> Option<&Stream> {
self.streams.iter().find(|stream| &stream.srcpad == pad)
}
}
#[derive(Default)]
pub(crate) struct GopBuffer {
state: Mutex<State>,
settings: Mutex<Settings>,
}
impl GopBuffer {
fn sink_chain(
&self,
pad: &gst::Pad,
buffer: gst::Buffer,
) -> Result<gst::FlowSuccess, gst::FlowError> {
let obj = self.obj();
if buffer.pts().is_none() {
gst::error!(CAT, obj: obj, "Require timestamped buffers!");
return Err(gst::FlowError::Error);
}
let settings = self.settings.lock().unwrap().clone();
let mut state = self.state.lock().unwrap();
let stream = state
.stream_from_sink_pad_mut(pad)
.expect("pad without an internal Stream");
let Some(segment) = stream.sink_segment.clone() else {
gst::element_imp_error!(self, gst::CoreError::Clock, ["Got buffer before segment"]);
return Err(gst::FlowError::Error);
};
if stream.delta_frames.intra_only() && buffer.flags().contains(gst::BufferFlags::DELTA_UNIT)
{
gst::error!(CAT, obj: pad, "Intra-only stream with delta units");
return Err(gst::FlowError::Error);
}
if stream.delta_frames.requires_dts() && buffer.dts().is_none() {
gst::error!(CAT, obj: pad, "Require DTS for video streams");
return Err(gst::FlowError::Error);
}
let srcpad = stream.srcpad.clone();
stream.queue_buffer(buffer, &segment)?;
let mut gops_to_push = vec![];
let Some(newest_gop) = stream.queued_gops.front() else {
return Ok(gst::FlowSuccess::Ok);
};
// we are looking for the latest pts value here (which should be the largest)
let newest_ts = if stream.delta_frames.requires_dts() {
newest_gop.end_dts.unwrap()
} else {
gst::Signed::Positive(newest_gop.end_pts)
};
loop {
// check stored times as though the oldest GOP doesn't exist.
let Some(second_oldest_gop) = stream.peek_second_oldest_gop() else {
break;
};
// we are looking for the oldest pts here (with the largest value). This is our potentially
// new end time.
let oldest_ts = if stream.delta_frames.requires_dts() {
second_oldest_gop.start_dts.unwrap()
} else {
gst::Signed::Positive(second_oldest_gop.start_pts)
};
let stored_duration_without_oldest = newest_ts.saturating_sub(oldest_ts);
gst::trace!(
CAT,
obj: obj,
"newest_pts {}, second oldest_pts {}, stored_duration_without_oldest_gop {}, min-time {}",
newest_ts.display(),
oldest_ts.display(),
stored_duration_without_oldest.display(),
settings.min_time.display()
);
if stored_duration_without_oldest < settings.min_time {
break;
}
gops_to_push.push(stream.oldest_gop().unwrap());
}
if let Some(max_time) = settings.max_time {
while let Some(oldest_gop) = stream.peek_oldest_gop() {
let oldest_ts = oldest_gop.data.iter().rev().find_map(|item| match item {
GopItem::Buffer(buffer) => {
if stream.delta_frames.requires_dts() {
Some(gst::Signed::Positive(buffer.dts().unwrap()))
} else {
Some(gst::Signed::Positive(buffer.pts().unwrap()))
}
}
_ => None,
});
if newest_ts
.opt_saturating_sub(oldest_ts)
.is_some_and(|diff| diff > gst::Signed::Positive(max_time))
{
gst::warning!(CAT, obj: obj, "Stored data has overflowed the maximum allowed stored time {}, pushing oldest GOP", max_time.display());
gops_to_push.push(stream.oldest_gop().unwrap());
} else {
break;
}
}
}
drop(state);
for gop in gops_to_push.into_iter() {
gop.push_on_pad(&srcpad)?;
}
Ok(gst::FlowSuccess::Ok)
}
fn sink_event(&self, pad: &gst::Pad, event: gst::Event) -> bool {
let obj = self.obj();
let mut state = self.state.lock().unwrap();
let stream = state
.stream_from_sink_pad_mut(pad)
.expect("pad without an internal Stream!");
match event.view() {
gst::EventView::Caps(caps) => {
let Some(delta_frames) = DeltaFrames::from_caps(caps.caps()) else {
return false;
};
stream.delta_frames = delta_frames;
}
gst::EventView::FlushStop(_flush) => {
gst::debug!(CAT, obj: obj, "flushing stored data");
stream.flush();
}
gst::EventView::Eos(_eos) => {
gst::debug!(CAT, obj: obj, "draining data at EOS");
let gops = stream.drain_all().collect::<Vec<_>>();
let srcpad = stream.srcpad.clone();
drop(state);
for gop in gops.into_iter() {
let _ = gop.push_on_pad(&srcpad);
}
// once we've pushed all the data, we can push the corresponding eos
gst::Pad::event_default(pad, Some(&*obj), event);
return true;
}
gst::EventView::Segment(segment) => {
let Ok(segment) = segment.segment().clone().downcast::<gst::ClockTime>() else {
gst::error!(CAT, "Non TIME segments are not supported");
return false;
};
stream.sink_segment = Some(segment);
}
_ => (),
};
if event.is_serialized() {
if stream.peek_oldest_gop().is_none() {
// if there is nothing queued, the event can go straight through
gst::trace!(CAT, obj: obj, "nothing queued, event {:?} passthrough", event.structure().map(|s| s.name().as_str()));
drop(state);
return gst::Pad::event_default(pad, Some(&*obj), event);
}
let gop = stream.queued_gops.front_mut().unwrap();
gop.data.push_back(GopItem::Event(event));
true
} else {
// non-serialized events can be pushed directly
drop(state);
gst::Pad::event_default(pad, Some(&*obj), event)
}
}
fn sink_query(&self, pad: &gst::Pad, query: &mut gst::QueryRef) -> bool {
let obj = self.obj();
if query.is_serialized() {
// TODO: serialized queries somehow?
gst::warning!(CAT, obj: pad, "Serialized queries are currently not supported");
return false;
}
gst::Pad::query_default(pad, Some(&*obj), query)
}
fn src_query(&self, pad: &gst::Pad, query: &mut gst::QueryRef) -> bool {
let obj = self.obj();
match query.view_mut() {
gst::QueryViewMut::Latency(latency) => {
let mut upstream_query = gst::query::Latency::new();
let otherpad = {
let state = self.state.lock().unwrap();
let Some(stream) = state.stream_from_src_pad(pad) else {
return false;
};
stream.sinkpad.clone()
};
let ret = otherpad.peer_query(&mut upstream_query);
if ret {
let (live, mut min, mut max) = upstream_query.result();
let settings = self.settings.lock().unwrap();
min += settings.max_time.unwrap_or(settings.min_time);
max = max.opt_max(settings.max_time);
latency.set(live, min, max);
gst::debug!(
CAT,
obj: pad,
"Latency query response: live {} min {} max {}",
live,
min,
max.display()
);
}
ret
}
_ => gst::Pad::query_default(pad, Some(&*obj), query),
}
}
fn iterate_internal_links(&self, pad: &gst::Pad) -> gst::Iterator<gst::Pad> {
let state = self.state.lock().unwrap();
let otherpad = match pad.direction() {
gst::PadDirection::Src => state
.stream_from_src_pad(pad)
.map(|stream| stream.sinkpad.clone()),
gst::PadDirection::Sink => state
.stream_from_sink_pad(pad)
.map(|stream| stream.srcpad.clone()),
_ => unreachable!(),
};
if let Some(otherpad) = otherpad {
gst::Iterator::from_vec(vec![otherpad])
} else {
gst::Iterator::from_vec(vec![])
}
}
}
#[glib::object_subclass]
impl ObjectSubclass for GopBuffer {
const NAME: &'static str = "GstGopBuffer";
type Type = super::GopBuffer;
type ParentType = gst::Element;
}
impl ObjectImpl for GopBuffer {
fn properties() -> &'static [glib::ParamSpec] {
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
vec![
glib::ParamSpecUInt64::builder("minimum-duration")
.nick("Minimum Duration")
.blurb("The minimum duration to store")
.default_value(DEFAULT_MIN_TIME.nseconds())
.mutable_ready()
.build(),
glib::ParamSpecUInt64::builder("max-size-time")
.nick("Maximum Duration")
.blurb("The maximum duration to store (0=disable)")
.default_value(0)
.mutable_ready()
.build(),
]
});
&PROPERTIES
}
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
match pspec.name() {
"minimum-duration" => {
let mut settings = self.settings.lock().unwrap();
let min_time = value.get().expect("type checked upstream");
if settings.min_time != min_time {
settings.min_time = min_time;
drop(settings);
self.post_message(gst::message::Latency::builder().src(&*self.obj()).build());
}
}
"max-size-time" => {
let mut settings = self.settings.lock().unwrap();
let max_time = value
.get::<Option<gst::ClockTime>>()
.expect("type checked upstream");
let max_time = if matches!(max_time, Some(gst::ClockTime::ZERO) | None) {
None
} else {
max_time
};
if settings.max_time != max_time {
settings.max_time = max_time;
drop(settings);
self.post_message(gst::message::Latency::builder().src(&*self.obj()).build());
}
}
_ => unimplemented!(),
}
}
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
match pspec.name() {
"minimum-duration" => {
let settings = self.settings.lock().unwrap();
settings.min_time.to_value()
}
"max-size-time" => {
let settings = self.settings.lock().unwrap();
settings.max_time.unwrap_or(gst::ClockTime::ZERO).to_value()
}
_ => unimplemented!(),
}
}
fn constructed(&self) {
self.parent_constructed();
let obj = self.obj();
let class = obj.class();
let templ = class.pad_template("video_sink").unwrap();
let sinkpad = gst::Pad::builder_from_template(&templ)
.name("video_sink")
.chain_function(|pad, parent, buffer| {
GopBuffer::catch_panic_pad_function(
parent,
|| Err(gst::FlowError::Error),
|gopbuffer| gopbuffer.sink_chain(pad, buffer),
)
})
.event_function(|pad, parent, event| {
GopBuffer::catch_panic_pad_function(
parent,
|| false,
|gopbuffer| gopbuffer.sink_event(pad, event),
)
})
.query_function(|pad, parent, query| {
GopBuffer::catch_panic_pad_function(
parent,
|| false,
|gopbuffer| gopbuffer.sink_query(pad, query),
)
})
.iterate_internal_links_function(|pad, parent| {
GopBuffer::catch_panic_pad_function(
parent,
|| gst::Pad::iterate_internal_links_default(pad, parent),
|gopbuffer| gopbuffer.iterate_internal_links(pad),
)
})
.flags(gst::PadFlags::PROXY_CAPS)
.build();
obj.add_pad(&sinkpad).unwrap();
let templ = class.pad_template("video_src").unwrap();
let srcpad = gst::Pad::builder_from_template(&templ)
.name("video_src")
.query_function(|pad, parent, query| {
GopBuffer::catch_panic_pad_function(
parent,
|| false,
|gopbuffer| gopbuffer.src_query(pad, query),
)
})
.iterate_internal_links_function(|pad, parent| {
GopBuffer::catch_panic_pad_function(
parent,
|| gst::Pad::iterate_internal_links_default(pad, parent),
|gopbuffer| gopbuffer.iterate_internal_links(pad),
)
})
.build();
obj.add_pad(&srcpad).unwrap();
let mut state = self.state.lock().unwrap();
state.streams.push(Stream {
sinkpad,
srcpad,
sink_segment: None,
delta_frames: DeltaFrames::IntraOnly,
queued_gops: VecDeque::new(),
});
}
}
impl GstObjectImpl for GopBuffer {}
impl ElementImpl for GopBuffer {
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
gst::subclass::ElementMetadata::new(
"GopBuffer",
"Video",
"GOP Buffer",
"Matthew Waters <matthew@centricular.com>",
)
});
Some(&*ELEMENT_METADATA)
}
fn pad_templates() -> &'static [gst::PadTemplate] {
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
// This element is designed to implement multiple streams but it has not been
// implemented.
//
// The things missing for multiple (audio or video) streams are:
// 1. More pad templates
// 2. Choosing a main stream to drive the timestamp logic between all input streams
// 3. Allowing either the main stream to cause other streams to push data
// regardless of it's GOP state, or allow each stream to be individually delimited
// by GOP but all still within the minimum duration.
let video_caps = [
gst::Structure::builder("video/x-h264")
.field("stream-format", gst::List::new(["avc", "avc3"]))
.field("alignment", "au")
.build(),
gst::Structure::builder("video/x-h265")
.field("stream-format", gst::List::new(["hvc1", "hev1"]))
.field("alignment", "au")
.build(),
gst::Structure::builder("video/x-vp8").build(),
gst::Structure::builder("video/x-vp9").build(),
gst::Structure::builder("video/x-av1")
.field("stream-format", "obu-stream")
.field("alignment", "tu")
.build(),
]
.into_iter()
.collect::<gst::Caps>();
let src_pad_template = gst::PadTemplate::new(
"video_src",
gst::PadDirection::Src,
gst::PadPresence::Always,
&video_caps,
)
.unwrap();
let sink_pad_template = gst::PadTemplate::new(
"video_sink",
gst::PadDirection::Sink,
gst::PadPresence::Always,
&video_caps,
)
.unwrap();
vec![src_pad_template, sink_pad_template]
});
PAD_TEMPLATES.as_ref()
}
fn change_state(
&self,
transition: gst::StateChange,
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
#[allow(clippy::single_match)]
match transition {
gst::StateChange::NullToReady => {
let settings = self.settings.lock().unwrap();
if let Some(max_time) = settings.max_time {
if max_time < settings.min_time {
gst::element_imp_error!(
self,
gst::CoreError::StateChange,
["Configured maximum time is less than the minimum time"]
);
return Err(gst::StateChangeError);
}
}
}
_ => (),
}
self.parent_change_state(transition)?;
Ok(gst::StateChangeSuccess::Success)
}
}

View file

@ -1,27 +0,0 @@
// Copyright (C) 2022 Matthew Waters <matthew@centricular.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// <https://mozilla.org/MPL/2.0/>.
//
// SPDX-License-Identifier: MPL-2.0
use gst::glib;
use gst::prelude::*;
mod imp;
glib::wrapper! {
pub(crate) struct GopBuffer(ObjectSubclass<imp::GopBuffer>) @extends gst::Element, gst::Object;
}
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
gst::Element::register(
Some(plugin),
"gopbuffer",
gst::Rank::PRIMARY,
GopBuffer::static_type(),
)?;
Ok(())
}

View file

@ -1,34 +0,0 @@
// Copyright (C) 2022 Matthew Waters <matthew@centricular.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// <https://mozilla.org/MPL/2.0/>.
//
// SPDX-License-Identifier: MPL-2.0
#![allow(clippy::non_send_fields_in_send_ty, unused_doc_comments)]
/**
* plugin-gopbuffer:
*
* Since: plugins-rs-0.13.0
*/
use gst::glib;
mod gopbuffer;
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
gopbuffer::register(plugin)
}
gst::plugin_define!(
gopbuffer,
env!("CARGO_PKG_DESCRIPTION"),
plugin_init,
concat!(env!("CARGO_PKG_VERSION"), "-", env!("COMMIT_ID")),
// FIXME: MPL-2.0 is only allowed since 1.18.3 (as unknown) and 1.20 (as known)
"MPL",
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_REPOSITORY"),
env!("BUILD_REL_DATE")
);

View file

@ -1,128 +0,0 @@
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// <https://mozilla.org/MPL/2.0/>.
//
// SPDX-License-Identifier: MPL-2.0
//
use gst::prelude::*;
fn init() {
use std::sync::Once;
static INIT: Once = Once::new();
INIT.call_once(|| {
gst::init().unwrap();
gstgopbuffer::plugin_register_static().unwrap();
});
}
macro_rules! check_buffer {
($buf1:expr, $buf2:expr) => {
assert_eq!($buf1.pts(), $buf2.pts());
assert_eq!($buf1.dts(), $buf2.dts());
assert_eq!($buf1.flags(), $buf2.flags());
};
}
#[test]
fn test_min_one_gop_held() {
const OFFSET: gst::ClockTime = gst::ClockTime::from_seconds(10);
init();
let mut h =
gst_check::Harness::with_padnames("gopbuffer", Some("video_sink"), Some("video_src"));
// 200ms min buffer time
let element = h.element().unwrap();
element.set_property("minimum-duration", gst::ClockTime::from_mseconds(200));
h.set_src_caps(
gst::Caps::builder("video/x-h264")
.field("width", 320i32)
.field("height", 240i32)
.field("framerate", gst::Fraction::new(10, 1))
.field("stream-format", "avc")
.field("alignment", "au")
.field("codec_data", gst::Buffer::with_size(1).unwrap())
.build(),
);
let mut in_segment = gst::Segment::new();
in_segment.set_format(gst::Format::Time);
in_segment.set_base(10.seconds());
assert!(h.push_event(gst::event::Segment::builder(&in_segment).build()));
h.play();
// Push 10 buffers of 100ms each, 2nd and 5th buffer without DELTA_UNIT flag
let in_buffers: Vec<_> = (0..6)
.map(|i| {
let mut buffer = gst::Buffer::with_size(1).unwrap();
{
let buffer = buffer.get_mut().unwrap();
buffer.set_pts(OFFSET + gst::ClockTime::from_mseconds(i * 100));
buffer.set_dts(OFFSET + gst::ClockTime::from_mseconds(i * 100));
buffer.set_duration(gst::ClockTime::from_mseconds(100));
if i != 1 && i != 4 {
buffer.set_flags(gst::BufferFlags::DELTA_UNIT);
}
}
assert_eq!(h.push(buffer.clone()), Ok(gst::FlowSuccess::Ok));
buffer
})
.collect();
// pull mandatory events
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::StreamStart);
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::Caps);
// GstHarness pushes its own segment event that we need to eat
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::Segment);
let ev = h.pull_event().unwrap();
let gst::event::EventView::Segment(recv_segment) = ev.view() else {
unreachable!()
};
let recv_segment = recv_segment.segment();
assert_eq!(recv_segment, &in_segment);
// check that at least the first GOP has been output already as it exceeds the minimum-time
// value
let mut in_iter = in_buffers.iter();
// the first buffer is dropped because it was not preceded by a keyframe
let _buffer = in_iter.next().unwrap();
// a keyframe
let out = h.pull().unwrap();
let buffer = in_iter.next().unwrap();
check_buffer!(buffer, out);
// not a keyframe
let out = h.pull().unwrap();
let buffer = in_iter.next().unwrap();
check_buffer!(buffer, out);
// not a keyframe
let out = h.pull().unwrap();
let buffer = in_iter.next().unwrap();
check_buffer!(buffer, out);
// no more buffers
assert_eq!(h.buffers_in_queue(), 0);
// push eos to drain out the rest of the data
assert!(h.push_event(gst::event::Eos::new()));
for buffer in in_iter {
let out = h.pull().unwrap();
check_buffer!(buffer, out);
}
// no more buffers
assert_eq!(h.buffers_in_queue(), 0);
let ev = h.pull_event().unwrap();
assert_eq!(ev.type_(), gst::EventType::Eos);
}

View file

@ -1,53 +0,0 @@
[package]
name = "gst-plugin-inter"
version.workspace = true
authors = ["Mathieu Duponchelle <mathieu@centricular.com>"]
license = "MPL-2.0"
description = "GStreamer Inter Plugin"
repository.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
anyhow = "1"
gst = { workspace = true, features = ["v1_18"] }
gst-utils.workspace = true
gst-app.workspace = true
once_cell = "1.0"
[dev-dependencies]
pretty_assertions = "1"
gst-check.workspace = true
futures = "0.3"
tokio = { version = "1", features = ["fs", "macros", "rt-multi-thread", "time"] }
tokio-stream = "0.1.11"
serial_test = "3"
[lib]
name = "gstrsinter"
crate-type = ["cdylib", "rlib"]
path = "src/lib.rs"
[build-dependencies]
gst-plugin-version-helper.workspace = true
[features]
static = []
capi = []
doc = ["gst/v1_18"]
[package.metadata.capi]
min_version = "0.8.0"
[package.metadata.capi.header]
enabled = false
[package.metadata.capi.library]
install_subdir = "gstreamer-1.0"
versioning = false
[package.metadata.capi.pkg_config]
requires_private = "gstreamer-1.0, gobject-2.0, glib-2.0, gmodule-2.0"
[[example]]
name = "plug-and-play"

View file

@ -1,5 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
fn main() {
gst_plugin_version_helper::info()
}

View file

@ -1,74 +0,0 @@
use anyhow::Error;
use futures::prelude::*;
use futures::stream::select_all;
use gst::prelude::*;
fn toplevel(obj: &gst::Object) -> gst::Object {
if let Some(parent) = obj.parent() {
toplevel(&parent)
} else {
obj.clone()
}
}
#[tokio::main]
async fn main() -> Result<(), Error> {
gst::init()?;
let src_pipeline = gst::parse::launch("videotestsrc is-live=true ! intersink")?;
let sink_pipeline = gst::parse::launch("intersrc ! videoconvert ! autovideosink")?;
let mut stream = select_all([
src_pipeline.bus().unwrap().stream(),
sink_pipeline.bus().unwrap().stream(),
]);
let base_time = gst::SystemClock::obtain().time().unwrap();
src_pipeline.set_clock(Some(&gst::SystemClock::obtain()))?;
src_pipeline.set_start_time(gst::ClockTime::NONE);
src_pipeline.set_base_time(base_time);
sink_pipeline.set_clock(Some(&gst::SystemClock::obtain()))?;
sink_pipeline.set_start_time(gst::ClockTime::NONE);
sink_pipeline.set_base_time(base_time);
src_pipeline.set_state(gst::State::Playing)?;
sink_pipeline.set_state(gst::State::Playing)?;
while let Some(msg) = stream.next().await {
use gst::MessageView;
match msg.view() {
MessageView::Latency(..) => {
if let Some(o) = msg.src() {
if let Ok(pipeline) = toplevel(o).downcast::<gst::Pipeline>() {
eprintln!("Recalculating latency {:?}", pipeline);
let _ = pipeline.recalculate_latency();
}
}
}
MessageView::Eos(..) => {
eprintln!("Unexpected EOS");
break;
}
MessageView::Error(err) => {
eprintln!(
"Got error from {}: {} ({})",
msg.src()
.map(|s| String::from(s.path_string()))
.unwrap_or_else(|| "None".into()),
err.error(),
err.debug().unwrap_or_else(|| "".into()),
);
break;
}
_ => (),
}
}
src_pipeline.set_state(gst::State::Null)?;
sink_pipeline.set_state(gst::State::Null)?;
Ok(())
}

View file

@ -1,324 +0,0 @@
use anyhow::Error;
use futures::prelude::*;
use gst::prelude::*;
use std::collections::HashMap;
use std::io::prelude::*;
use tokio::task;
struct Producer {
pipeline: gst::Pipeline,
sink: gst::Element,
overlay: gst::Element,
}
struct Consumer {
pipeline: gst::Pipeline,
src: gst::Element,
}
fn create_sink_pipeline(producer_name: &str) -> Result<Producer, Error> {
let pipeline = gst::Pipeline::builder()
.name(format!("producer-{producer_name}"))
.build();
let videotestsrc = gst::ElementFactory::make("videotestsrc")
.property_from_str("pattern", "ball")
.property("is-live", true)
.build()?;
let capsfilter = gst::ElementFactory::make("capsfilter")
.property(
"caps",
gst::Caps::builder("video/x-raw")
.field("framerate", gst::Fraction::new(50, 1))
.build(),
)
.build()?;
let queue = gst::ElementFactory::make("queue").build()?;
let overlay = gst::ElementFactory::make("textoverlay")
.property("font-desc", "Sans 30")
.property("text", format!("Producer: {producer_name}"))
.property_from_str("valignment", "top")
.build()?;
let timeoverlay = gst::ElementFactory::make("timeoverlay")
.property("font-desc", "Sans 30")
.property_from_str("valignment", "center")
.property_from_str("halignment", "center")
.build()?;
let sink = gst::ElementFactory::make("intersink")
.property("producer-name", producer_name)
.build()?;
pipeline.add_many([
&videotestsrc,
&capsfilter,
&queue,
&overlay,
&timeoverlay,
&sink,
])?;
gst::Element::link_many([
&videotestsrc,
&capsfilter,
&queue,
&overlay,
&timeoverlay,
&sink,
])?;
Ok(Producer {
pipeline,
sink,
overlay,
})
}
fn create_src_pipeline(producer_name: &str, consumer_name: &str) -> Result<Consumer, Error> {
let pipeline = gst::Pipeline::builder()
.name(format!("consumer-{consumer_name}"))
.build();
let src = gst::ElementFactory::make("intersrc")
.property("producer-name", producer_name)
.build()?;
let queue = gst::ElementFactory::make("queue").build()?;
let vconv = gst::ElementFactory::make("videoconvert").build()?;
let overlay = gst::ElementFactory::make("textoverlay")
.property("font-desc", "Sans 30")
.property("text", format!("Consumer: {consumer_name}"))
.property_from_str("valignment", "bottom")
.build()?;
let vconv2 = gst::ElementFactory::make("videoconvert").build()?;
let sink = gst::ElementFactory::make("autovideosink").build()?;
pipeline.add_many([&src, &queue, &vconv, &overlay, &vconv2, &sink])?;
gst::Element::link_many([&src, &queue, &vconv, &overlay, &vconv2, &sink])?;
Ok(Consumer { pipeline, src })
}
fn prompt_on() {
print!("$ ");
let _ = std::io::stdout().flush();
}
fn monitor_pipeline(pipeline: &gst::Pipeline, base_time: gst::ClockTime) -> Result<(), Error> {
pipeline.set_clock(Some(&gst::SystemClock::obtain()))?;
pipeline.set_start_time(gst::ClockTime::NONE);
pipeline.set_base_time(base_time);
pipeline.set_state(gst::State::Playing)?;
let mut bus_stream = pipeline.bus().expect("Pipeline should have a bus").stream();
let pipeline_clone = pipeline.downgrade();
task::spawn(async move {
while let Some(msg) = bus_stream.next().await {
use gst::MessageView;
if let Some(pipeline) = pipeline_clone.upgrade() {
match msg.view() {
MessageView::Latency(..) => {
let _ = pipeline.recalculate_latency();
}
MessageView::Eos(..) => {
println!(
"EOS from {}",
msg.src()
.map(|s| String::from(s.path_string()))
.unwrap_or_else(|| "None".into())
);
prompt_on();
break;
}
MessageView::Error(err) => {
let _ = pipeline.set_state(gst::State::Null);
println!(
"Got error from {}: {} ({})",
msg.src()
.map(|s| String::from(s.path_string()))
.unwrap_or_else(|| "None".into()),
err.error(),
err.debug().unwrap_or_else(|| "".into()),
);
prompt_on();
break;
}
MessageView::StateChanged(sc) => {
if msg.src() == Some(pipeline.upcast_ref()) {
pipeline.debug_to_dot_file(
gst::DebugGraphDetails::all(),
format!("{}-{:?}-{:?}", pipeline.name(), sc.old(), sc.current()),
);
}
}
_ => (),
}
} else {
break;
}
}
});
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Error> {
gst::init()?;
println!("h for help");
let base_time = gst::SystemClock::obtain().time().unwrap();
let mut producers: HashMap<String, Producer> = HashMap::new();
let mut consumers: HashMap<String, Consumer> = HashMap::new();
let mut stdin = std::io::stdin().lock();
loop {
let mut buf = String::new();
prompt_on();
match stdin.read_line(&mut buf)? {
0 => {
eprintln!("EOF!");
break;
}
_ => {
let command: Vec<_> = buf.split_whitespace().collect();
match command.first() {
Some(&"ap") => {
if command.len() != 2 {
println!("ap <producer_name>: Add a producer");
} else {
let producer_name = command.get(1).unwrap().to_string();
if producers.contains_key(&producer_name) {
println!("Producer with name {producer_name} already exists!");
continue;
}
let producer = create_sink_pipeline(&producer_name)?;
monitor_pipeline(&producer.pipeline, base_time)?;
println!("Added producer with name {producer_name}");
producers.insert(producer_name, producer);
}
}
Some(&"ac") => {
if command.len() != 3 {
println!("ac <consumer_name> <producer_name>: Add a consumer");
} else {
let consumer_name = command.get(1).unwrap().to_string();
let producer_name = command.get(2).unwrap().to_string();
if consumers.contains_key(&consumer_name) {
println!("Consumer with name {consumer_name} already exists!");
continue;
}
let consumer = create_src_pipeline(&producer_name, &consumer_name)?;
monitor_pipeline(&consumer.pipeline, base_time)?;
println!("Added consumer with name {consumer_name} and producer name {producer_name}");
consumers.insert(consumer_name, consumer);
}
}
Some(&"rp") => {
if command.len() != 2 {
println!("rp <producer_name>: Remove a producer");
} else {
let producer_name = command.get(1).unwrap().to_string();
if let Some(producer) = producers.remove(&producer_name) {
let _ = producer.pipeline.set_state(gst::State::Null);
println!("Removed producer with name {producer_name}");
} else {
println!("No producer with name {producer_name}");
}
}
}
Some(&"rc") => {
if command.len() != 2 {
println!("rc <consumer_name>: Remove a consumer");
} else {
let consumer_name = command.get(1).unwrap().to_string();
if let Some(consumer) = consumers.remove(&consumer_name) {
let _ = consumer.pipeline.set_state(gst::State::Null);
println!("Removed consumer with name {consumer_name}");
} else {
println!("No consumer with name {consumer_name}");
}
}
}
Some(&"cnp") => {
if command.len() != 3 {
println!("cnp <old_producer_name> <new_producer_name>: Change the name of a producer");
} else {
let old_producer_name = command.get(1).unwrap().to_string();
let producer_name = command.get(2).unwrap().to_string();
if producers.contains_key(&producer_name) {
println!("Producer with name {producer_name} already exists!");
continue;
}
if let Some(producer) = producers.remove(&old_producer_name) {
producer.sink.set_property("producer-name", &producer_name);
producer
.overlay
.set_property("text", format!("Producer: {producer_name}"));
println!(
"Changed producer name {old_producer_name} -> {producer_name}"
);
producers.insert(producer_name, producer);
} else {
println!("No producer with name {old_producer_name}");
}
}
}
Some(&"cpn") => {
if command.len() != 3 {
println!("cpn <consumer_name> <new_producer_name>: Change the producer name for a consumer");
} else {
let consumer_name = command.get(1).unwrap().to_string();
let producer_name = command.get(2).unwrap().to_string();
if let Some(consumer) = consumers.get_mut(&consumer_name) {
consumer.src.set_property("producer-name", &producer_name);
println!("Changed producer name for consumer {consumer_name} to {producer_name}");
} else {
println!("No consumer with name {consumer_name}");
}
}
}
Some(&"h") => {
println!("h: show this help");
println!("ap <producer_name>: Add a producer");
println!("ac <consumer_name> <producer_name>: Add a consumer");
println!("rp <producer_name>: Remove a producer");
println!("rc <consumer_name>: Remove a consumer");
println!("cnp <old_producer_name> <new_producer_name>: Change the name of a producer");
println!("cpn <consumer_name> <new_producer_name>: Change the producer name for a consumer");
}
_ => {
println!("Unknown command");
}
}
}
}
buf.clear();
}
for (_, producer) in producers {
let _ = producer.pipeline.set_state(gst::State::Null);
}
for (_, consumer) in consumers {
let _ = consumer.pipeline.set_state(gst::State::Null);
}
Ok(())
}

View file

@ -1,44 +0,0 @@
// Copyright (C) 2023 Mathieu Duponchelle <mathieu@centricular.com>
//
// Take a look at the license at the top of the repository in the LICENSE file.
#![allow(unused_doc_comments)]
//! GStreamer elements for connecting pipelines in the same process
mod sink;
mod src;
mod streamproducer;
/**
* plugin-rsinter:
* @title: Rust inter elements
* @short_description: A set of elements for transferring data between pipelines
*
* This plugin exposes two elements, `intersink` and `intersrc`, that can be
* used to transfer data from one pipeline to multiple others in the same
* process.
*
* The elements are implemented using the `StreamProducer` API from
* gstreamer-utils.
*
* Since: plugins-rs-0.11.0
*/
use gst::glib;
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
sink::register(plugin)?;
src::register(plugin)?;
Ok(())
}
gst::plugin_define!(
rsinter,
env!("CARGO_PKG_DESCRIPTION"),
plugin_init,
concat!(env!("CARGO_PKG_VERSION"), "-", env!("COMMIT_ID")),
"MPL-2.0",
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_REPOSITORY"),
env!("BUILD_REL_DATE")
);

View file

@ -1,217 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
use crate::streamproducer::InterStreamProducer;
use anyhow::Error;
use gst::glib;
use gst::prelude::*;
use gst::subclass::prelude::*;
use std::sync::Mutex;
use once_cell::sync::Lazy;
const DEFAULT_PRODUCER_NAME: &str = "default";
#[derive(Debug)]
struct Settings {
producer_name: String,
}
impl Default for Settings {
fn default() -> Self {
Settings {
producer_name: DEFAULT_PRODUCER_NAME.to_string(),
}
}
}
struct State {
appsink: gst_app::AppSink,
sinkpad: gst::GhostPad,
}
/* Locking order is field order */
pub struct InterSink {
settings: Mutex<Settings>,
state: Mutex<State>,
}
impl InterSink {
fn prepare(&self) -> Result<(), Error> {
let settings = self.settings.lock().unwrap();
let state = self.state.lock().unwrap();
InterStreamProducer::acquire(&settings.producer_name, &state.appsink)?;
Ok(())
}
fn unprepare(&self) {
let settings = self.settings.lock().unwrap();
InterStreamProducer::release(&settings.producer_name);
}
}
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
gst::DebugCategory::new(
"intersink",
gst::DebugColorFlags::empty(),
Some("Inter Sink"),
)
});
#[glib::object_subclass]
impl ObjectSubclass for InterSink {
const NAME: &'static str = "GstInterSink";
type Type = super::InterSink;
type ParentType = gst::Bin;
fn with_class(klass: &Self::Class) -> Self {
let templ = klass.pad_template("sink").unwrap();
let sinkpad = gst::GhostPad::from_template(&templ);
Self {
settings: Mutex::new(Default::default()),
state: Mutex::new(State {
appsink: gst_app::AppSink::builder().name("appsink").build(),
sinkpad: sinkpad.upcast(),
}),
}
}
}
impl ObjectImpl for InterSink {
fn properties() -> &'static [glib::ParamSpec] {
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
vec![glib::ParamSpecString::builder("producer-name")
.nick("Producer Name")
.blurb("Producer Name to use")
.doc_show_default()
.mutable_playing()
.build()]
});
PROPERTIES.as_ref()
}
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
match pspec.name() {
"producer-name" => {
let mut settings = self.settings.lock().unwrap();
let old_producer_name = settings.producer_name.clone();
settings.producer_name = value
.get::<String>()
.unwrap_or_else(|_| DEFAULT_PRODUCER_NAME.to_string());
if let Some(appsink) = InterStreamProducer::release(&old_producer_name) {
if let Err(err) =
InterStreamProducer::acquire(&settings.producer_name, &appsink)
{
drop(settings);
gst::error!(CAT, imp: self, "{err}");
self.post_error_message(gst::error_msg!(
gst::StreamError::Failed,
["{err}"]
))
} else {
drop(settings);
// This is required because StreamProducer obtains the latency
// it needs to forward from Latency events, and we need to let the
// application know it should recalculate latency to get the event
// to travel upstream again
self.post_message(gst::message::Latency::new());
}
}
}
_ => unimplemented!(),
};
}
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
match pspec.name() {
"producer-name" => {
let settings = self.settings.lock().unwrap();
settings.producer_name.to_value()
}
_ => unimplemented!(),
}
}
fn constructed(&self) {
self.parent_constructed();
let obj = self.obj();
obj.set_suppressed_flags(gst::ElementFlags::SINK | gst::ElementFlags::SOURCE);
obj.set_element_flags(gst::ElementFlags::SINK);
let state = self.state.lock().unwrap();
obj.add(&state.appsink).unwrap();
obj.add_pad(&state.sinkpad).unwrap();
state
.sinkpad
.set_target(Some(&state.appsink.static_pad("sink").unwrap()))
.unwrap();
}
}
impl GstObjectImpl for InterSink {}
impl ElementImpl for InterSink {
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
gst::subclass::ElementMetadata::new(
"Inter Sink",
"Generic/Sink",
"Inter Sink",
"Mathieu Duponchelle <mathieu@centricular.com>",
)
});
Some(&*ELEMENT_METADATA)
}
fn pad_templates() -> &'static [gst::PadTemplate] {
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
let caps = gst::Caps::new_any();
let sink_pad_template = gst::PadTemplate::new(
"sink",
gst::PadDirection::Sink,
gst::PadPresence::Always,
&caps,
)
.unwrap();
vec![sink_pad_template]
});
PAD_TEMPLATES.as_ref()
}
fn change_state(
&self,
transition: gst::StateChange,
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
gst::trace!(CAT, imp: self, "Changing state {:?}", transition);
if transition == gst::StateChange::ReadyToPaused {
if let Err(err) = self.prepare() {
gst::element_error!(
self.obj(),
gst::StreamError::Failed,
["Failed to prepare: {}", err]
);
return Err(gst::StateChangeError);
}
}
let ret = self.parent_change_state(transition)?;
if transition == gst::StateChange::PausedToReady {
self.unprepare();
}
Ok(ret)
}
}
impl BinImpl for InterSink {}

View file

@ -1,35 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
use glib::prelude::*;
use gst::glib;
mod imp;
/**
* SECTION:element-intersink
*
* #intersink is an element that can be used to produce data for
* multiple #intersrc elements to consume.
*
* You can access the underlying appsink element through the static name
* "appsink".
*
* #intersink should not reside in the same pipeline as the #intersrc
* that consumes from it, here is an example of how to use those elements
* in separate pipelines:
*
* {{ generic/inter/examples/basic.rs }}
*/
glib::wrapper! {
pub struct InterSink(ObjectSubclass<imp::InterSink>) @extends gst::Bin, gst::Element, gst::Object;
}
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
gst::Element::register(
Some(plugin),
"intersink",
gst::Rank::NONE,
InterSink::static_type(),
)
}

View file

@ -1,203 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
use crate::streamproducer::InterStreamProducer;
use anyhow::Error;
use gst::glib;
use gst::prelude::*;
use gst::subclass::prelude::*;
use std::sync::Mutex;
use once_cell::sync::Lazy;
const DEFAULT_PRODUCER_NAME: &str = "default";
#[derive(Debug)]
struct Settings {
producer_name: String,
}
impl Default for Settings {
fn default() -> Self {
Settings {
producer_name: DEFAULT_PRODUCER_NAME.to_string(),
}
}
}
struct State {
srcpad: gst::GhostPad,
appsrc: gst_app::AppSrc,
}
/* Locking order is field order */
pub struct InterSrc {
settings: Mutex<Settings>,
state: Mutex<State>,
}
impl InterSrc {
fn prepare(&self) -> Result<(), Error> {
let settings = self.settings.lock().unwrap();
let state = self.state.lock().unwrap();
InterStreamProducer::subscribe(&settings.producer_name, &state.appsrc);
Ok(())
}
fn unprepare(&self) {
let settings = self.settings.lock().unwrap();
let state = self.state.lock().unwrap();
InterStreamProducer::unsubscribe(&settings.producer_name, &state.appsrc);
}
}
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
gst::DebugCategory::new("intersrc", gst::DebugColorFlags::empty(), Some("Inter Src"))
});
#[glib::object_subclass]
impl ObjectSubclass for InterSrc {
const NAME: &'static str = "GstInterSrc";
type Type = super::InterSrc;
type ParentType = gst::Bin;
fn with_class(klass: &Self::Class) -> Self {
let templ = klass.pad_template("src").unwrap();
let srcpad = gst::GhostPad::from_template(&templ);
Self {
settings: Mutex::new(Default::default()),
state: Mutex::new(State {
srcpad: srcpad.upcast(),
appsrc: gst_app::AppSrc::builder().name("appsrc").build(),
}),
}
}
}
impl ObjectImpl for InterSrc {
fn properties() -> &'static [glib::ParamSpec] {
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
vec![glib::ParamSpecString::builder("producer-name")
.nick("Producer Name")
.blurb("Producer Name to consume from")
.doc_show_default()
.mutable_playing()
.build()]
});
PROPERTIES.as_ref()
}
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
match pspec.name() {
"producer-name" => {
let mut settings = self.settings.lock().unwrap();
let old_producer_name = settings.producer_name.clone();
settings.producer_name = value
.get::<String>()
.unwrap_or_else(|_| DEFAULT_PRODUCER_NAME.to_string());
let state = self.state.lock().unwrap();
if InterStreamProducer::unsubscribe(&old_producer_name, &state.appsrc) {
InterStreamProducer::subscribe(&settings.producer_name, &state.appsrc);
}
}
_ => unimplemented!(),
};
}
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
match pspec.name() {
"producer-name" => {
let settings = self.settings.lock().unwrap();
settings.producer_name.to_value()
}
_ => unimplemented!(),
}
}
fn constructed(&self) {
self.parent_constructed();
let obj = self.obj();
obj.set_suppressed_flags(gst::ElementFlags::SINK | gst::ElementFlags::SOURCE);
obj.set_element_flags(gst::ElementFlags::SOURCE);
let state = self.state.lock().unwrap();
gst_utils::StreamProducer::configure_consumer(&state.appsrc);
obj.add(&state.appsrc).unwrap();
obj.add_pad(&state.srcpad).unwrap();
state
.srcpad
.set_target(Some(&state.appsrc.static_pad("src").unwrap()))
.unwrap();
}
}
impl GstObjectImpl for InterSrc {}
impl ElementImpl for InterSrc {
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
gst::subclass::ElementMetadata::new(
"Inter Src",
"Generic/Src",
"Inter Src",
"Mathieu Duponchelle <mathieu@centricular.com>",
)
});
Some(&*ELEMENT_METADATA)
}
fn pad_templates() -> &'static [gst::PadTemplate] {
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
let caps = gst::Caps::new_any();
let src_pad_template = gst::PadTemplate::new(
"src",
gst::PadDirection::Src,
gst::PadPresence::Always,
&caps,
)
.unwrap();
vec![src_pad_template]
});
PAD_TEMPLATES.as_ref()
}
fn change_state(
&self,
transition: gst::StateChange,
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
gst::trace!(CAT, imp: self, "Changing state {:?}", transition);
if transition == gst::StateChange::ReadyToPaused {
if let Err(err) = self.prepare() {
gst::element_error!(
self.obj(),
gst::StreamError::Failed,
["Failed to prepare: {}", err]
);
return Err(gst::StateChangeError);
}
}
let ret = self.parent_change_state(transition)?;
if transition == gst::StateChange::PausedToReady {
self.unprepare();
}
Ok(ret)
}
}
impl BinImpl for InterSrc {}

View file

@ -1,34 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
use glib::prelude::*;
use gst::glib;
mod imp;
/**
* SECTION:element-intersrc
*
* #intersrc is an element that can be used to consume data from an #intersink.
*
* You can access the underlying appsrc element through the static name
* "appsrc".
*
* #intersrc should not reside in the same pipeline as the #intersink
* that it consumes from, here is an example of how to use those elements
* in separate pipelines:
*
* {{ generic/inter/examples/basic.rs }}
*/
glib::wrapper! {
pub struct InterSrc(ObjectSubclass<imp::InterSrc>) @extends gst::Bin, gst::Element, gst::Object;
}
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
gst::Element::register(
Some(plugin),
"intersrc",
gst::Rank::NONE,
InterSrc::static_type(),
)
}

View file

@ -1,159 +0,0 @@
use gst::prelude::*;
use std::collections::{HashMap, HashSet};
use std::sync::Mutex;
use anyhow::{anyhow, Error};
use once_cell::sync::Lazy;
pub enum InterStreamProducer {
Pending {
consumers: HashSet<gst_app::AppSrc>,
},
Active {
producer: gst_utils::StreamProducer,
links: HashMap<gst_app::AppSrc, gst_utils::ConsumptionLink>,
},
}
static PRODUCERS: Lazy<Mutex<HashMap<String, InterStreamProducer>>> =
Lazy::new(|| Mutex::new(HashMap::new()));
fn toplevel(obj: &gst::Object) -> gst::Object {
if let Some(parent) = obj.parent() {
toplevel(&parent)
} else {
obj.clone()
}
}
fn ensure_different_toplevel(producer: &gst_app::AppSink, consumer: &gst_app::AppSrc) {
let top_a = toplevel(producer.upcast_ref());
let top_b = toplevel(consumer.upcast_ref());
if top_a == top_b {
gst::glib::g_critical!(
"gstrsinter",
"Intersink with appsink {} should not share the same toplevel bin \
as intersrc with appsrc {}, this results in loops in latency calculation",
producer.name(),
consumer.name()
);
}
}
impl InterStreamProducer {
pub fn acquire(
name: &str,
appsink: &gst_app::AppSink,
) -> Result<gst_utils::StreamProducer, Error> {
let mut producers = PRODUCERS.lock().unwrap();
if let Some(producer) = producers.remove(name) {
match producer {
InterStreamProducer::Pending { consumers } => {
let producer = gst_utils::StreamProducer::from(appsink);
let mut links = HashMap::new();
for consumer in consumers {
ensure_different_toplevel(appsink, &consumer);
let link = producer
.add_consumer(&consumer)
.expect("consumer should not have already been added");
links.insert(consumer, link);
}
producers.insert(
name.to_string(),
InterStreamProducer::Active {
producer: producer.clone(),
links,
},
);
Ok(producer)
}
InterStreamProducer::Active { .. } => {
producers.insert(name.to_string(), producer);
Err(anyhow!(
"An active producer already exists with name {}",
name
))
}
}
} else {
let producer = gst_utils::StreamProducer::from(appsink);
producers.insert(
name.to_string(),
InterStreamProducer::Active {
producer: producer.clone(),
links: HashMap::new(),
},
);
Ok(producer)
}
}
pub fn release(name: &str) -> Option<gst_app::AppSink> {
let mut producers = PRODUCERS.lock().unwrap();
if let Some(producer) = producers.remove(name) {
match producer {
InterStreamProducer::Pending { .. } => None,
InterStreamProducer::Active { links, producer } => {
producers.insert(
name.to_string(),
InterStreamProducer::Pending {
consumers: links.into_keys().collect(),
},
);
Some(producer.appsink().clone())
}
}
} else {
None
}
}
pub fn subscribe(name: &str, consumer: &gst_app::AppSrc) {
let mut producers = PRODUCERS.lock().unwrap();
if let Some(producer) = producers.get_mut(name) {
match producer {
InterStreamProducer::Pending { consumers } => {
consumers.insert(consumer.clone());
}
InterStreamProducer::Active { producer, links } => {
ensure_different_toplevel(producer.appsink(), consumer);
let link = producer
.add_consumer(consumer)
.expect("consumer should not already have been added");
links.insert(consumer.clone(), link);
}
}
} else {
let producer = InterStreamProducer::Pending {
consumers: [consumer.clone()].into(),
};
producers.insert(name.to_string(), producer);
}
}
pub fn unsubscribe(name: &str, consumer: &gst_app::AppSrc) -> bool {
let mut producers = PRODUCERS.lock().unwrap();
if let Some(producer) = producers.get_mut(name) {
match producer {
InterStreamProducer::Pending { consumers } => consumers.remove(consumer),
InterStreamProducer::Active { links, .. } => links.remove(consumer).is_some(),
}
} else {
false
}
}
}

View file

@ -1,138 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
use gst::prelude::*;
use serial_test::serial;
use pretty_assertions::assert_eq;
fn init() {
use std::sync::Once;
static INIT: Once = Once::new();
INIT.call_once(|| {
gst::init().unwrap();
gstrsinter::plugin_register_static().unwrap();
});
}
fn start_consumer(producer_name: &str) -> gst_check::Harness {
let mut hc = gst_check::Harness::new("intersrc");
hc.element()
.unwrap()
.set_property("producer-name", producer_name);
hc.play();
hc
}
fn start_producer(producer_name: &str) -> (gst::Pad, gst::Element) {
let element = gst::ElementFactory::make("intersink").build().unwrap();
element.set_property("producer-name", producer_name);
element.set_state(gst::State::Playing).unwrap();
let sinkpad = element.static_pad("sink").unwrap();
let srcpad = gst::Pad::new(gst::PadDirection::Src);
srcpad.set_active(true).unwrap();
srcpad.link(&sinkpad).unwrap();
srcpad.push_event(gst::event::StreamStart::builder("foo").build());
srcpad
.push_event(gst::event::Caps::builder(&gst::Caps::builder("video/x-raw").build()).build());
srcpad.push_event(
gst::event::Segment::builder(&gst::FormattedSegment::<gst::format::Time>::new()).build(),
);
(srcpad, element)
}
fn push_one(srcpad: &gst::Pad, pts: gst::ClockTime) {
let mut inbuf = gst::Buffer::with_size(1).unwrap();
{
let buf = inbuf.get_mut().unwrap();
buf.set_pts(pts);
}
srcpad.push(inbuf).unwrap();
}
#[test]
#[serial]
fn test_forward_one_buffer() {
init();
let mut hc = start_consumer("p1");
let (srcpad, element) = start_producer("p1");
push_one(&srcpad, gst::ClockTime::from_nseconds(1));
let outbuf = hc.pull().unwrap();
assert_eq!(outbuf.pts(), Some(gst::ClockTime::from_nseconds(1)));
element.set_state(gst::State::Null).unwrap();
}
#[test]
#[serial]
fn test_change_name_of_producer() {
init();
let mut hc1 = start_consumer("p1");
let mut hc2 = start_consumer("p2");
let (srcpad, element) = start_producer("p1");
/* Once this returns, the buffer should have been dispatched only to hc1 */
push_one(&srcpad, gst::ClockTime::from_nseconds(1));
let outbuf = hc1.pull().unwrap();
assert_eq!(outbuf.pts(), Some(gst::ClockTime::from_nseconds(1)));
element.set_property("producer-name", "p2");
/* This should only get dispatched to hc2, and it should be its first buffer */
push_one(&srcpad, gst::ClockTime::from_nseconds(2));
let outbuf = hc2.pull().unwrap();
assert_eq!(outbuf.pts(), Some(gst::ClockTime::from_nseconds(2)));
element.set_property("producer-name", "p1");
/* Back to hc1, which should not see the buffer we pushed in the previous step */
push_one(&srcpad, gst::ClockTime::from_nseconds(3));
let outbuf = hc1.pull().unwrap();
assert_eq!(outbuf.pts(), Some(gst::ClockTime::from_nseconds(3)));
element.set_state(gst::State::Null).unwrap();
}
#[test]
#[serial]
fn test_change_producer_name() {
init();
let mut hc = start_consumer("p1");
let (srcpad1, element1) = start_producer("p1");
let (srcpad2, element2) = start_producer("p2");
/* This buffer should be dispatched to no consumer */
push_one(&srcpad2, gst::ClockTime::from_nseconds(1));
/* This one should be dispatched to hc, and it should be its first buffer */
push_one(&srcpad1, gst::ClockTime::from_nseconds(2));
let outbuf = hc.pull().unwrap();
assert_eq!(outbuf.pts(), Some(gst::ClockTime::from_nseconds(2)));
hc.element().unwrap().set_property("producer-name", "p2");
/* This buffer should be dispatched to no consumer */
push_one(&srcpad1, gst::ClockTime::from_nseconds(3));
/* This one should be dispatched to hc, and it should be its next buffer */
push_one(&srcpad2, gst::ClockTime::from_nseconds(4));
let outbuf = hc.pull().unwrap();
assert_eq!(outbuf.pts(), Some(gst::ClockTime::from_nseconds(4)));
element1.set_state(gst::State::Null).unwrap();
element2.set_state(gst::State::Null).unwrap();
}

View file

@ -1,43 +0,0 @@
[package]
name = "gst-plugin-originalbuffer"
version.workspace = true
authors = ["Olivier Crête <olivier.crete@collabora.com>"]
repository.workspace = true
license = "MPL-2.0"
description = "GStreamer Origin buffer meta Plugin"
edition.workspace = true
rust-version.workspace = true
[dependencies]
glib.workspace = true
gst.workspace = true
gst-video.workspace = true
atomic_refcell = "0.1"
once_cell.workspace = true
[lib]
name = "gstoriginalbuffer"
crate-type = ["cdylib", "rlib"]
path = "src/lib.rs"
[build-dependencies]
gst-plugin-version-helper.workspace = true
[features]
static = []
capi = []
doc = ["gst/v1_16"]
[package.metadata.capi]
min_version = "0.9.21"
[package.metadata.capi.header]
enabled = false
[package.metadata.capi.library]
install_subdir = "gstreamer-1.0"
versioning = false
import_library = false
[package.metadata.capi.pkg_config]
requires_private = "gstreamer-1.0, gstreamer-base-1.0, gobject-2.0, glib-2.0, gmodule-2.0"

View file

@ -1,3 +0,0 @@
fn main() {
gst_plugin_version_helper::info()
}

View file

@ -1,38 +0,0 @@
// Copyright (C) 2024 Collabora Ltd
// @author: Olivier Crête <olivier.crete@collabora.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// <https://mozilla.org/MPL/2.0/>.
//
// SPDX-License-Identifier: MPL-2.0
#![allow(clippy::non_send_fields_in_send_ty, unused_doc_comments)]
/**
* plugin-originalbuffer:
*
* Since: plugins-rs-0.12 */
use gst::glib;
mod originalbuffermeta;
mod originalbufferrestore;
mod originalbuffersave;
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
originalbuffersave::register(plugin)?;
originalbufferrestore::register(plugin)?;
Ok(())
}
gst::plugin_define!(
originalbuffer,
env!("CARGO_PKG_DESCRIPTION"),
plugin_init,
concat!(env!("CARGO_PKG_VERSION"), "-", env!("COMMIT_ID")),
"MPL",
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_REPOSITORY"),
env!("BUILD_REL_DATE")
);

View file

@ -1,199 +0,0 @@
// Copyright (C) 2024 Collabora Ltd
// @author: Olivier Crête <olivier.crete@collabora.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// <https://mozilla.org/MPL/2.0/>.
//
// SPDX-License-Identifier: MPL-2.0
use gst::prelude::*;
use std::fmt;
use std::mem;
#[repr(transparent)]
pub struct OriginalBufferMeta(imp::OriginalBufferMeta);
unsafe impl Send for OriginalBufferMeta {}
unsafe impl Sync for OriginalBufferMeta {}
impl OriginalBufferMeta {
pub fn add(
buffer: &mut gst::BufferRef,
original: gst::Buffer,
caps: Option<gst::Caps>,
) -> gst::MetaRefMut<'_, Self, gst::meta::Standalone> {
unsafe {
// Manually dropping because gst_buffer_add_meta() takes ownership of the
// content of the struct
let mut params =
mem::ManuallyDrop::new(imp::OriginalBufferMetaParams { original, caps });
let meta = gst::ffi::gst_buffer_add_meta(
buffer.as_mut_ptr(),
imp::original_buffer_meta_get_info(),
&mut *params as *mut imp::OriginalBufferMetaParams as gst::glib::ffi::gpointer,
) as *mut imp::OriginalBufferMeta;
Self::from_mut_ptr(buffer, meta)
}
}
pub fn replace(&mut self, original: gst::Buffer, caps: Option<gst::Caps>) {
self.0.original = Some(original);
self.0.caps = caps;
}
pub fn original(&self) -> &gst::Buffer {
self.0.original.as_ref().unwrap()
}
pub fn caps(&self) -> &gst::Caps {
self.0.caps.as_ref().unwrap()
}
}
unsafe impl MetaAPI for OriginalBufferMeta {
type GstType = imp::OriginalBufferMeta;
fn meta_api() -> gst::glib::Type {
imp::original_buffer_meta_api_get_type()
}
}
impl fmt::Debug for OriginalBufferMeta {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("OriginalBufferMeta")
.field("buffer", &self.original())
.finish()
}
}
mod imp {
use gst::glib::translate::*;
use once_cell::sync::Lazy;
use std::mem;
use std::ptr;
pub(super) struct OriginalBufferMetaParams {
pub original: gst::Buffer,
pub caps: Option<gst::Caps>,
}
#[repr(C)]
pub struct OriginalBufferMeta {
parent: gst::ffi::GstMeta,
pub(super) original: Option<gst::Buffer>,
pub(super) caps: Option<gst::Caps>,
}
pub(super) fn original_buffer_meta_api_get_type() -> glib::Type {
static TYPE: Lazy<glib::Type> = Lazy::new(|| unsafe {
let t = from_glib(gst::ffi::gst_meta_api_type_register(
b"GstOriginalBufferMetaAPI\0".as_ptr() as *const _,
[ptr::null::<std::os::raw::c_char>()].as_ptr() as *mut *const _,
));
assert_ne!(t, glib::Type::INVALID);
t
});
*TYPE
}
unsafe extern "C" fn original_buffer_meta_init(
meta: *mut gst::ffi::GstMeta,
params: glib::ffi::gpointer,
_buffer: *mut gst::ffi::GstBuffer,
) -> glib::ffi::gboolean {
assert!(!params.is_null());
let meta = &mut *(meta as *mut OriginalBufferMeta);
let params = ptr::read(params as *const OriginalBufferMetaParams);
let OriginalBufferMetaParams { original, caps } = params;
ptr::write(&mut meta.original, Some(original));
ptr::write(&mut meta.caps, caps);
true.into_glib()
}
unsafe extern "C" fn original_buffer_meta_free(
meta: *mut gst::ffi::GstMeta,
_buffer: *mut gst::ffi::GstBuffer,
) {
let meta = &mut *(meta as *mut OriginalBufferMeta);
meta.original = None;
meta.caps = None;
}
unsafe extern "C" fn original_buffer_meta_transform(
dest: *mut gst::ffi::GstBuffer,
meta: *mut gst::ffi::GstMeta,
_buffer: *mut gst::ffi::GstBuffer,
_type_: glib::ffi::GQuark,
_data: glib::ffi::gpointer,
) -> glib::ffi::gboolean {
let dest = gst::BufferRef::from_mut_ptr(dest);
let meta = &*(meta as *const OriginalBufferMeta);
if dest.meta::<super::OriginalBufferMeta>().is_some() {
return true.into_glib();
}
// We don't store a ref in the meta if it's self-refencing, but we add it
// when copying the meta to another buffer.
super::OriginalBufferMeta::add(
dest,
meta.original.as_ref().unwrap().clone(),
meta.caps.clone(),
);
true.into_glib()
}
pub(super) fn original_buffer_meta_get_info() -> *const gst::ffi::GstMetaInfo {
struct MetaInfo(ptr::NonNull<gst::ffi::GstMetaInfo>);
unsafe impl Send for MetaInfo {}
unsafe impl Sync for MetaInfo {}
static META_INFO: Lazy<MetaInfo> = Lazy::new(|| unsafe {
MetaInfo(
ptr::NonNull::new(gst::ffi::gst_meta_register(
original_buffer_meta_api_get_type().into_glib(),
b"OriginalBufferMeta\0".as_ptr() as *const _,
mem::size_of::<OriginalBufferMeta>(),
Some(original_buffer_meta_init),
Some(original_buffer_meta_free),
Some(original_buffer_meta_transform),
) as *mut gst::ffi::GstMetaInfo)
.expect("Failed to register meta API"),
)
});
META_INFO.0.as_ptr()
}
}
#[test]
fn test() {
gst::init().unwrap();
let mut b = gst::Buffer::with_size(10).unwrap();
let caps = gst::Caps::new_empty_simple("video/x-raw");
let copy = b.copy();
let m = OriginalBufferMeta::add(b.make_mut(), copy, Some(caps.clone()));
assert_eq!(m.caps(), caps.as_ref());
assert_eq!(m.original().clone(), b);
let b2: gst::Buffer = b.copy_deep().unwrap();
let m = b.meta::<OriginalBufferMeta>().unwrap();
assert_eq!(m.caps(), caps.as_ref());
assert_eq!(m.original(), &b);
let m = b2.meta::<OriginalBufferMeta>().unwrap();
assert_eq!(m.caps(), caps.as_ref());
assert_eq!(m.original(), &b);
let b3: gst::Buffer = b2.copy_deep().unwrap();
drop(b2);
let m = b3.meta::<OriginalBufferMeta>().unwrap();
assert_eq!(m.caps(), caps.as_ref());
assert_eq!(m.original(), &b);
}

View file

@ -1,315 +0,0 @@
// Copyright (C) 2024 Collabora Ltd
// @author: Olivier Crête <olivier.crete@collabora.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// <https://mozilla.org/MPL/2.0/>.
//
// SPDX-License-Identifier: MPL-2.0
use gst::glib;
use gst::subclass::prelude::*;
use gst_video::prelude::*;
use atomic_refcell::AtomicRefCell;
use crate::originalbuffermeta;
use crate::originalbuffermeta::OriginalBufferMeta;
struct CapsState {
caps: gst::Caps,
vinfo: Option<gst_video::VideoInfo>,
}
impl Default for CapsState {
fn default() -> Self {
CapsState {
caps: gst::Caps::new_empty(),
vinfo: None,
}
}
}
#[derive(Default)]
struct State {
sinkpad_caps: CapsState,
meta_caps: CapsState,
sinkpad_segment: Option<gst::Event>,
}
pub struct OriginalBufferRestore {
state: AtomicRefCell<State>,
src_pad: gst::Pad,
sink_pad: gst::Pad,
}
use once_cell::sync::Lazy;
#[cfg(unused_code)]
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
gst::DebugCategory::new(
"originalbufferrestore",
gst::DebugColorFlags::empty(),
Some("Restore Original buffer as meta"),
)
});
#[glib::object_subclass]
impl ObjectSubclass for OriginalBufferRestore {
const NAME: &'static str = "GstOriginalBufferRestore";
type Type = super::OriginalBufferRestore;
type ParentType = gst::Element;
fn with_class(klass: &Self::Class) -> Self {
let sink_templ = klass.pad_template("sink").unwrap();
let src_templ = klass.pad_template("src").unwrap();
let sink_pad = gst::Pad::builder_from_template(&sink_templ)
.chain_function(|pad, parent, buffer| {
OriginalBufferRestore::catch_panic_pad_function(
parent,
|| Err(gst::FlowError::Error),
|obj| obj.sink_chain(pad, buffer),
)
})
.event_function(|pad, parent, event| {
OriginalBufferRestore::catch_panic_pad_function(
parent,
|| false,
|obj| obj.sink_event(pad, parent, event),
)
})
.query_function(|pad, parent, query| {
OriginalBufferRestore::catch_panic_pad_function(
parent,
|| false,
|obj| obj.sink_query(pad, parent, query),
)
})
.build();
let src_pad = gst::Pad::builder_from_template(&src_templ)
.event_function(|pad, parent, event| {
OriginalBufferRestore::catch_panic_pad_function(
parent,
|| false,
|obj| obj.src_event(pad, parent, event),
)
})
.build();
Self {
src_pad,
sink_pad,
state: Default::default(),
}
}
}
impl ObjectImpl for OriginalBufferRestore {
fn constructed(&self) {
self.parent_constructed();
let obj = self.obj();
obj.add_pad(&self.sink_pad).unwrap();
obj.add_pad(&self.src_pad).unwrap();
}
}
impl GstObjectImpl for OriginalBufferRestore {}
impl ElementImpl for OriginalBufferRestore {
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
gst::subclass::ElementMetadata::new(
"Original Buffer Restore",
"Generic",
"Restores a reference to the buffer in a meta",
"Olivier Crête <olivier.crete@collabora.com>",
)
});
Some(&*ELEMENT_METADATA)
}
fn pad_templates() -> &'static [gst::PadTemplate] {
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
let caps = gst::Caps::new_any();
let src_pad_template = gst::PadTemplate::new(
"src",
gst::PadDirection::Src,
gst::PadPresence::Always,
&caps,
)
.unwrap();
let sink_pad_template = gst::PadTemplate::new(
"sink",
gst::PadDirection::Sink,
gst::PadPresence::Always,
&caps,
)
.unwrap();
vec![src_pad_template, sink_pad_template]
});
PAD_TEMPLATES.as_ref()
}
fn change_state(
&self,
transition: gst::StateChange,
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
let ret = self.parent_change_state(transition)?;
if transition == gst::StateChange::PausedToReady {
let mut state = self.state.borrow_mut();
*state = State::default();
}
Ok(ret)
}
}
impl OriginalBufferRestore {
fn sink_event(
&self,
pad: &gst::Pad,
parent: Option<&impl IsA<gst::Object>>,
event: gst::Event,
) -> bool {
match event.view() {
gst::EventView::Caps(e) => {
let mut state = self.state.borrow_mut();
let caps = e.caps_owned();
let vinfo = gst_video::VideoInfo::from_caps(&caps).ok();
state.sinkpad_caps = CapsState { caps, vinfo };
true
}
gst::EventView::Segment(_) => {
let mut state = self.state.borrow_mut();
state.sinkpad_segment = Some(event);
true
}
_ => gst::Pad::event_default(pad, parent, event),
}
}
fn src_event(
&self,
pad: &gst::Pad,
parent: Option<&impl IsA<gst::Object>>,
event: gst::Event,
) -> bool {
if event.type_() == gst::EventType::Reconfigure
|| event.has_name("gst-original-buffer-forward-upstream-event")
{
let s = gst::Structure::builder("gst-original-buffer-forward-upstream-event")
.field("event", event)
.build();
let event = gst::event::CustomUpstream::new(s);
self.sink_pad.push_event(event)
} else {
gst::Pad::event_default(pad, parent, event)
}
}
fn sink_query(
&self,
pad: &gst::Pad,
parent: Option<&impl IsA<gst::Object>>,
query: &mut gst::QueryRef,
) -> bool {
if let gst::QueryViewMut::Custom(_) = query.view_mut() {
let s = query.structure_mut();
if s.has_name("gst-original-buffer-forward-query") {
if let Ok(mut q) = s.get::<gst::Query>("query") {
s.remove_field("query");
assert!(q.is_writable());
let res = self.src_pad.peer_query(q.get_mut().unwrap());
s.set("query", q);
s.set("result", res);
return true;
}
}
}
gst::Pad::query_default(pad, parent, query)
}
fn sink_chain(
&self,
_pad: &gst::Pad,
inbuf: gst::Buffer,
) -> Result<gst::FlowSuccess, gst::FlowError> {
let Some(ometa) = inbuf.meta::<OriginalBufferMeta>() else {
//gst::element_warning!(self, gst::StreamError::Failed, ["Buffer {} is missing the GstOriginalBufferMeta, put originalbuffersave upstream in your pipeline", buffer]);
return Ok(gst::FlowSuccess::Ok);
};
let mut state = self.state.borrow_mut();
let meta_caps = &mut state.meta_caps;
if &meta_caps.caps != ometa.caps() {
if !self.src_pad.push_event(gst::event::Caps::new(ometa.caps())) {
return Err(gst::FlowError::NotNegotiated);
}
meta_caps.caps = ometa.caps().clone();
meta_caps.vinfo = gst_video::VideoInfo::from_caps(&meta_caps.caps).ok();
}
let mut outbuf = ometa.original().copy();
inbuf
.copy_into(
outbuf.make_mut(),
gst::BufferCopyFlags::TIMESTAMPS | gst::BufferCopyFlags::FLAGS,
..,
)
.unwrap();
for meta in inbuf.iter_meta::<gst::Meta>() {
if meta.api() == originalbuffermeta::OriginalBufferMeta::meta_api() {
continue;
}
if meta.has_tag::<gst::meta::tags::Memory>()
|| meta.has_tag::<gst::meta::tags::MemoryReference>()
{
continue;
}
if meta.has_tag::<gst_video::video_meta::tags::Size>() {
if let (Some(ref meta_vinfo), Some(ref sink_vinfo)) =
(&state.meta_caps.vinfo, &state.sinkpad_caps.vinfo)
{
if (meta_vinfo.width() != sink_vinfo.width()
|| meta_vinfo.height() != sink_vinfo.height())
&& meta
.transform(
outbuf.make_mut(),
&gst_video::video_meta::VideoMetaTransformScale::new(
sink_vinfo, meta_vinfo,
),
)
.is_ok()
{
continue;
}
}
}
let _ = meta.transform(
outbuf.make_mut(),
&gst::meta::MetaTransformCopy::new(false, ..),
);
}
if let Some(event) = state.sinkpad_segment.take() {
if !self.src_pad.push_event(event) {
return Err(gst::FlowError::Error);
}
}
self.src_pad.push(outbuf)
}
}

View file

@ -1,31 +0,0 @@
// Copyright (C) 2024 Collabora Ltd
// @author: Olivier Crête <olivier.crete@collabora.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// <https://mozilla.org/MPL/2.0/>.
//
// SPDX-License-Identifier: MPL-2.0
/**
* SECTION:element-originalbufferrestore
*
* See originalbuffersave for details
*/
use gst::glib;
use gst::prelude::*;
mod imp;
glib::wrapper! {
pub struct OriginalBufferRestore(ObjectSubclass<imp::OriginalBufferRestore>) @extends gst::Element, gst::Object;
}
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
gst::Element::register(
Some(plugin),
"originalbufferrestore",
gst::Rank::NONE,
OriginalBufferRestore::static_type(),
)
}

View file

@ -1,205 +0,0 @@
// Copyright (C) 2024 Collabora Ltd
// @author: Olivier Crête <olivier.crete@collabora.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// <https://mozilla.org/MPL/2.0/>.
//
// SPDX-License-Identifier: MPL-2.0
use gst::glib;
use gst::prelude::*;
use gst::subclass::prelude::*;
use crate::originalbuffermeta::OriginalBufferMeta;
pub struct OriginalBufferSave {
src_pad: gst::Pad,
sink_pad: gst::Pad,
}
use once_cell::sync::Lazy;
#[cfg(unused_code)]
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
gst::DebugCategory::new(
"originalbuffersave",
gst::DebugColorFlags::empty(),
Some("Save Original buffer as meta"),
)
});
#[glib::object_subclass]
impl ObjectSubclass for OriginalBufferSave {
const NAME: &'static str = "GstOriginalBufferSave";
type Type = super::OriginalBufferSave;
type ParentType = gst::Element;
fn with_class(klass: &Self::Class) -> Self {
let sink_templ = klass.pad_template("sink").unwrap();
let src_templ = klass.pad_template("src").unwrap();
let sink_pad = gst::Pad::builder_from_template(&sink_templ)
.chain_function(|pad, parent, buffer| {
OriginalBufferSave::catch_panic_pad_function(
parent,
|| Err(gst::FlowError::Error),
|obj| obj.sink_chain(pad, buffer),
)
})
.query_function(|pad, parent, query| {
OriginalBufferSave::catch_panic_pad_function(
parent,
|| false,
|obj| obj.sink_query(pad, parent, query),
)
})
.flags(gst::PadFlags::PROXY_CAPS | gst::PadFlags::PROXY_ALLOCATION)
.build();
let src_pad = gst::Pad::builder_from_template(&src_templ)
.event_function(|pad, parent, event| {
OriginalBufferSave::catch_panic_pad_function(
parent,
|| false,
|obj| obj.src_event(pad, parent, event),
)
})
.build();
Self { src_pad, sink_pad }
}
}
impl ObjectImpl for OriginalBufferSave {
fn constructed(&self) {
self.parent_constructed();
let obj = self.obj();
obj.add_pad(&self.sink_pad).unwrap();
obj.add_pad(&self.src_pad).unwrap();
}
}
impl GstObjectImpl for OriginalBufferSave {}
impl ElementImpl for OriginalBufferSave {
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
gst::subclass::ElementMetadata::new(
"Original Buffer Save",
"Generic",
"Saves a reference to the buffer in a meta",
"Olivier Crête <olivier.crete@collabora.com>",
)
});
Some(&*ELEMENT_METADATA)
}
fn pad_templates() -> &'static [gst::PadTemplate] {
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
let caps = gst::Caps::new_any();
let src_pad_template = gst::PadTemplate::new(
"src",
gst::PadDirection::Src,
gst::PadPresence::Always,
&caps,
)
.unwrap();
let sink_pad_template = gst::PadTemplate::new(
"sink",
gst::PadDirection::Sink,
gst::PadPresence::Always,
&caps,
)
.unwrap();
vec![src_pad_template, sink_pad_template]
});
PAD_TEMPLATES.as_ref()
}
}
impl OriginalBufferSave {
fn forward_query(&self, query: gst::Query) -> Option<gst::Query> {
let mut s = gst::Structure::new_empty("gst-original-buffer-forward-query");
s.set("query", query);
let mut query = gst::query::Custom::new(s);
if self.src_pad.peer_query(&mut query) {
let s = query.structure_mut();
if let (Ok(true), Ok(q)) = (s.get("result"), s.get::<gst::Query>("query")) {
Some(q)
} else {
None
}
} else {
None
}
}
fn sink_chain(
&self,
pad: &gst::Pad,
inbuf: gst::Buffer,
) -> Result<gst::FlowSuccess, gst::FlowError> {
let mut buf = inbuf.copy();
let caps = pad.current_caps();
if let Some(mut meta) = buf.make_mut().meta_mut::<OriginalBufferMeta>() {
meta.replace(inbuf, caps);
} else {
OriginalBufferMeta::add(buf.make_mut(), inbuf, caps);
}
self.src_pad.push(buf)
}
fn sink_query(
&self,
pad: &gst::Pad,
parent: Option<&impl IsA<gst::Object>>,
query: &mut gst::QueryRef,
) -> bool {
let ret = gst::Pad::query_default(pad, parent, query);
if !ret {
return ret;
}
if let gst::QueryViewMut::Caps(q) = query.view_mut() {
if let Some(caps) = q.result_owned() {
let forwarding_q = gst::query::Caps::new(Some(&caps)).into();
if let Some(forwarding_q) = self.forward_query(forwarding_q) {
if let gst::QueryView::Caps(c) = forwarding_q.view() {
let res = c
.result_owned()
.map(|c| c.intersect_with_mode(&caps, gst::CapsIntersectMode::First));
q.set_result(&res);
}
}
}
}
// We should also do allocation queries, but that requires supporting the same
// intersection semantics as gsttee, which should be in a helper function.
true
}
fn src_event(
&self,
pad: &gst::Pad,
parent: Option<&impl IsA<gst::Object>>,
event: gst::Event,
) -> bool {
let event = if event.has_name("gst-original-buffer-forward-upstream-event") {
event.structure().unwrap().get("event").unwrap()
} else {
event
};
gst::Pad::event_default(pad, parent, event)
}
}

View file

@ -1,41 +0,0 @@
// Copyright (C) 2024 Collabora Ltd
// @author: Olivier Crête <olivier.crete@collabora.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// <https://mozilla.org/MPL/2.0/>.
//
// SPDX-License-Identifier: MPL-2.0
/**
* SECTION:element-originalbuffersave
*
* GStreamer elements to store the original buffer and restore it later
*
* In many analysis scenario (for example machine learning), it is desirable to
* use a pre-processed buffer, for example by lowering the resolution, but we may
* want to take the output of this analysis, and apply it to the original buffer.
*
* These elements do just this, the typical usage would be a pipeline like:
*
* `... ! originalbuffersave ! videoconvertscale ! video/x-raw, width=100, height=100 ! analysiselement ! originalbufferrestore ! ...`
*
* The originalbufferrestore element will "restore" the buffer that was entered to the "save" element, but will keep any metadata that was added later.
*/
use gst::glib;
use gst::prelude::*;
mod imp;
glib::wrapper! {
pub struct OriginalBufferSave(ObjectSubclass<imp::OriginalBufferSave>) @extends gst::Element, gst::Object;
}
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
gst::Element::register(
Some(plugin),
"originalbuffersave",
gst::Rank::NONE,
OriginalBufferSave::static_type(),
)
}

View file

@ -1,16 +1,16 @@
[package]
name = "gst-plugin-sodium"
version.workspace = true
version = "0.10.3"
authors = ["Jordan Petridis <jordan@centricular.com>"]
repository.workspace = true
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
description = "GStreamer plugin for libsodium-based file encryption and decryption"
license = "MIT"
edition.workspace = true
rust-version.workspace = true
edition = "2021"
rust-version = "1.63"
[dependencies]
gst.workspace = true
gst-base.workspace = true
gst = { package="gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
sodiumoxide = "0.2.1"
once_cell = "1.3.0"
hex = "0.4"
@ -24,8 +24,18 @@ serde_json = { version = "1.0", optional = true }
[dev-dependencies]
pretty_assertions = "1"
rand = "0.8"
gst-check.workspace = true
gst-app.workspace = true
[dev-dependencies.gst-check]
git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs"
branch = "0.20"
version = "0.20"
package="gstreamer-check"
[dev-dependencies.gst-app]
git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs"
branch = "0.20"
version = "0.20"
package="gstreamer-app"
[lib]
name = "gstsodium"
@ -48,7 +58,7 @@ path = "examples/decrypt_example.rs"
required-features = ["serde", "serde_json", "clap"]
[build-dependencies]
gst-plugin-version-helper.workspace = true
gst-plugin-version-helper = { path="../../version-helper", version = "0.7" }
[features]
static = []
@ -56,7 +66,7 @@ capi = []
doc = ["gst/v1_18"]
[package.metadata.capi]
min_version = "0.9.21"
min_version = "0.8.0"
[package.metadata.capi.header]
enabled = false
@ -64,7 +74,6 @@ enabled = false
[package.metadata.capi.library]
install_subdir = "gstreamer-1.0"
versioning = false
import_library = false
[package.metadata.capi.pkg_config]
requires_private = "gstreamer-1.0, gstreamer-base-1.0, gobject-2.0, glib-2.0, gmodule-2.0, libsodium"

View file

@ -1,23 +0,0 @@
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

Some files were not shown because too many files have changed in this diff Show more