mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2024-09-29 15:22:07 +00:00
Compare commits
36 commits
Author | SHA1 | Date | |
---|---|---|---|
|
4e4d226b71 | ||
|
b903503a01 | ||
|
20b18c23e6 | ||
|
160033cb5b | ||
|
3dde725560 | ||
|
1113e0aa51 | ||
|
9b33184018 | ||
|
528f46a510 | ||
|
8b8f10691c | ||
|
6936bf65c4 | ||
|
b3cb41eae3 | ||
|
84bae8b9cf | ||
|
eb296a0e63 | ||
|
b64da48508 | ||
|
37877efc90 | ||
|
f22b3420b6 | ||
|
b17348b08d | ||
|
bf0a23d560 | ||
|
384a9d05f0 | ||
|
eda0d9abe1 | ||
|
3c4d22bc5c | ||
|
56a25af929 | ||
|
54adcb8482 | ||
|
46d7c177c2 | ||
|
2fc2357306 | ||
|
9cd68ffb5f | ||
|
73a5703eeb | ||
|
7d7f73768b | ||
|
510cda303a | ||
|
d2cd2ef5ac | ||
|
a505dba3a2 | ||
|
a4d5a35403 | ||
|
8a6895a73e | ||
|
f3c1a91fb9 | ||
|
6df679d69f | ||
|
85bf8d6c63 |
769 changed files with 28293 additions and 130031 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,7 +1,7 @@
|
||||||
|
Cargo.lock
|
||||||
target
|
target
|
||||||
*~
|
*~
|
||||||
*.bk
|
*.bk
|
||||||
*.swp
|
*.swp
|
||||||
.vscode
|
.vscode
|
||||||
builddir
|
builddir
|
||||||
.meson-subproject-wrap-hash.txt
|
|
295
.gitlab-ci.yml
295
.gitlab-ci.yml
|
@ -1,4 +1,4 @@
|
||||||
.templates_sha: &templates_sha 6a40df92957c8ce9ee741aaccc5daaaf70545b1e
|
.templates_sha: &templates_sha 567700e483aabed992d0a4fea84994a0472deff6
|
||||||
|
|
||||||
include:
|
include:
|
||||||
- project: 'freedesktop/ci-templates'
|
- project: 'freedesktop/ci-templates'
|
||||||
|
@ -14,17 +14,15 @@ include:
|
||||||
file: '/.gitlab-image-tags.yml'
|
file: '/.gitlab-image-tags.yml'
|
||||||
|
|
||||||
variables:
|
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
|
# 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.
|
# to ensure that we are testing against the same thing as GStreamer itself.
|
||||||
# The tag name is included above from the main repo.
|
# The tag name is included above from the main repo.
|
||||||
GSTREAMER_DOC_IMAGE: "registry.freedesktop.org/gstreamer/gstreamer/amd64/fedora:$FEDORA_TAG-main"
|
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_BASE: "registry.freedesktop.org/gstreamer/gstreamer-rs/windows"
|
||||||
WINDOWS_RUST_MINIMUM_IMAGE: "$WINDOWS_BASE:$GST_RS_IMG_WINDOWS_TAG-main-$GST_RS_MSRV"
|
WINDOWS_RUST_MINIMUM_IMAGE: "$WINDOWS_BASE:$GST_RS_IMG_TAG-main-$GST_RS_MSRV"
|
||||||
WINDOWS_RUST_STABLE_IMAGE: "$WINDOWS_BASE:$GST_RS_IMG_WINDOWS_TAG-main-$GST_RS_STABLE"
|
WINDOWS_RUST_STABLE_IMAGE: "$WINDOWS_BASE:$GST_RS_IMG_TAG-main-$GST_RS_STABLE"
|
||||||
|
|
||||||
workflow:
|
workflow:
|
||||||
rules:
|
rules:
|
||||||
|
@ -38,17 +36,10 @@ workflow:
|
||||||
|
|
||||||
default:
|
default:
|
||||||
interruptible: true
|
interruptible: true
|
||||||
# Auto-retry jobs in case of infra failures
|
|
||||||
retry:
|
|
||||||
max: 1
|
|
||||||
when:
|
|
||||||
- 'runner_system_failure'
|
|
||||||
- 'stuck_or_timeout_failure'
|
|
||||||
- 'scheduler_failure'
|
|
||||||
- 'api_failure'
|
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- "trigger"
|
- "trigger"
|
||||||
|
- "prep"
|
||||||
- "lint"
|
- "lint"
|
||||||
- "test"
|
- "test"
|
||||||
- "extras"
|
- "extras"
|
||||||
|
@ -60,7 +51,6 @@ trigger:
|
||||||
stage: 'trigger'
|
stage: 'trigger'
|
||||||
variables:
|
variables:
|
||||||
GIT_STRATEGY: none
|
GIT_STRATEGY: none
|
||||||
tags: [ 'placeholder-job' ]
|
|
||||||
script:
|
script:
|
||||||
- echo "Trigger job done, now running the pipeline."
|
- echo "Trigger job done, now running the pipeline."
|
||||||
rules:
|
rules:
|
||||||
|
@ -76,26 +66,121 @@ trigger:
|
||||||
when: 'manual'
|
when: 'manual'
|
||||||
allow_failure: false
|
allow_failure: false
|
||||||
|
|
||||||
.debian:12:
|
.debian:11:
|
||||||
|
variables:
|
||||||
|
FDO_DISTRIBUTION_VERSION: 'bullseye-slim'
|
||||||
|
before_script:
|
||||||
|
- source ./ci/env.sh
|
||||||
|
- mkdir .cargo && echo -e "[net]\ngit-fetch-with-cli = true" > .cargo/config
|
||||||
|
|
||||||
|
.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: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: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:
|
variables:
|
||||||
SODIUM_USE_PKG_CONFIG: "true"
|
SODIUM_USE_PKG_CONFIG: "true"
|
||||||
after_script:
|
after_script:
|
||||||
- rm -rf target
|
- rm -rf target
|
||||||
before_script:
|
|
||||||
- source ./ci/env.sh
|
|
||||||
- mkdir .cargo && echo -e "[net]\ngit-fetch-with-cli = true" > .cargo/config.toml
|
|
||||||
|
|
||||||
.debian:12-stable:
|
.img-stable:
|
||||||
extends: .debian:12
|
extends:
|
||||||
image: "registry.freedesktop.org/gstreamer/gstreamer-rs/debian/bookworm-slim:$GST_RS_STABLE-$GST_RS_IMG_TAG"
|
- .dist-debian-container
|
||||||
|
- .debian:11-stable
|
||||||
|
needs:
|
||||||
|
- job: 'build-stable'
|
||||||
|
optional: true
|
||||||
|
- job: 'update-stable'
|
||||||
|
optional: true
|
||||||
|
|
||||||
.debian:12-msrv:
|
.img-msrv:
|
||||||
extends: .debian:12
|
extends:
|
||||||
image: "registry.freedesktop.org/gstreamer/gstreamer-rs/debian/bookworm-slim:$GST_RS_MSRV-$GST_RS_IMG_TAG"
|
- .dist-debian-container
|
||||||
|
- .debian:11-msrv
|
||||||
|
needs:
|
||||||
|
- job: 'build-msrv'
|
||||||
|
optional: true
|
||||||
|
- job: 'update-msrv'
|
||||||
|
optional: true
|
||||||
|
|
||||||
.debian:12-nightly:
|
.img-nightly:
|
||||||
extends: .debian:12
|
extends:
|
||||||
image: "registry.freedesktop.org/gstreamer/gstreamer-rs/debian/bookworm-slim:nightly-$GST_RS_IMG_TAG"
|
- .dist-debian-container
|
||||||
|
- .debian:11-nightly
|
||||||
|
needs:
|
||||||
|
- job: 'build-nightly'
|
||||||
|
optional: true
|
||||||
|
- job: 'update-nightly'
|
||||||
|
optional: true
|
||||||
|
|
||||||
.cargo test:
|
.cargo test:
|
||||||
stage: "test"
|
stage: "test"
|
||||||
|
@ -105,47 +190,50 @@ trigger:
|
||||||
RUST_BACKTRACE: 'full'
|
RUST_BACKTRACE: 'full'
|
||||||
script:
|
script:
|
||||||
- rustc --version
|
- rustc --version
|
||||||
- CARGO_FLAGS="-j${FDO_CI_CONCURRENT:-$(nproc)} --locked --color=always --all --all-targets"
|
|
||||||
|
|
||||||
- cargo build $CARGO_FLAGS
|
- cargo build --locked --color=always --workspace --all-targets
|
||||||
- RUST_BACKTRACE=1 G_DEBUG=fatal_warnings cargo test $CARGO_FLAGS
|
- G_DEBUG=fatal_warnings cargo test --locked --color=always --workspace --all-targets
|
||||||
- cargo build $CARGO_FLAGS --all-features --exclude gst-plugin-gtk4
|
- cargo build --locked --color=always --workspace --all-targets --all-features
|
||||||
- RUST_BACKTRACE=1 G_DEBUG=fatal_warnings cargo test $CARGO_FLAGS --all-features --exclude gst-plugin-gtk4
|
- G_DEBUG=fatal_warnings cargo test --locked --color=always --workspace --all-targets --all-features
|
||||||
- cargo build $CARGO_FLAGS --no-default-features
|
- cargo build --locked --color=always --workspace --all-targets --no-default-features
|
||||||
- RUST_BACKTRACE=1 G_DEBUG=fatal_warnings cargo test $CARGO_FLAGS --no-default-features
|
- G_DEBUG=fatal_warnings cargo test --locked --color=always --workspace --all-targets --no-default-features
|
||||||
|
|
||||||
test msrv:
|
test msrv:
|
||||||
extends:
|
extends:
|
||||||
- '.cargo test'
|
- '.cargo test'
|
||||||
- '.debian:12-msrv'
|
- .img-msrv
|
||||||
needs: [ "trigger" ]
|
rules:
|
||||||
|
- if: '$UPDATE_IMG == null || $UPDATE_IMG == "msrv"'
|
||||||
|
|
||||||
test stable:
|
test stable:
|
||||||
extends:
|
extends:
|
||||||
- '.cargo test'
|
- '.cargo test'
|
||||||
- '.debian:12-stable'
|
- .img-stable
|
||||||
needs: [ "trigger" ]
|
rules:
|
||||||
|
- if: '$UPDATE_IMG == null || $UPDATE_IMG == "stable"'
|
||||||
|
|
||||||
test nightly:
|
test nightly:
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
extends:
|
extends:
|
||||||
- '.cargo test'
|
- '.cargo test'
|
||||||
- '.debian:12-nightly'
|
- .img-nightly
|
||||||
needs: [ "trigger" ]
|
rules:
|
||||||
|
- if: '$UPDATE_IMG == null || $UPDATE_IMG == "nightly"'
|
||||||
|
|
||||||
.meson:
|
.meson:
|
||||||
extends: .debian:12-stable
|
extends: .img-stable
|
||||||
|
rules:
|
||||||
|
- if: '$UPDATE_IMG == null || $UPDATE_IMG == "stable"'
|
||||||
variables:
|
variables:
|
||||||
# csound-sys only looks at /usr/lib and /usr/local top levels
|
# csound-sys only looks at /usr/lib and /usr/local top levels
|
||||||
CSOUND_LIB_DIR: '/usr/lib/x86_64-linux-gnu/'
|
CSOUND_LIB_DIR: '/usr/lib/x86_64-linux-gnu/'
|
||||||
|
|
||||||
meson shared:
|
meson shared:
|
||||||
extends: .meson
|
extends: .meson
|
||||||
needs: [ "trigger" ]
|
|
||||||
variables:
|
variables:
|
||||||
CI_ARTIFACTS_URL: "${CI_PROJECT_URL}/-/jobs/${CI_JOB_ID}/artifacts/raw/"
|
CI_ARTIFACTS_URL: "${CI_PROJECT_URL}/-/jobs/${CI_JOB_ID}/artifacts/raw/"
|
||||||
script:
|
script:
|
||||||
- meson build --default-library=shared --prefix=$(pwd)/install --fatal-meson-warnings
|
- meson build --default-library=shared --prefix=$(pwd)/install
|
||||||
- ninja -C build install
|
- ninja -C build install
|
||||||
- ./ci/check-installed.py install
|
- ./ci/check-installed.py install
|
||||||
- ninja -C build docs/gst_plugins_cache.json
|
- ninja -C build docs/gst_plugins_cache.json
|
||||||
|
@ -159,7 +247,6 @@ meson shared:
|
||||||
|
|
||||||
meson static:
|
meson static:
|
||||||
extends: .meson
|
extends: .meson
|
||||||
needs: [ "trigger" ]
|
|
||||||
script:
|
script:
|
||||||
- meson build --default-library=static --prefix=$(pwd)/install -Dsodium-source=built-in
|
- meson build --default-library=static --prefix=$(pwd)/install -Dsodium-source=built-in
|
||||||
- ninja -C build install
|
- ninja -C build install
|
||||||
|
@ -181,6 +268,7 @@ documentation:
|
||||||
variables:
|
variables:
|
||||||
MESON_ARGS: >
|
MESON_ARGS: >
|
||||||
-Ddoc=enabled
|
-Ddoc=enabled
|
||||||
|
-Domx=disabled
|
||||||
-Dpython=disabled
|
-Dpython=disabled
|
||||||
-Dlibav=disabled
|
-Dlibav=disabled
|
||||||
-Dlibnice=disabled
|
-Dlibnice=disabled
|
||||||
|
@ -273,7 +361,6 @@ documentation:
|
||||||
- 'docker'
|
- 'docker'
|
||||||
- 'windows'
|
- 'windows'
|
||||||
- '2022'
|
- '2022'
|
||||||
- "gstreamer-windows"
|
|
||||||
script:
|
script:
|
||||||
# Set the code page to UTF-8
|
# Set the code page to UTF-8
|
||||||
- chcp 65001
|
- chcp 65001
|
||||||
|
@ -298,145 +385,75 @@ test windows stable:
|
||||||
image: "$WINDOWS_RUST_STABLE_IMAGE"
|
image: "$WINDOWS_RUST_STABLE_IMAGE"
|
||||||
|
|
||||||
rustfmt:
|
rustfmt:
|
||||||
extends: '.debian:12-stable'
|
extends: .img-stable
|
||||||
stage: "lint"
|
stage: "lint"
|
||||||
tags: [ 'placeholder-job' ]
|
rules:
|
||||||
needs: []
|
- when: 'always'
|
||||||
script:
|
script:
|
||||||
- cargo fmt --version
|
- cargo fmt --version
|
||||||
- cargo fmt -- --color=always --check
|
- 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:
|
check commits:
|
||||||
extends: '.debian:12-stable'
|
extends: .img-stable
|
||||||
stage: "lint"
|
stage: "lint"
|
||||||
tags: [ 'placeholder-job' ]
|
rules:
|
||||||
needs: []
|
- when: 'always'
|
||||||
script:
|
script:
|
||||||
- ci-fairy check-commits --textwidth 0 --no-signed-off-by
|
- ci-fairy check-commits --textwidth 0 --no-signed-off-by
|
||||||
- ci/check-for-symlinks.sh
|
|
||||||
- ci/check-meson-version.sh
|
|
||||||
|
|
||||||
clippy:
|
clippy:
|
||||||
extends: '.debian:12-stable'
|
extends: .img-stable
|
||||||
needs:
|
|
||||||
- "trigger"
|
|
||||||
- "test stable"
|
|
||||||
stage: 'extras'
|
stage: 'extras'
|
||||||
variables:
|
variables:
|
||||||
# csound-sys only looks at /usr/lib and /usr/local top levels
|
# csound-sys only looks at /usr/lib and /usr/local top levels
|
||||||
CSOUND_LIB_DIR: '/usr/lib/x86_64-linux-gnu/'
|
CSOUND_LIB_DIR: '/usr/lib/x86_64-linux-gnu/'
|
||||||
|
rules:
|
||||||
|
- when: 'always'
|
||||||
script:
|
script:
|
||||||
- CARGO_FLAGS="-j${FDO_CI_CONCURRENT:-$(nproc)} --locked --color=always --all --all-targets"
|
- cargo clippy --locked --color=always --all --all-features --all-targets -- -D warnings -A unknown-lints
|
||||||
- cargo clippy $CARGO_FLAGS -- -D warnings -A unknown-lints
|
|
||||||
- cargo clippy $CARGO_FLAGS --all-features --exclude gst-plugin-gtk4 -- -D warnings -A unknown-lints
|
|
||||||
- cargo clippy $CARGO_FLAGS --no-default-features -- -D warnings -A unknown-lints
|
|
||||||
|
|
||||||
deny:
|
deny:
|
||||||
extends: .debian:12-stable
|
extends: .img-stable
|
||||||
stage: 'extras'
|
stage: 'extras'
|
||||||
needs:
|
|
||||||
- "trigger"
|
|
||||||
- "test stable"
|
|
||||||
rules:
|
rules:
|
||||||
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
||||||
script:
|
script:
|
||||||
- cargo update --color=always
|
- cargo deny check
|
||||||
- cargo deny --color=always --workspace --all-features check all
|
|
||||||
|
|
||||||
outdated:
|
outdated:
|
||||||
extends: '.debian:12-stable'
|
extends: .img-stable
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
needs:
|
|
||||||
- "trigger"
|
|
||||||
- "test stable"
|
|
||||||
stage: 'extras'
|
stage: 'extras'
|
||||||
rules:
|
rules:
|
||||||
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
||||||
script:
|
script:
|
||||||
- cargo update --color=always
|
- cargo outdated --root-deps-only --exit-code 1 -v
|
||||||
# Ignore bitstream-io until we can update MSRV to 1.80
|
|
||||||
# Ignore test-with until we can update MSRV to 1.77
|
|
||||||
- cargo outdated --color=always --root-deps-only --exit-code 1 -v -i bitstream-io -i test-with
|
|
||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
allow_failure: true
|
allow_failure: true
|
||||||
extends:
|
extends:
|
||||||
- '.debian:12-stable'
|
- .img-stable
|
||||||
needs:
|
|
||||||
- "trigger"
|
|
||||||
- "test stable"
|
|
||||||
stage: 'extras'
|
stage: 'extras'
|
||||||
|
rules:
|
||||||
|
- when: 'always'
|
||||||
variables:
|
variables:
|
||||||
RUSTFLAGS: "-Cinstrument-coverage"
|
RUSTFLAGS: "-Cinstrument-coverage"
|
||||||
LLVM_PROFILE_FILE: "gst-plugins-rs-%p-%m.profraw"
|
LLVM_PROFILE_FILE: "gst-plugins-rs-%p-%m.profraw"
|
||||||
# csound-sys only looks at /usr/lib and /usr/local top levels
|
# csound-sys only looks at /usr/lib and /usr/local top levels
|
||||||
CSOUND_LIB_DIR: '/usr/lib/x86_64-linux-gnu/'
|
CSOUND_LIB_DIR: '/usr/lib/x86_64-linux-gnu/'
|
||||||
script:
|
script:
|
||||||
- CARGO_FLAGS="-j${FDO_CI_CONCURRENT:-$(nproc)} --locked --color=always --all"
|
- cargo test --locked --color=always --all --all-features
|
||||||
|
|
||||||
- cargo test $CARGO_FLAGS --all-features --exclude gst-plugin-gtk4
|
|
||||||
# generate html report
|
# generate html report
|
||||||
- mkdir -p coverage
|
- grcov . --binary-path ./target/debug/ -s . -t html --branch --ignore-not-existing --ignore "*target*" --ignore "*/build.rs" -o ./coverage/
|
||||||
- grcov . --binary-path ./target/debug/ -s . -t html,cobertura --branch --ignore-not-existing --ignore "*target*" --ignore "*/build.rs" -o ./coverage/
|
# generate cobertura report for gitlab integration
|
||||||
|
- grcov . --binary-path ./target/debug/ -s . -t cobertura --branch --ignore-not-existing --ignore "*target*" --ignore "*/build.rs" -o coverage.xml
|
||||||
# output coverage summary for gitlab parsing.
|
# output coverage summary for gitlab parsing.
|
||||||
# TODO: use grcov once https://github.com/mozilla/grcov/issues/556 is fixed
|
# TODO: use grcov once https://github.com/mozilla/grcov/issues/556 is fixed
|
||||||
- grep "%" coverage/html/index.html | head -1 || true
|
- grep "%" coverage/index.html | head -1 || true
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- 'coverage'
|
- 'coverage'
|
||||||
reports:
|
reports:
|
||||||
coverage_report:
|
coverage_report:
|
||||||
coverage_format: cobertura
|
coverage_format: cobertura
|
||||||
path: "coverage/cobertura.xml"
|
path: coverage.xml
|
||||||
|
|
||||||
cerbero trigger:
|
|
||||||
image: $CERBERO_TRIGGER_IMAGE
|
|
||||||
needs: [ "trigger" ]
|
|
||||||
timeout: '4h'
|
|
||||||
tags:
|
|
||||||
- placeholder-job
|
|
||||||
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
|
|
||||||
|
|
|
@ -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 -->
|
|
467
CHANGELOG.md
467
CHANGELOG.md
|
@ -5,446 +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),
|
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).
|
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
|
||||||
|
|
||||||
## [0.13.1] - 2024-08-27
|
|
||||||
### Fixed
|
|
||||||
- transcriberbin: Fix gst-inspect with missing elements.
|
|
||||||
- gtk4paintablesink: Move dmabuf cfg to the correct bracket level.
|
|
||||||
- webrtcsrc: Don't hold the state lock while removing sessions.
|
|
||||||
- rtpbasepay: Various fixes to payloader base class.
|
|
||||||
- webrtcsink: Fix various assertions when finalizing.
|
|
||||||
- webrtcsrc: Make sure to always call end_session() without state lock.
|
|
||||||
- mpegtslivesrc: Handle PCR discontinuities as errors.
|
|
||||||
- ndisrc: Calculate timestamps for metadata buffers too.
|
|
||||||
- Various new clippy warnings.
|
|
||||||
- webrtcsink: Fix segment format mismatch when using a remote offer.
|
|
||||||
- awstranscriber: Fix sanity check in transcribe loop.
|
|
||||||
- whepsrc: Fix incorrect default caps.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- gtk4paintablesink: Enable `gtk::GraphicsOffload::black-background` when
|
|
||||||
building with GTK 4.16 or newer.
|
|
||||||
- gstwebrtc-api: Always include index file in dist for convenience.
|
|
||||||
- rtpbasepay: Negotiate SSRC/PT with downstream via caps for backwards
|
|
||||||
compatibility.
|
|
||||||
- hlssink3: Use more accurate fragment duration from splitmuxsink if
|
|
||||||
available.
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- gtk4paintablesink: Add `window-width` and `window-height` properties.
|
|
||||||
- gtk4paintablesink: Add custom widget for automatically updating window size.
|
|
||||||
- fmp4mux / mp4mux: Add image orientation tag support.
|
|
||||||
- webrtcsink: Add nvv4l2av1enc support.
|
|
||||||
- cmafmux: Add Opus support.
|
|
||||||
|
|
||||||
## [0.13.0] - 2024-07-16
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- rtp: New RTP payloader and depayloader base classes, in addition to new
|
|
||||||
payloader and depayloaders for: PCMA, PCMU, AC-3, AV1 (ported to the new
|
|
||||||
base classes), MPEG-TS, VP8, VP9, MP4A, MP4G, JPEG, Opus, KLV.
|
|
||||||
- originalbuffer: New pair of elements that allows to save a buffer, perform
|
|
||||||
transformations on it and then restore the original buffer but keeping any
|
|
||||||
new analytics and other metadata on it.
|
|
||||||
- gopbuffer: New element for buffering an entire group-of-pictures.
|
|
||||||
- tttocea708: New element for converting timed text to CEA-708 closed captions.
|
|
||||||
- cea708mux: New element for muxing multiple CEA-708 services together.
|
|
||||||
- transcriberbin: Add support for generating CEA-708 closed captions and
|
|
||||||
CEA-608-in-708.
|
|
||||||
- cea708overlay: New overlay element for CEA-708 and CEA-608 closed captions.
|
|
||||||
- dav1ddec: Signal colorimetry in the caps.
|
|
||||||
- webrtc: Add support for RFC7273 clock signalling and synchronization to
|
|
||||||
webrtcsrc and webrtcsink.
|
|
||||||
- tracers: Add a new pad push durations tracer.
|
|
||||||
- transcriberbin: Add support for a secondary audio stream.
|
|
||||||
- quinn: New plugin with a QUIC source and sink element.
|
|
||||||
- rtpgccbwe: New mode based on linear regression instead of a kalman filter.
|
|
||||||
- rtp: New rtpsend and rtprecv elements that provide a new implementation of
|
|
||||||
the rtpbin element with a separate send and receive side.
|
|
||||||
- rtpsrc2: Add support for new rtpsend / rtprecv elements instead of rtpbin.
|
|
||||||
- webrtcsrc: Add multi-producer support.
|
|
||||||
- livesync: Add sync property for enabling/disabling syncing of the output
|
|
||||||
buffers to the clock.
|
|
||||||
- mpegtslivesrc: New element for receiving an MPEG-TS stream, e.g. over SRT or
|
|
||||||
UDP, and exposing the remote PCR clock as a local GStreamer clock.
|
|
||||||
- gtk4paintablesink: Add support for rotations / flipping.
|
|
||||||
- gtk4paintablesink: Add support for RGBx formats in non-GL mode.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- livesync: Queue up to latency buffers instead of requiring a queue of the
|
|
||||||
same size in front of livesync.
|
|
||||||
- livesync: Synchronize the first buffer to the clock too.
|
|
||||||
- livesync: Use correct duration for deciding whether a filler has to be
|
|
||||||
inserted or not.
|
|
||||||
- audioloudnorm: Fix possible off-by-one in the limiter when handling the very
|
|
||||||
last buffer.
|
|
||||||
- webrtcsink: Fix property types for rav1enc.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- sccparse, mccparse: Port from nom to winnow.
|
|
||||||
- uriplaylistbin: Rely on uridecodebin3 gapless logic instead of
|
|
||||||
re-implementing it.
|
|
||||||
- webrtc: Refactor of JavaScript API.
|
|
||||||
- janusvrwebrtcsink: New use-string-ids property to distinguish between
|
|
||||||
integer and string room IDs, instead of always using strings and guessing
|
|
||||||
what the server expects.
|
|
||||||
- janusvrwebrtcsink: Handle more events and expose some via signals.
|
|
||||||
- dav1ddec: Require dav1d 1.3.0.
|
|
||||||
- closedcaption: Drop libcaption C code and switch to a pure Rust
|
|
||||||
implementation.
|
|
||||||
|
|
||||||
## [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 producer-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
|
## [0.10.3] - 2023-03-02
|
||||||
### Added
|
### Added
|
||||||
- tracers: `queue_levels` tracer now also supports printing the `appsrc` levels.
|
- tracers: `queue_levels` tracer now also supports printing the `appsrc` levels.
|
||||||
|
@ -518,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
|
- 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
|
- gtk4: Support for rendering GL textures on X11/EGL, X11/GLX, Wayland and macOS
|
||||||
- hlssink3: Allow generating i-frame-only playlist
|
- 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.
|
without gaps from a potentially unstable source.
|
||||||
- mp4mux: New non-fragmented MP4 muxer element
|
- mp4mux: New non-fragmented MP4 muxer element
|
||||||
- spotifyaudiosrc: Support configurable bitrate
|
- spotifyaudiosrc: Support configurable bitrate
|
||||||
|
@ -535,30 +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: Make the `turn-server` property a `turn-servers` list
|
||||||
- webrtcsink: Move from async-std to tokio
|
- webrtcsink: Move from async-std to tokio
|
||||||
|
|
||||||
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.13.1...HEAD
|
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.10.2...HEAD
|
||||||
[0.13.1]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.13.0...0.13.1
|
|
||||||
[0.13.0]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.12.7...0.13.0
|
|
||||||
[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
|
|
||||||
[0.10.2]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.10.1...0.10.2
|
[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.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
|
[0.10.0]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.9.0...0.10.0
|
||||||
|
|
5095
Cargo.lock
generated
5095
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
51
Cargo.toml
51
Cargo.toml
|
@ -1,5 +1,4 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
|
||||||
|
|
||||||
members = [
|
members = [
|
||||||
"tutorial",
|
"tutorial",
|
||||||
|
@ -9,15 +8,11 @@ members = [
|
||||||
"audio/claxon",
|
"audio/claxon",
|
||||||
"audio/csound",
|
"audio/csound",
|
||||||
"audio/lewton",
|
"audio/lewton",
|
||||||
"audio/speechmatics",
|
|
||||||
"audio/spotify",
|
"audio/spotify",
|
||||||
|
|
||||||
"generic/file",
|
"generic/file",
|
||||||
"generic/originalbuffer",
|
|
||||||
"generic/sodium",
|
"generic/sodium",
|
||||||
"generic/threadshare",
|
"generic/threadshare",
|
||||||
"generic/inter",
|
|
||||||
"generic/gopbuffer",
|
|
||||||
|
|
||||||
"mux/flavors",
|
"mux/flavors",
|
||||||
"mux/fmp4",
|
"mux/fmp4",
|
||||||
|
@ -25,18 +20,15 @@ members = [
|
||||||
|
|
||||||
"net/aws",
|
"net/aws",
|
||||||
"net/hlssink3",
|
"net/hlssink3",
|
||||||
"net/mpegtslive",
|
|
||||||
"net/ndi",
|
"net/ndi",
|
||||||
"net/onvif",
|
"net/onvif",
|
||||||
"net/raptorq",
|
"net/raptorq",
|
||||||
"net/reqwest",
|
"net/reqwest",
|
||||||
"net/rtp",
|
"net/rtp",
|
||||||
"net/rtsp",
|
|
||||||
"net/webrtchttp",
|
"net/webrtchttp",
|
||||||
"net/webrtc",
|
"net/webrtc",
|
||||||
"net/webrtc/protocol",
|
"net/webrtc/protocol",
|
||||||
"net/webrtc/signalling",
|
"net/webrtc/signalling",
|
||||||
"net/quinn",
|
|
||||||
|
|
||||||
"text/ahead",
|
"text/ahead",
|
||||||
"text/json",
|
"text/json",
|
||||||
|
@ -70,28 +62,22 @@ default-members = [
|
||||||
"audio/claxon",
|
"audio/claxon",
|
||||||
"audio/lewton",
|
"audio/lewton",
|
||||||
|
|
||||||
"generic/originalbuffer",
|
|
||||||
"generic/threadshare",
|
"generic/threadshare",
|
||||||
"generic/inter",
|
|
||||||
"generic/gopbuffer",
|
|
||||||
|
|
||||||
"mux/fmp4",
|
"mux/fmp4",
|
||||||
"mux/mp4",
|
"mux/mp4",
|
||||||
|
|
||||||
"net/aws",
|
"net/aws",
|
||||||
"net/mpegtslive",
|
|
||||||
"net/hlssink3",
|
"net/hlssink3",
|
||||||
"net/onvif",
|
"net/onvif",
|
||||||
"net/raptorq",
|
"net/raptorq",
|
||||||
"net/reqwest",
|
"net/reqwest",
|
||||||
"net/rtp",
|
"net/rtp",
|
||||||
"net/rtsp",
|
|
||||||
"net/webrtchttp",
|
"net/webrtchttp",
|
||||||
"net/webrtc",
|
"net/webrtc",
|
||||||
"net/webrtc/protocol",
|
"net/webrtc/protocol",
|
||||||
"net/webrtc/signalling",
|
"net/webrtc/signalling",
|
||||||
"net/ndi",
|
"net/ndi",
|
||||||
"net/quinn",
|
|
||||||
|
|
||||||
"text/ahead",
|
"text/ahead",
|
||||||
"text/json",
|
"text/json",
|
||||||
|
@ -120,40 +106,3 @@ panic = 'unwind'
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = 1
|
opt-level = 1
|
||||||
lto = "off"
|
|
||||||
|
|
||||||
[workspace.package]
|
|
||||||
version = "0.14.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", features = ["v4_6"]}
|
|
||||||
gdk-wayland = { package = "gdk4-wayland", git = "https://github.com/gtk-rs/gtk4-rs", branch = "master", features = ["v4_4"]}
|
|
||||||
gdk-x11 = { package = "gdk4-x11", git = "https://github.com/gtk-rs/gtk4-rs", branch = "master", features = ["v4_4"]}
|
|
||||||
gdk-win32 = { package = "gdk4-win32", git = "https://github.com/gtk-rs/gtk4-rs", branch = "master", features = ["v4_4"]}
|
|
||||||
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" }
|
|
||||||
|
|
|
@ -23,7 +23,6 @@ You will find the following plugins in this repository:
|
||||||
|
|
||||||
- `aws`: Various elements for Amazon AWS services using the [AWS SDK](https://awslabs.github.io/aws-sdk-rust/) library
|
- `aws`: Various elements for Amazon AWS services using the [AWS SDK](https://awslabs.github.io/aws-sdk-rust/) library
|
||||||
- `s3src`/`s3sink`: A source and sink element to talk to the Amazon S3 object storage system.
|
- `s3src`/`s3sink`: A source and sink element to talk to the Amazon S3 object storage system.
|
||||||
- `s3putobjectsink`: A sink element to talk to Amazon S3. Uses `PutObject` instead of multi-part upload like `s3sink`.
|
|
||||||
- `s3hlssink`: A sink element to store HLS streams on Amazon S3.
|
- `s3hlssink`: A sink element to store HLS streams on Amazon S3.
|
||||||
- `awstranscriber`: an element wrapping the AWS Transcriber service.
|
- `awstranscriber`: an element wrapping the AWS Transcriber service.
|
||||||
- `awstranscribeparse`: an element parsing the packets of the AWS Transcriber service.
|
- `awstranscribeparse`: an element parsing the packets of the AWS Transcriber service.
|
||||||
|
@ -34,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.
|
- `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.
|
- `raptorq`: Encoder/decoder element for RaptorQ RTP FEC mechanism.
|
||||||
|
|
||||||
- `reqwest`: An HTTP source element based on the [reqwest](https://github.com/seanmonstar/reqwest) library.
|
- `reqwest`: An HTTP source element based on the [reqwest](https://github.com/seanmonstar/reqwest) library.
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
[package]
|
[package]
|
||||||
name = "gst-plugin-audiofx"
|
name = "gst-plugin-audiofx"
|
||||||
version.workspace = true
|
version = "0.10.3"
|
||||||
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
|
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
|
||||||
repository.workspace = true
|
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
description = "GStreamer Rust Audio Effects Plugin"
|
description = "GStreamer Rust Audio Effects Plugin"
|
||||||
edition.workspace = true
|
edition = "2021"
|
||||||
rust-version.workspace = true
|
rust-version = "1.63"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gst = { 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 = { workspace = true, features = ["v1_20"] }
|
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20", features = ["v1_16"] }
|
||||||
gst-audio = { workspace = true, features = ["v1_20"] }
|
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20", features = ["v1_16"] }
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
byte-slice-cast = "1.0"
|
byte-slice-cast = "1.0"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
|
once_cell = "1.0"
|
||||||
ebur128 = "0.1"
|
ebur128 = "0.1"
|
||||||
hrtf = "0.8"
|
hrtf = "0.8"
|
||||||
nnnoiseless = { version = "0.5", default-features = false }
|
nnnoiseless = { version = "0.5", default-features = false }
|
||||||
smallvec = "1"
|
smallvec = "1"
|
||||||
atomic_refcell = "0.1"
|
atomic_refcell = "0.1"
|
||||||
rayon = "1.5"
|
rayon = "1.5"
|
||||||
once_cell.workspace = true
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "gstrsaudiofx"
|
name = "gstrsaudiofx"
|
||||||
|
@ -29,11 +29,11 @@ crate-type = ["cdylib", "rlib"]
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
gst-check = { workspace = true, features = ["v1_18"] }
|
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20", features = ["v1_18"] }
|
||||||
gst-app.workspace = true
|
gst-app = { package = "gstreamer-app", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
gst-plugin-version-helper.workspace = true
|
gst-plugin-version-helper = { path="../../version-helper", version = "0.7" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
static = []
|
static = []
|
||||||
|
@ -41,7 +41,7 @@ capi = []
|
||||||
doc = ["gst/v1_18"]
|
doc = ["gst/v1_18"]
|
||||||
|
|
||||||
[package.metadata.capi]
|
[package.metadata.capi]
|
||||||
min_version = "0.9.21"
|
min_version = "0.8.0"
|
||||||
|
|
||||||
[package.metadata.capi.header]
|
[package.metadata.capi.header]
|
||||||
enabled = false
|
enabled = false
|
||||||
|
@ -49,7 +49,6 @@ enabled = false
|
||||||
[package.metadata.capi.library]
|
[package.metadata.capi.library]
|
||||||
install_subdir = "gstreamer-1.0"
|
install_subdir = "gstreamer-1.0"
|
||||||
versioning = false
|
versioning = false
|
||||||
import_library = false
|
|
||||||
|
|
||||||
[package.metadata.capi.pkg_config]
|
[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"
|
requires_private = "gstreamer-1.0, gstreamer-base-1.0, gstreamer-audio-1.0, gobject-2.0, glib-2.0, gmodule-2.0"
|
||||||
|
|
|
@ -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.
|
|
1
audio/audiofx/LICENSE-MPL-2.0
Symbolic link
1
audio/audiofx/LICENSE-MPL-2.0
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../LICENSE-MPL-2.0
|
|
@ -36,7 +36,7 @@ fn run() -> Result<(), Error> {
|
||||||
let uri = &args[1];
|
let uri = &args[1];
|
||||||
let hrir = &args[2];
|
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 !
|
"uridecodebin uri={uri} ! audioconvert ! audio/x-raw,channels=1 !
|
||||||
hrtfrender hrir-file={hrir} name=hrtf ! audioresample ! autoaudiosink"
|
hrtfrender hrir-file={hrir} name=hrtf ! audioresample ! autoaudiosink"
|
||||||
))?
|
))?
|
||||||
|
|
|
@ -11,8 +11,8 @@ use gst::prelude::*;
|
||||||
use gst::subclass::prelude::*;
|
use gst::subclass::prelude::*;
|
||||||
use gst_audio::subclass::prelude::*;
|
use gst_audio::subclass::prelude::*;
|
||||||
|
|
||||||
use std::cmp;
|
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
use std::{cmp, u64};
|
||||||
|
|
||||||
use byte_slice_cast::*;
|
use byte_slice_cast::*;
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
gst::Element::register(
|
gst::Element::register(
|
||||||
Some(plugin),
|
Some(plugin),
|
||||||
"rsaudioecho",
|
"rsaudioecho",
|
||||||
gst::Rank::NONE,
|
gst::Rank::None,
|
||||||
AudioEcho::static_type(),
|
AudioEcho::static_type(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ use gst::subclass::prelude::*;
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
use std::u64;
|
||||||
|
|
||||||
use byte_slice_cast::*;
|
use byte_slice_cast::*;
|
||||||
|
|
||||||
|
@ -263,7 +264,7 @@ impl State {
|
||||||
|
|
||||||
// Drains everything
|
// Drains everything
|
||||||
fn drain(&mut self, imp: &AudioLoudNorm) -> Result<gst::Buffer, gst::FlowError> {
|
fn drain(&mut self, imp: &AudioLoudNorm) -> Result<gst::Buffer, gst::FlowError> {
|
||||||
gst::debug!(CAT, imp = imp, "Draining");
|
gst::debug!(CAT, imp: imp, "Draining");
|
||||||
|
|
||||||
let (pts, distance) = self.adapter.prev_pts();
|
let (pts, distance) = self.adapter.prev_pts();
|
||||||
let distance_samples = distance / self.info.bpf() as u64;
|
let distance_samples = distance / self.info.bpf() as u64;
|
||||||
|
@ -298,7 +299,7 @@ impl State {
|
||||||
self.frame_type = FrameType::Final;
|
self.frame_type = FrameType::Final;
|
||||||
} else if src.is_empty() {
|
} else if src.is_empty() {
|
||||||
// Nothing to drain at all
|
// Nothing to drain at all
|
||||||
gst::debug!(CAT, imp = imp, "No data to drain");
|
gst::debug!(CAT, imp: imp, "No data to drain");
|
||||||
return Err(gst::FlowError::Eos);
|
return Err(gst::FlowError::Eos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -341,15 +342,15 @@ impl State {
|
||||||
|
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = imp,
|
imp: imp,
|
||||||
"Calculated global loudness for first frame {} with peak {}",
|
"Calculated global loudness for first frame {} with peak {}",
|
||||||
global,
|
global,
|
||||||
true_peak
|
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.);
|
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;
|
let offset_tp = true_peak * offset;
|
||||||
|
|
||||||
// If the new peak would be more quiet than targeted one, take it. Otherwise only go as
|
// If the new peak would be more quiet than targeted one, take it. Otherwise only go as
|
||||||
|
@ -395,7 +396,7 @@ impl State {
|
||||||
self.prev_delta = self.delta[self.index];
|
self.prev_delta = self.delta[self.index];
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = imp,
|
imp: imp,
|
||||||
"Initializing for first frame with gain adjustment of {}",
|
"Initializing for first frame with gain adjustment of {}",
|
||||||
self.prev_delta
|
self.prev_delta
|
||||||
);
|
);
|
||||||
|
@ -457,7 +458,7 @@ impl State {
|
||||||
|
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = imp,
|
imp: imp,
|
||||||
"Applying gain adjustment {}-{}",
|
"Applying gain adjustment {}-{}",
|
||||||
gain,
|
gain,
|
||||||
gain_next
|
gain_next
|
||||||
|
@ -531,7 +532,7 @@ impl State {
|
||||||
|
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = imp,
|
imp: imp,
|
||||||
"Calculated global loudness {}, short term loudness {} and relative threshold {}",
|
"Calculated global loudness {}, short term loudness {} and relative threshold {}",
|
||||||
global,
|
global,
|
||||||
shortterm,
|
shortterm,
|
||||||
|
@ -554,7 +555,7 @@ impl State {
|
||||||
self.above_threshold = true;
|
self.above_threshold = true;
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = imp,
|
imp: imp,
|
||||||
"Above threshold now ({} >= {}, {} > -70)",
|
"Above threshold now ({} >= {}, {} > -70)",
|
||||||
shortterm_out,
|
shortterm_out,
|
||||||
self.target_i,
|
self.target_i,
|
||||||
|
@ -582,7 +583,7 @@ impl State {
|
||||||
self.prev_delta = self.delta[self.index];
|
self.prev_delta = self.delta[self.index];
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = imp,
|
imp: imp,
|
||||||
"Calculated new gain adjustment {}",
|
"Calculated new gain adjustment {}",
|
||||||
self.prev_delta
|
self.prev_delta
|
||||||
);
|
);
|
||||||
|
@ -704,7 +705,7 @@ impl State {
|
||||||
self.process_fill_final_frame(imp, num_samples, FRAME_SIZE);
|
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.
|
// data from the buf to limiter_buf until we have output everything.
|
||||||
//
|
//
|
||||||
// At this point we have to output 3s - (FRAME_SIZE - num_samples)
|
// At this point we have to output 3s - (FRAME_SIZE - num_samples)
|
||||||
|
@ -753,7 +754,7 @@ impl State {
|
||||||
// amount of samples the last frame is short to reach the correct read position.
|
// amount of samples the last frame is short to reach the correct read position.
|
||||||
if next_frame_size < FRAME_SIZE {
|
if next_frame_size < FRAME_SIZE {
|
||||||
self.limiter_buf_index += FRAME_SIZE - next_frame_size;
|
self.limiter_buf_index += FRAME_SIZE - next_frame_size;
|
||||||
if self.limiter_buf_index >= self.limiter_buf.len() {
|
if self.limiter_buf_index > self.limiter_buf.len() {
|
||||||
self.limiter_buf_index -= self.limiter_buf.len();
|
self.limiter_buf_index -= self.limiter_buf.len();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -776,13 +777,13 @@ impl State {
|
||||||
|
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = imp,
|
imp: imp,
|
||||||
"Applying linear gain adjustment of {}",
|
"Applying linear gain adjustment of {}",
|
||||||
self.offset
|
self.offset
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut outbuf =
|
let mut outbuf = gst::Buffer::with_size(src.len() * mem::size_of::<f64>())
|
||||||
gst::Buffer::with_size(mem::size_of_val(src)).map_err(|_| gst::FlowError::Error)?;
|
.map_err(|_| gst::FlowError::Error)?;
|
||||||
{
|
{
|
||||||
let outbuf = outbuf.get_mut().unwrap();
|
let outbuf = outbuf.get_mut().unwrap();
|
||||||
let mut dst = outbuf.map_writable().map_err(|_| gst::FlowError::Error)?;
|
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;
|
smp_cnt += LIMITER_LOOKAHEAD + peak_delta - LIMITER_ATTACK_WINDOW;
|
||||||
|
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = imp,
|
imp: imp,
|
||||||
"Found peak {} at sample {}, going to attack state at sample {} (gain reduction {}-{})",
|
"Found peak {} at sample {}, going to attack state at sample {} (gain reduction {}-{})",
|
||||||
peak_value,
|
peak_value,
|
||||||
smp_cnt + LIMITER_ATTACK_WINDOW,
|
smp_cnt + LIMITER_ATTACK_WINDOW,
|
||||||
smp_cnt,
|
smp_cnt,
|
||||||
self.gain_reduction[0],
|
self.gain_reduction[0],
|
||||||
self.gain_reduction[1]
|
self.gain_reduction[1]
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// Process all samples, no peak found
|
// Process all samples, no peak found
|
||||||
smp_cnt = nb_samples;
|
smp_cnt = nb_samples;
|
||||||
|
@ -991,15 +992,15 @@ impl State {
|
||||||
self.sustain_cnt = None;
|
self.sustain_cnt = None;
|
||||||
|
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = imp,
|
imp: imp,
|
||||||
"Found new peak {} at sample {}, restarting attack state at sample {} (gain reduction {}-{})",
|
"Found new peak {} at sample {}, restarting attack state at sample {} (gain reduction {}-{})",
|
||||||
peak_value,
|
peak_value,
|
||||||
smp_cnt + LIMITER_ATTACK_WINDOW,
|
smp_cnt + LIMITER_ATTACK_WINDOW,
|
||||||
smp_cnt,
|
smp_cnt,
|
||||||
self.gain_reduction[0],
|
self.gain_reduction[0],
|
||||||
self.gain_reduction[1],
|
self.gain_reduction[1],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// If the slope is lower we can't simply reduce the slope as we would
|
// 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.
|
// 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);
|
self.sustain_cnt = Some(self.env_cnt);
|
||||||
|
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = imp,
|
imp: imp,
|
||||||
"Found new peak {} at sample {}, adjusting attack state at sample {} (gain reduction {}-{})",
|
"Found new peak {} at sample {}, adjusting attack state at sample {} (gain reduction {}-{})",
|
||||||
peak_value,
|
peak_value,
|
||||||
smp_cnt + LIMITER_ATTACK_WINDOW,
|
smp_cnt + LIMITER_ATTACK_WINDOW,
|
||||||
smp_cnt,
|
smp_cnt,
|
||||||
self.gain_reduction[0],
|
self.gain_reduction[0],
|
||||||
self.gain_reduction[1],
|
self.gain_reduction[1],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return smp_cnt;
|
return smp_cnt;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1056,7 +1057,7 @@ impl State {
|
||||||
// to ensure that we at least sustain it for that long afterwards.
|
// to ensure that we at least sustain it for that long afterwards.
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = imp,
|
imp: imp,
|
||||||
"Found new low peak {} at sample {} in attack state at sample {}",
|
"Found new low peak {} at sample {} in attack state at sample {}",
|
||||||
peak_value,
|
peak_value,
|
||||||
smp_cnt + LIMITER_ATTACK_WINDOW,
|
smp_cnt + LIMITER_ATTACK_WINDOW,
|
||||||
|
@ -1072,7 +1073,7 @@ impl State {
|
||||||
// If we reached the target gain reduction, go into sustain state.
|
// If we reached the target gain reduction, go into sustain state.
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = imp,
|
imp: imp,
|
||||||
"Going to sustain state at sample {} (gain reduction {})",
|
"Going to sustain state at sample {} (gain reduction {})",
|
||||||
smp_cnt,
|
smp_cnt,
|
||||||
self.gain_reduction[1]
|
self.gain_reduction[1]
|
||||||
|
@ -1150,25 +1151,25 @@ impl State {
|
||||||
self.gain_reduction[1] = gain_reduction;
|
self.gain_reduction[1] = gain_reduction;
|
||||||
|
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = imp,
|
imp: imp,
|
||||||
"Found new peak {} at sample {}, going back to attack state at sample {} (gain reduction {}-{})",
|
"Found new peak {} at sample {}, going back to attack state at sample {} (gain reduction {}-{})",
|
||||||
peak_value,
|
peak_value,
|
||||||
smp_cnt + LIMITER_ATTACK_WINDOW,
|
smp_cnt + LIMITER_ATTACK_WINDOW,
|
||||||
smp_cnt,
|
smp_cnt,
|
||||||
self.gain_reduction[0],
|
self.gain_reduction[0],
|
||||||
self.gain_reduction[1],
|
self.gain_reduction[1],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = imp,
|
imp: imp,
|
||||||
"Found new peak {} at sample {}, going sustain further at sample {} (gain reduction {})",
|
"Found new peak {} at sample {}, going sustain further at sample {} (gain reduction {})",
|
||||||
peak_value,
|
peak_value,
|
||||||
smp_cnt + LIMITER_ATTACK_WINDOW,
|
smp_cnt + LIMITER_ATTACK_WINDOW,
|
||||||
smp_cnt,
|
smp_cnt,
|
||||||
self.gain_reduction[1],
|
self.gain_reduction[1],
|
||||||
);
|
);
|
||||||
// We need to sustain until the peak at least
|
// We need to sustain until the peak at least
|
||||||
self.sustain_cnt = Some(LIMITER_LOOKAHEAD);
|
self.sustain_cnt = Some(LIMITER_LOOKAHEAD);
|
||||||
}
|
}
|
||||||
|
@ -1189,7 +1190,7 @@ impl State {
|
||||||
|
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = imp,
|
imp: imp,
|
||||||
"Going to release state for sample {} at sample {} (gain reduction {}-1.0)",
|
"Going to release state for sample {} at sample {} (gain reduction {}-1.0)",
|
||||||
smp_cnt + LIMITER_RELEASE_WINDOW,
|
smp_cnt + LIMITER_RELEASE_WINDOW,
|
||||||
smp_cnt,
|
smp_cnt,
|
||||||
|
@ -1258,26 +1259,26 @@ impl State {
|
||||||
self.gain_reduction[1] = gain_reduction;
|
self.gain_reduction[1] = gain_reduction;
|
||||||
|
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = imp,
|
imp: imp,
|
||||||
"Found new peak {} at sample {}, going back to attack state at sample {} (gain reduction {}-{})",
|
"Found new peak {} at sample {}, going back to attack state at sample {} (gain reduction {}-{})",
|
||||||
peak_value,
|
peak_value,
|
||||||
smp_cnt + LIMITER_ATTACK_WINDOW,
|
smp_cnt + LIMITER_ATTACK_WINDOW,
|
||||||
smp_cnt,
|
smp_cnt,
|
||||||
self.gain_reduction[0],
|
self.gain_reduction[0],
|
||||||
self.gain_reduction[1],
|
self.gain_reduction[1],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
self.gain_reduction[1] = current_gain_reduction;
|
self.gain_reduction[1] = current_gain_reduction;
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = imp,
|
imp: imp,
|
||||||
"Going from release to sustain state at sample {} because of low peak {} at sample {} (gain reduction {})",
|
"Going from release to sustain state at sample {} because of low peak {} at sample {} (gain reduction {})",
|
||||||
smp_cnt,
|
smp_cnt,
|
||||||
peak_value,
|
peak_value,
|
||||||
smp_cnt + LIMITER_ATTACK_WINDOW,
|
smp_cnt + LIMITER_ATTACK_WINDOW,
|
||||||
self.gain_reduction[1]
|
self.gain_reduction[1]
|
||||||
);
|
);
|
||||||
self.limiter_state = LimiterState::Sustain;
|
self.limiter_state = LimiterState::Sustain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1312,7 +1313,7 @@ impl State {
|
||||||
self.limiter_state = LimiterState::Out;
|
self.limiter_state = LimiterState::Out;
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = imp,
|
imp: imp,
|
||||||
"Leaving release state and going to out state at sample {}",
|
"Leaving release state and going to out state at sample {}",
|
||||||
smp_cnt,
|
smp_cnt,
|
||||||
);
|
);
|
||||||
|
@ -1350,7 +1351,7 @@ impl State {
|
||||||
self.gain_reduction[1] = self.target_tp / max;
|
self.gain_reduction[1] = self.target_tp / max;
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = imp,
|
imp: imp,
|
||||||
"Reducing gain for start of first frame by {} ({} > {}) and going to sustain state",
|
"Reducing gain for start of first frame by {} ({} > {}) and going to sustain state",
|
||||||
self.gain_reduction[1],
|
self.gain_reduction[1],
|
||||||
max,
|
max,
|
||||||
|
@ -1366,7 +1367,7 @@ impl State {
|
||||||
let channels = self.info.channels() as usize;
|
let channels = self.info.channels() as usize;
|
||||||
let nb_samples = dst.len() / channels;
|
let nb_samples = dst.len() / channels;
|
||||||
|
|
||||||
gst::debug!(CAT, imp = imp, "Running limiter for {} samples", nb_samples);
|
gst::debug!(CAT, imp: imp, "Running limiter for {} samples", nb_samples);
|
||||||
|
|
||||||
// For the first frame we can't adjust the gain before it smoothly anymore so instead
|
// For the first frame we can't adjust the gain before it smoothly anymore so instead
|
||||||
// apply the gain reduction immediately if we get above the threshold and move to sustain
|
// apply the gain reduction immediately if we get above the threshold and move to sustain
|
||||||
|
@ -1535,12 +1536,12 @@ impl AudioLoudNorm {
|
||||||
_pad: &gst::Pad,
|
_pad: &gst::Pad,
|
||||||
buffer: gst::Buffer,
|
buffer: gst::Buffer,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
gst::log!(CAT, imp = self, "Handling buffer {:?}", buffer);
|
gst::log!(CAT, imp: self, "Handling buffer {:?}", buffer);
|
||||||
|
|
||||||
let mut state_guard = self.state.borrow_mut();
|
let mut state_guard = self.state.borrow_mut();
|
||||||
let state = match *state_guard {
|
let state = match *state_guard {
|
||||||
None => {
|
None => {
|
||||||
gst::error!(CAT, imp = self, "Not negotiated yet");
|
gst::error!(CAT, imp: self, "Not negotiated yet");
|
||||||
return Err(gst::FlowError::NotNegotiated);
|
return Err(gst::FlowError::NotNegotiated);
|
||||||
}
|
}
|
||||||
Some(ref mut state) => state,
|
Some(ref mut state) => state,
|
||||||
|
@ -1548,7 +1549,7 @@ impl AudioLoudNorm {
|
||||||
|
|
||||||
let mut outbufs = vec![];
|
let mut outbufs = vec![];
|
||||||
if buffer.flags().contains(gst::BufferFlags::DISCONT) {
|
if buffer.flags().contains(gst::BufferFlags::DISCONT) {
|
||||||
gst::debug!(CAT, imp = self, "Draining on discontinuity");
|
gst::debug!(CAT, imp: self, "Draining on discontinuity");
|
||||||
match state.drain(self) {
|
match state.drain(self) {
|
||||||
Ok(outbuf) => {
|
Ok(outbuf) => {
|
||||||
outbufs.push(outbuf);
|
outbufs.push(outbuf);
|
||||||
|
@ -1566,7 +1567,7 @@ impl AudioLoudNorm {
|
||||||
drop(state_guard);
|
drop(state_guard);
|
||||||
|
|
||||||
for buffer in outbufs {
|
for buffer in outbufs {
|
||||||
gst::log!(CAT, imp = self, "Outputting buffer {:?}", buffer);
|
gst::log!(CAT, imp: self, "Outputting buffer {:?}", buffer);
|
||||||
self.srcpad.push(buffer)?;
|
self.srcpad.push(buffer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1576,17 +1577,17 @@ impl AudioLoudNorm {
|
||||||
fn sink_event(&self, pad: &gst::Pad, event: gst::Event) -> bool {
|
fn sink_event(&self, pad: &gst::Pad, event: gst::Event) -> bool {
|
||||||
use gst::EventView;
|
use gst::EventView;
|
||||||
|
|
||||||
gst::log!(CAT, obj = pad, "Handling event {:?}", event);
|
gst::log!(CAT, obj: pad, "Handling event {:?}", event);
|
||||||
|
|
||||||
match event.view() {
|
match event.view() {
|
||||||
EventView::Caps(c) => {
|
EventView::Caps(c) => {
|
||||||
let caps = c.caps();
|
let caps = c.caps();
|
||||||
gst::info!(CAT, obj = pad, "Got caps {:?}", caps);
|
gst::info!(CAT, obj: pad, "Got caps {:?}", caps);
|
||||||
|
|
||||||
let info = match gst_audio::AudioInfo::from_caps(caps) {
|
let info = match gst_audio::AudioInfo::from_caps(caps) {
|
||||||
Ok(info) => info,
|
Ok(info) => info,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
gst::error!(CAT, obj = pad, "Failed to parse caps");
|
gst::error!(CAT, obj: pad, "Failed to parse caps");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1604,9 +1605,9 @@ impl AudioLoudNorm {
|
||||||
drop(state);
|
drop(state);
|
||||||
|
|
||||||
if let Some(outbuf) = outbuf {
|
if let Some(outbuf) = outbuf {
|
||||||
gst::log!(CAT, imp = self, "Outputting buffer {:?}", outbuf);
|
gst::log!(CAT, imp: self, "Outputting buffer {:?}", outbuf);
|
||||||
if let Err(err) = self.srcpad.push(outbuf) {
|
if let Err(err) = self.srcpad.push(outbuf) {
|
||||||
gst::error!(CAT, imp = self, "Failed to push drained data: {}", err);
|
gst::error!(CAT, imp: self, "Failed to push drained data: {}", err);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1626,11 +1627,11 @@ impl AudioLoudNorm {
|
||||||
drop(state);
|
drop(state);
|
||||||
|
|
||||||
if let Some(outbuf) = outbuf {
|
if let Some(outbuf) = outbuf {
|
||||||
gst::log!(CAT, imp = self, "Outputting buffer {:?}", outbuf);
|
gst::log!(CAT, imp: self, "Outputting buffer {:?}", outbuf);
|
||||||
if let Err(err) = self.srcpad.push(outbuf) {
|
if let Err(err) = self.srcpad.push(outbuf) {
|
||||||
gst::error!(
|
gst::error!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = self,
|
imp: self,
|
||||||
"Failed to push drained data on EOS: {}",
|
"Failed to push drained data on EOS: {}",
|
||||||
err
|
err
|
||||||
);
|
);
|
||||||
|
@ -1660,7 +1661,7 @@ impl AudioLoudNorm {
|
||||||
fn src_query(&self, pad: &gst::Pad, query: &mut gst::QueryRef) -> bool {
|
fn src_query(&self, pad: &gst::Pad, query: &mut gst::QueryRef) -> bool {
|
||||||
use gst::QueryViewMut;
|
use gst::QueryViewMut;
|
||||||
|
|
||||||
gst::log!(CAT, obj = pad, "Handling query {:?}", query);
|
gst::log!(CAT, obj: pad, "Handling query {:?}", query);
|
||||||
match query.view_mut() {
|
match query.view_mut() {
|
||||||
QueryViewMut::Latency(q) => {
|
QueryViewMut::Latency(q) => {
|
||||||
let mut peer_query = gst::query::Latency::new();
|
let mut peer_query = gst::query::Latency::new();
|
||||||
|
@ -1689,7 +1690,7 @@ impl ObjectSubclass for AudioLoudNorm {
|
||||||
|
|
||||||
fn with_class(klass: &Self::Class) -> Self {
|
fn with_class(klass: &Self::Class) -> Self {
|
||||||
let templ = klass.pad_template("sink").unwrap();
|
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| {
|
.chain_function(|pad, parent, buffer| {
|
||||||
Self::catch_panic_pad_function(
|
Self::catch_panic_pad_function(
|
||||||
parent,
|
parent,
|
||||||
|
@ -1704,7 +1705,7 @@ impl ObjectSubclass for AudioLoudNorm {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let templ = klass.pad_template("src").unwrap();
|
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| {
|
.query_function(|pad, parent, query| {
|
||||||
Self::catch_panic_pad_function(parent, || false, |this| this.src_query(pad, query))
|
Self::catch_panic_pad_function(parent, || false, |this| this.src_query(pad, query))
|
||||||
})
|
})
|
||||||
|
|
|
@ -19,7 +19,7 @@ pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
gst::Element::register(
|
gst::Element::register(
|
||||||
Some(plugin),
|
Some(plugin),
|
||||||
"audioloudnorm",
|
"audioloudnorm",
|
||||||
gst::Rank::NONE,
|
gst::Rank::None,
|
||||||
AudioLoudNorm::static_type(),
|
AudioLoudNorm::static_type(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,7 +113,7 @@ impl AudioRNNoise {
|
||||||
|
|
||||||
let settings = *self.settings.lock().unwrap();
|
let settings = *self.settings.lock().unwrap();
|
||||||
let mut buffer = gst::Buffer::with_size(available).map_err(|e| {
|
let mut buffer = gst::Buffer::with_size(available).map_err(|e| {
|
||||||
gst::error!(CAT, imp = self, "Failed to allocate buffer at EOS {:?}", e);
|
gst::error!(CAT, imp: self, "Failed to allocate buffer at EOS {:?}", e);
|
||||||
gst::FlowError::Flushing
|
gst::FlowError::Flushing
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -129,13 +129,10 @@ impl AudioRNNoise {
|
||||||
buffer.set_duration(duration);
|
buffer.set_duration(duration);
|
||||||
buffer.set_pts(pts);
|
buffer.set_pts(pts);
|
||||||
|
|
||||||
let (level, has_voice) = {
|
let mut out_map = buffer.map_writable().map_err(|_| gst::FlowError::Error)?;
|
||||||
let mut out_map = buffer.map_writable().map_err(|_| gst::FlowError::Error)?;
|
let out_data = out_map.as_mut_slice_of::<f32>().unwrap();
|
||||||
let out_data = out_map.as_mut_slice_of::<f32>().unwrap();
|
|
||||||
self.process(state, &settings, in_data, out_data)
|
|
||||||
};
|
|
||||||
|
|
||||||
gst_audio::AudioLevelMeta::add(buffer, level, has_voice);
|
self.process(state, &settings, in_data, out_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.obj().src_pad().push(buffer)
|
self.obj().src_pad().push(buffer)
|
||||||
|
@ -163,13 +160,10 @@ impl AudioRNNoise {
|
||||||
buffer.set_duration(duration);
|
buffer.set_duration(duration);
|
||||||
buffer.set_pts(pts);
|
buffer.set_pts(pts);
|
||||||
|
|
||||||
let (level, has_voice) = {
|
let mut out_map = buffer.map_writable().map_err(|_| gst::FlowError::Error)?;
|
||||||
let mut out_map = buffer.map_writable().map_err(|_| gst::FlowError::Error)?;
|
let out_data = out_map.as_mut_slice_of::<f32>().unwrap();
|
||||||
let out_data = out_map.as_mut_slice_of::<f32>().unwrap();
|
|
||||||
self.process(state, &settings, in_data, out_data)
|
|
||||||
};
|
|
||||||
|
|
||||||
gst_audio::AudioLevelMeta::add(buffer, level, has_voice);
|
self.process(state, &settings, in_data, out_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(GenerateOutputSuccess::Buffer(buffer))
|
Ok(GenerateOutputSuccess::Buffer(buffer))
|
||||||
|
@ -181,10 +175,9 @@ impl AudioRNNoise {
|
||||||
settings: &Settings,
|
settings: &Settings,
|
||||||
input_plane: &[f32],
|
input_plane: &[f32],
|
||||||
output_plane: &mut [f32],
|
output_plane: &mut [f32],
|
||||||
) -> (u8, bool) {
|
) {
|
||||||
let channels = state.in_info.channels() as usize;
|
let channels = state.in_info.channels() as usize;
|
||||||
let size = FRAME_SIZE * channels;
|
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 (out_frame, in_frame) in output_plane.chunks_mut(size).zip(input_plane.chunks(size)) {
|
||||||
for (index, item) in in_frame.iter().enumerate() {
|
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 {
|
if vad < settings.vad_threshold {
|
||||||
out_frame.fill(0.0);
|
out_frame.fill(0.0);
|
||||||
} else {
|
} 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() {
|
for (index, item) in out_frame.iter_mut().enumerate() {
|
||||||
let channel_index = index % channels;
|
let channel_index = index % channels;
|
||||||
let channel_denoiser = &state.denoisers[channel_index];
|
let channel_denoiser = &state.denoisers[channel_index];
|
||||||
|
@ -231,20 +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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,7 +321,7 @@ impl BaseTransformImpl for AudioRNNoise {
|
||||||
use gst::EventView;
|
use gst::EventView;
|
||||||
|
|
||||||
if let EventView::Eos(_) = event.view() {
|
if let EventView::Eos(_) = event.view() {
|
||||||
gst::debug!(CAT, imp = self, "Handling EOS");
|
gst::debug!(CAT, imp: self, "Handling EOS");
|
||||||
if self.drain().is_err() {
|
if self.drain().is_err() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -362,7 +337,7 @@ impl BaseTransformImpl for AudioRNNoise {
|
||||||
let (live, mut min, mut max) = upstream_query.result();
|
let (live, mut min, mut max) = upstream_query.result();
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = self,
|
imp: self,
|
||||||
"Peer latency: live {} min {} max {}",
|
"Peer latency: live {} min {} max {}",
|
||||||
live,
|
live,
|
||||||
min,
|
min,
|
||||||
|
@ -407,7 +382,7 @@ impl AudioFilterImpl for AudioRNNoise {
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
gst::debug!(CAT, imp = self, "Set caps to {:?}", info);
|
gst::debug!(CAT, imp: self, "Set caps to {:?}", info);
|
||||||
|
|
||||||
let mut denoisers = vec![];
|
let mut denoisers = vec![];
|
||||||
for _i in 0..info.channels() {
|
for _i in 0..info.channels() {
|
||||||
|
|
|
@ -20,7 +20,7 @@ pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
gst::Element::register(
|
gst::Element::register(
|
||||||
Some(plugin),
|
Some(plugin),
|
||||||
"audiornnoise",
|
"audiornnoise",
|
||||||
gst::Rank::NONE,
|
gst::Rank::None,
|
||||||
AudioRNNoise::static_type(),
|
AudioRNNoise::static_type(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ use gst::subclass::prelude::*;
|
||||||
use gst_audio::subclass::prelude::*;
|
use gst_audio::subclass::prelude::*;
|
||||||
use gst_base::prelude::*;
|
use gst_base::prelude::*;
|
||||||
|
|
||||||
|
use std::i32;
|
||||||
use std::sync::atomic;
|
use std::sync::atomic;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
@ -129,7 +130,7 @@ impl ObjectImpl for EbuR128Level {
|
||||||
let this = args[0].get::<super::EbuR128Level>().unwrap();
|
let this = args[0].get::<super::EbuR128Level>().unwrap();
|
||||||
let imp = this.imp();
|
let imp = this.imp();
|
||||||
|
|
||||||
gst::info!(CAT, obj = this, "Resetting measurements",);
|
gst::info!(CAT, obj: this, "Resetting measurements",);
|
||||||
imp.reset.store(true, atomic::Ordering::SeqCst);
|
imp.reset.store(true, atomic::Ordering::SeqCst);
|
||||||
|
|
||||||
None
|
None
|
||||||
|
@ -175,7 +176,7 @@ impl ObjectImpl for EbuR128Level {
|
||||||
let mode = value.get().expect("type checked upstream");
|
let mode = value.get().expect("type checked upstream");
|
||||||
gst::info!(
|
gst::info!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = self,
|
imp: self,
|
||||||
"Changing mode from {:?} to {:?}",
|
"Changing mode from {:?} to {:?}",
|
||||||
settings.mode,
|
settings.mode,
|
||||||
mode
|
mode
|
||||||
|
@ -186,7 +187,7 @@ impl ObjectImpl for EbuR128Level {
|
||||||
let post_messages = value.get().expect("type checked upstream");
|
let post_messages = value.get().expect("type checked upstream");
|
||||||
gst::info!(
|
gst::info!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = self,
|
imp: self,
|
||||||
"Changing post-messages from {} to {}",
|
"Changing post-messages from {} to {}",
|
||||||
settings.post_messages,
|
settings.post_messages,
|
||||||
post_messages
|
post_messages
|
||||||
|
@ -197,7 +198,7 @@ impl ObjectImpl for EbuR128Level {
|
||||||
let interval = value.get::<u64>().unwrap().nseconds();
|
let interval = value.get::<u64>().unwrap().nseconds();
|
||||||
gst::info!(
|
gst::info!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = self,
|
imp: self,
|
||||||
"Changing interval from {} to {}",
|
"Changing interval from {} to {}",
|
||||||
settings.interval,
|
settings.interval,
|
||||||
interval,
|
interval,
|
||||||
|
@ -286,7 +287,7 @@ impl BaseTransformImpl for EbuR128Level {
|
||||||
// Drop state
|
// Drop state
|
||||||
let _ = self.state.borrow_mut().take();
|
let _ = self.state.borrow_mut().take();
|
||||||
|
|
||||||
gst::info!(CAT, imp = self, "Stopped");
|
gst::info!(CAT, imp: self, "Stopped");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -377,7 +378,7 @@ impl BaseTransformImpl for EbuR128Level {
|
||||||
Ok(loudness) => s.set("momentary-loudness", loudness),
|
Ok(loudness) => s.set("momentary-loudness", loudness),
|
||||||
Err(err) => gst::error!(
|
Err(err) => gst::error!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = self,
|
imp: self,
|
||||||
"Failed to get momentary loudness: {}",
|
"Failed to get momentary loudness: {}",
|
||||||
err
|
err
|
||||||
),
|
),
|
||||||
|
@ -389,7 +390,7 @@ impl BaseTransformImpl for EbuR128Level {
|
||||||
Ok(loudness) => s.set("shortterm-loudness", loudness),
|
Ok(loudness) => s.set("shortterm-loudness", loudness),
|
||||||
Err(err) => gst::error!(
|
Err(err) => gst::error!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = self,
|
imp: self,
|
||||||
"Failed to get shortterm loudness: {}",
|
"Failed to get shortterm loudness: {}",
|
||||||
err
|
err
|
||||||
),
|
),
|
||||||
|
@ -401,7 +402,7 @@ impl BaseTransformImpl for EbuR128Level {
|
||||||
Ok(loudness) => s.set("global-loudness", loudness),
|
Ok(loudness) => s.set("global-loudness", loudness),
|
||||||
Err(err) => gst::error!(
|
Err(err) => gst::error!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = self,
|
imp: self,
|
||||||
"Failed to get global loudness: {}",
|
"Failed to get global loudness: {}",
|
||||||
err
|
err
|
||||||
),
|
),
|
||||||
|
@ -411,7 +412,7 @@ impl BaseTransformImpl for EbuR128Level {
|
||||||
Ok(threshold) => s.set("relative-threshold", threshold),
|
Ok(threshold) => s.set("relative-threshold", threshold),
|
||||||
Err(err) => gst::error!(
|
Err(err) => gst::error!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = self,
|
imp: self,
|
||||||
"Failed to get relative threshold: {}",
|
"Failed to get relative threshold: {}",
|
||||||
err
|
err
|
||||||
),
|
),
|
||||||
|
@ -422,12 +423,7 @@ impl BaseTransformImpl for EbuR128Level {
|
||||||
match state.ebur128.loudness_range() {
|
match state.ebur128.loudness_range() {
|
||||||
Ok(range) => s.set("loudness-range", range),
|
Ok(range) => s.set("loudness-range", range),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
gst::error!(
|
gst::error!(CAT, imp: self, "Failed to get loudness range: {}", err)
|
||||||
CAT,
|
|
||||||
imp = self,
|
|
||||||
"Failed to get loudness range: {}",
|
|
||||||
err
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -440,7 +436,7 @@ impl BaseTransformImpl for EbuR128Level {
|
||||||
match peaks {
|
match peaks {
|
||||||
Ok(peaks) => s.set("sample-peak", peaks),
|
Ok(peaks) => s.set("sample-peak", peaks),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
gst::error!(CAT, imp = self, "Failed to get sample peaks: {}", err)
|
gst::error!(CAT, imp: self, "Failed to get sample peaks: {}", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -453,12 +449,12 @@ impl BaseTransformImpl for EbuR128Level {
|
||||||
match peaks {
|
match peaks {
|
||||||
Ok(peaks) => s.set("true-peak", peaks),
|
Ok(peaks) => s.set("true-peak", peaks),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
gst::error!(CAT, imp = self, "Failed to get true peaks: {}", err)
|
gst::error!(CAT, imp: self, "Failed to get true peaks: {}", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gst::debug!(CAT, imp = self, "Posting message {}", s);
|
gst::debug!(CAT, imp: self, "Posting message {}", s);
|
||||||
|
|
||||||
let msg = gst::message::Element::builder(s).src(&*self.obj()).build();
|
let msg = gst::message::Element::builder(s).src(&*self.obj()).build();
|
||||||
|
|
||||||
|
@ -509,7 +505,7 @@ impl AudioFilterImpl for EbuR128Level {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup(&self, info: &gst_audio::AudioInfo) -> Result<(), gst::LoggableError> {
|
fn setup(&self, info: &gst_audio::AudioInfo) -> Result<(), gst::LoggableError> {
|
||||||
gst::debug!(CAT, imp = self, "Configured for caps {:?}", info);
|
gst::debug!(CAT, imp: self, "Configured for caps {:?}", info);
|
||||||
|
|
||||||
let settings = *self.settings.lock().unwrap();
|
let settings = *self.settings.lock().unwrap();
|
||||||
|
|
||||||
|
@ -572,7 +568,7 @@ impl AudioFilterImpl for EbuR128Level {
|
||||||
val => {
|
val => {
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = self,
|
imp: self,
|
||||||
"Unknown channel position {:?}, ignoring channel",
|
"Unknown channel position {:?}, ignoring channel",
|
||||||
val
|
val
|
||||||
);
|
);
|
||||||
|
@ -750,12 +746,12 @@ fn interleaved_channel_data_into_slice<'a, T: FromByteSlice>(
|
||||||
) -> Result<&'a [T], gst::FlowError> {
|
) -> Result<&'a [T], gst::FlowError> {
|
||||||
buf.plane_data(0)
|
buf.plane_data(0)
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
gst::error!(CAT, imp = imp, "Failed to get audio data: {}", err);
|
gst::error!(CAT, imp: imp, "Failed to get audio data: {}", err);
|
||||||
gst::FlowError::Error
|
gst::FlowError::Error
|
||||||
})?
|
})?
|
||||||
.as_slice_of::<T>()
|
.as_slice_of::<T>()
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
gst::error!(CAT, imp = imp, "Failed to handle audio data: {}", err);
|
gst::error!(CAT, imp: imp, "Failed to handle audio data: {}", err);
|
||||||
gst::FlowError::Error
|
gst::FlowError::Error
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -769,12 +765,12 @@ fn non_interleaved_channel_data_into_slices<'a, T: FromByteSlice>(
|
||||||
.map(|c| {
|
.map(|c| {
|
||||||
buf.plane_data(c)
|
buf.plane_data(c)
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
gst::error!(CAT, imp = imp, "Failed to get audio data: {}", err);
|
gst::error!(CAT, imp: imp, "Failed to get audio data: {}", err);
|
||||||
gst::FlowError::Error
|
gst::FlowError::Error
|
||||||
})?
|
})?
|
||||||
.as_slice_of::<T>()
|
.as_slice_of::<T>()
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
gst::error!(CAT, imp = imp, "Failed to handle audio data: {}", err);
|
gst::error!(CAT, imp: imp, "Failed to handle audio data: {}", err);
|
||||||
gst::FlowError::Error
|
gst::FlowError::Error
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -22,7 +22,7 @@ pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
gst::Element::register(
|
gst::Element::register(
|
||||||
Some(plugin),
|
Some(plugin),
|
||||||
"ebur128level",
|
"ebur128level",
|
||||||
gst::Rank::NONE,
|
gst::Rank::None,
|
||||||
EbuR128Level::static_type(),
|
EbuR128Level::static_type(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,7 +224,7 @@ impl HrtfRender {
|
||||||
let mut outbuf =
|
let mut outbuf =
|
||||||
gst_audio::AudioBufferRef::from_buffer_ref_writable(outbuf, &state.outinfo).map_err(
|
gst_audio::AudioBufferRef::from_buffer_ref_writable(outbuf, &state.outinfo).map_err(
|
||||||
|err| {
|
|err| {
|
||||||
gst::error!(CAT, imp = self, "Failed to map buffer : {}", err);
|
gst::error!(CAT, imp: self, "Failed to map buffer : {}", err);
|
||||||
gst::FlowError::Error
|
gst::FlowError::Error
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
@ -248,13 +248,13 @@ impl HrtfRender {
|
||||||
|
|
||||||
while state.adapter.available() >= inblksz {
|
while state.adapter.available() >= inblksz {
|
||||||
let inbuf = state.adapter.take_buffer(inblksz).map_err(|_| {
|
let inbuf = state.adapter.take_buffer(inblksz).map_err(|_| {
|
||||||
gst::error!(CAT, imp = self, "Failed to map buffer");
|
gst::error!(CAT, imp: self, "Failed to map buffer");
|
||||||
gst::FlowError::Error
|
gst::FlowError::Error
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let inbuf = gst_audio::AudioBuffer::from_buffer_readable(inbuf, &state.ininfo)
|
let inbuf = gst_audio::AudioBuffer::from_buffer_readable(inbuf, &state.ininfo)
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
gst::error!(CAT, imp = self, "Failed to map buffer");
|
gst::error!(CAT, imp: self, "Failed to map buffer");
|
||||||
gst::FlowError::Error
|
gst::FlowError::Error
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -624,7 +624,7 @@ impl BaseTransformImpl for HrtfRender {
|
||||||
|
|
||||||
gst::log!(
|
gst::log!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = self,
|
imp: self,
|
||||||
"Adapter size: {}, input size {}, transformed size {}",
|
"Adapter size: {}, input size {}, transformed size {}",
|
||||||
state.adapter.available(),
|
state.adapter.available(),
|
||||||
size,
|
size,
|
||||||
|
@ -649,7 +649,7 @@ impl BaseTransformImpl for HrtfRender {
|
||||||
|
|
||||||
if direction == gst::PadDirection::Sink {
|
if direction == gst::PadDirection::Sink {
|
||||||
s.set("channels", 2);
|
s.set("channels", 2);
|
||||||
s.set("channel-mask", gst::Bitmask(0x3));
|
s.set("channel-mask", 0x3);
|
||||||
} else {
|
} else {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
if let Some(objs) = &settings.spatial_objects {
|
if let Some(objs) = &settings.spatial_objects {
|
||||||
|
@ -670,7 +670,7 @@ impl BaseTransformImpl for HrtfRender {
|
||||||
|
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = self,
|
imp: self,
|
||||||
"Transformed caps from {} to {} in direction {:?}",
|
"Transformed caps from {} to {} in direction {:?}",
|
||||||
caps,
|
caps,
|
||||||
other_caps,
|
other_caps,
|
||||||
|
@ -741,7 +741,7 @@ impl BaseTransformImpl for HrtfRender {
|
||||||
adapter: gst_base::UniqueAdapter::new(),
|
adapter: gst_base::UniqueAdapter::new(),
|
||||||
});
|
});
|
||||||
|
|
||||||
gst::debug!(CAT, imp = self, "Configured for caps {}", incaps);
|
gst::debug!(CAT, imp: self, "Configured for caps {}", incaps);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -749,7 +749,7 @@ impl BaseTransformImpl for HrtfRender {
|
||||||
fn sink_event(&self, event: gst::Event) -> bool {
|
fn sink_event(&self, event: gst::Event) -> bool {
|
||||||
use gst::EventView;
|
use gst::EventView;
|
||||||
|
|
||||||
gst::debug!(CAT, imp = self, "Handling event {:?}", event);
|
gst::debug!(CAT, imp: self, "Handling event {:?}", event);
|
||||||
|
|
||||||
match event.view() {
|
match event.view() {
|
||||||
EventView::FlushStop(_) => {
|
EventView::FlushStop(_) => {
|
||||||
|
|
|
@ -19,7 +19,7 @@ pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
gst::Element::register(
|
gst::Element::register(
|
||||||
Some(plugin),
|
Some(plugin),
|
||||||
"hrtfrender",
|
"hrtfrender",
|
||||||
gst::Rank::NONE,
|
gst::Rank::None,
|
||||||
HrtfRender::static_type(),
|
HrtfRender::static_type(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ fn run_test(
|
||||||
};
|
};
|
||||||
|
|
||||||
let pipeline = if let Some(second_input) = second_input {
|
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.",
|
"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,
|
first_input = first_input,
|
||||||
second_input = second_input,
|
second_input = second_input,
|
||||||
|
@ -49,7 +49,7 @@ fn run_test(
|
||||||
format = format,
|
format = format,
|
||||||
))
|
))
|
||||||
} else {
|
} 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",
|
"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]
|
#[test]
|
||||||
fn silence() {
|
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]
|
#[test]
|
||||||
|
@ -228,7 +228,7 @@ fn below_threshold() {
|
||||||
1000,
|
1000,
|
||||||
1024,
|
1024,
|
||||||
1,
|
1,
|
||||||
f64::NEG_INFINITY,
|
std::f64::NEG_INFINITY,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -156,7 +156,7 @@ fn test_hrtfrender_explicit_spatial_objects() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[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
|
// of objects that we set via property. In this test case input has 6 channels
|
||||||
// but the number of spatial objects set is 2.
|
// but the number of spatial objects set is 2.
|
||||||
fn test_hrtfrender_caps_negotiation_fail() {
|
fn test_hrtfrender_caps_negotiation_fail() {
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
[package]
|
[package]
|
||||||
name = "gst-plugin-claxon"
|
name = "gst-plugin-claxon"
|
||||||
version.workspace = true
|
version = "0.10.3"
|
||||||
authors = ["Ruben Gonzalez <rgonzalez@fluendo.com>"]
|
authors = ["Ruben Gonzalez <rgonzalez@fluendo.com>"]
|
||||||
repository.workspace = true
|
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
description = "GStreamer Claxon FLAC Decoder Plugin"
|
description = "GStreamer Claxon FLAC Decoder Plugin"
|
||||||
edition.workspace = true
|
edition = "2021"
|
||||||
rust-version.workspace = true
|
rust-version = "1.63"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gst.workspace = true
|
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
|
||||||
gst-audio.workspace = true
|
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
|
||||||
claxon = { version = "0.4" }
|
claxon = { version = "0.4" }
|
||||||
byte-slice-cast = "1.0"
|
byte-slice-cast = "1.0"
|
||||||
atomic_refcell = "0.1"
|
atomic_refcell = "0.1"
|
||||||
once_cell.workspace = true
|
once_cell = "1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[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]
|
[lib]
|
||||||
name = "gstclaxon"
|
name = "gstclaxon"
|
||||||
|
@ -25,7 +25,7 @@ crate-type = ["cdylib", "rlib"]
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
gst-plugin-version-helper.workspace = true
|
gst-plugin-version-helper = { path="../../version-helper", version = "0.7" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
static = []
|
static = []
|
||||||
|
@ -33,7 +33,7 @@ capi = []
|
||||||
doc = ["gst/v1_18"]
|
doc = ["gst/v1_18"]
|
||||||
|
|
||||||
[package.metadata.capi]
|
[package.metadata.capi]
|
||||||
min_version = "0.9.21"
|
min_version = "0.8.0"
|
||||||
|
|
||||||
[package.metadata.capi.header]
|
[package.metadata.capi.header]
|
||||||
enabled = false
|
enabled = false
|
||||||
|
@ -41,7 +41,6 @@ enabled = false
|
||||||
[package.metadata.capi.library]
|
[package.metadata.capi.library]
|
||||||
install_subdir = "gstreamer-1.0"
|
install_subdir = "gstreamer-1.0"
|
||||||
versioning = false
|
versioning = false
|
||||||
import_library = false
|
|
||||||
|
|
||||||
[package.metadata.capi.pkg_config]
|
[package.metadata.capi.pkg_config]
|
||||||
requires_private = "gstreamer-1.0, gstreamer-audio-1.0, gobject-2.0, glib-2.0, gmodule-2.0"
|
requires_private = "gstreamer-1.0, gstreamer-audio-1.0, gobject-2.0, glib-2.0, gmodule-2.0"
|
||||||
|
|
|
@ -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
1
audio/claxon/LICENSE-APACHE
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../LICENSE-APACHE
|
|
@ -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
1
audio/claxon/LICENSE-MIT
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../LICENSE-MIT
|
|
@ -115,7 +115,7 @@ impl AudioDecoderImpl for ClaxonDec {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_format(&self, caps: &gst::Caps) -> Result<(), gst::LoggableError> {
|
fn set_format(&self, caps: &gst::Caps) -> Result<(), gst::LoggableError> {
|
||||||
gst::debug!(CAT, imp = self, "Setting format {:?}", caps);
|
gst::debug!(CAT, imp: self, "Setting format {:?}", caps);
|
||||||
|
|
||||||
let mut audio_info: Option<gst_audio::AudioInfo> = None;
|
let mut audio_info: Option<gst_audio::AudioInfo> = None;
|
||||||
|
|
||||||
|
@ -124,15 +124,15 @@ impl AudioDecoderImpl for ClaxonDec {
|
||||||
let streamheaders = streamheaders.as_slice();
|
let streamheaders = streamheaders.as_slice();
|
||||||
|
|
||||||
if streamheaders.len() < 2 {
|
if streamheaders.len() < 2 {
|
||||||
gst::debug!(CAT, imp = self, "Not enough streamheaders, trying in-band");
|
gst::debug!(CAT, imp: self, "Not enough streamheaders, trying in-band");
|
||||||
} else {
|
} else {
|
||||||
let ident_buf = streamheaders[0].get::<Option<gst::Buffer>>();
|
let ident_buf = streamheaders[0].get::<Option<gst::Buffer>>();
|
||||||
if let Ok(Some(ident_buf)) = ident_buf {
|
if let Ok(Some(ident_buf)) = ident_buf {
|
||||||
gst::debug!(CAT, imp = self, "Got streamheader buffers");
|
gst::debug!(CAT, imp: self, "Got streamheader buffers");
|
||||||
let inmap = ident_buf.map_readable().unwrap();
|
let inmap = ident_buf.map_readable().unwrap();
|
||||||
|
|
||||||
if inmap[0..7] != [0x7f, b'F', b'L', b'A', b'C', 0x01, 0x00] {
|
if inmap[0..7] != [0x7f, b'F', b'L', b'A', b'C', 0x01, 0x00] {
|
||||||
gst::debug!(CAT, imp = self, "Unknown streamheader format");
|
gst::debug!(CAT, imp: self, "Unknown streamheader format");
|
||||||
} else if let Ok(tstreaminfo) = claxon_streaminfo(&inmap[13..]) {
|
} else if let Ok(tstreaminfo) = claxon_streaminfo(&inmap[13..]) {
|
||||||
if let Ok(taudio_info) = gstaudioinfo(&tstreaminfo) {
|
if let Ok(taudio_info) = gstaudioinfo(&tstreaminfo) {
|
||||||
// To speed up negotiation
|
// To speed up negotiation
|
||||||
|
@ -142,7 +142,7 @@ impl AudioDecoderImpl for ClaxonDec {
|
||||||
{
|
{
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = self,
|
imp: self,
|
||||||
"Error to negotiate output from based on in-caps streaminfo"
|
"Error to negotiate output from based on in-caps streaminfo"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -165,7 +165,7 @@ impl AudioDecoderImpl for ClaxonDec {
|
||||||
&self,
|
&self,
|
||||||
inbuf: Option<&gst::Buffer>,
|
inbuf: Option<&gst::Buffer>,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
gst::debug!(CAT, imp = self, "Handling buffer {:?}", inbuf);
|
gst::debug!(CAT, imp: self, "Handling buffer {:?}", inbuf);
|
||||||
|
|
||||||
let inbuf = match inbuf {
|
let inbuf = match inbuf {
|
||||||
None => return Ok(gst::FlowSuccess::Ok),
|
None => return Ok(gst::FlowSuccess::Ok),
|
||||||
|
@ -173,7 +173,7 @@ impl AudioDecoderImpl for ClaxonDec {
|
||||||
};
|
};
|
||||||
|
|
||||||
let inmap = inbuf.map_readable().map_err(|_| {
|
let inmap = inbuf.map_readable().map_err(|_| {
|
||||||
gst::error!(CAT, imp = self, "Failed to buffer readable");
|
gst::error!(CAT, imp: self, "Failed to buffer readable");
|
||||||
gst::FlowError::Error
|
gst::FlowError::Error
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -181,18 +181,18 @@ impl AudioDecoderImpl for ClaxonDec {
|
||||||
let state = state_guard.as_mut().ok_or(gst::FlowError::NotNegotiated)?;
|
let state = state_guard.as_mut().ok_or(gst::FlowError::NotNegotiated)?;
|
||||||
|
|
||||||
if inmap.as_slice() == b"fLaC" {
|
if inmap.as_slice() == b"fLaC" {
|
||||||
gst::debug!(CAT, imp = self, "fLaC buffer received");
|
gst::debug!(CAT, imp: self, "fLaC buffer received");
|
||||||
} else if inmap[0] & 0x7F == 0x00 {
|
} else if inmap[0] & 0x7F == 0x00 {
|
||||||
gst::debug!(CAT, imp = self, "Streaminfo header buffer received");
|
gst::debug!(CAT, imp: self, "Streaminfo header buffer received");
|
||||||
return self.handle_streaminfo_header(state, inmap.as_ref());
|
return self.handle_streaminfo_header(state, inmap.as_ref());
|
||||||
} else if inmap[0] == 0b1111_1111 && inmap[1] & 0b1111_1100 == 0b1111_1000 {
|
} else if inmap[0] == 0b1111_1111 && inmap[1] & 0b1111_1100 == 0b1111_1000 {
|
||||||
gst::debug!(CAT, imp = self, "Data buffer received");
|
gst::debug!(CAT, imp: self, "Data buffer received");
|
||||||
return self.handle_data(state, inmap.as_ref());
|
return self.handle_data(state, inmap.as_ref());
|
||||||
} else {
|
} else {
|
||||||
// info about other headers in flacparse and https://xiph.org/flac/format.html
|
// info about other headers in flacparse and https://xiph.org/flac/format.html
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = self,
|
imp: self,
|
||||||
"Other header buffer received {:?}",
|
"Other header buffer received {:?}",
|
||||||
inmap[0] & 0x7F
|
inmap[0] & 0x7F
|
||||||
);
|
);
|
||||||
|
@ -209,18 +209,18 @@ impl ClaxonDec {
|
||||||
indata: &[u8],
|
indata: &[u8],
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
let streaminfo = claxon_streaminfo(indata).map_err(|e| {
|
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
|
gst::FlowError::Error
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let audio_info = gstaudioinfo(&streaminfo).map_err(|e| {
|
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
|
gst::FlowError::Error
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = self,
|
imp: self,
|
||||||
"Successfully parsed headers: {:?}",
|
"Successfully parsed headers: {:?}",
|
||||||
audio_info
|
audio_info
|
||||||
);
|
);
|
||||||
|
|
|
@ -21,7 +21,7 @@ pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
gst::Element::register(
|
gst::Element::register(
|
||||||
Some(plugin),
|
Some(plugin),
|
||||||
"claxondec",
|
"claxondec",
|
||||||
gst::Rank::MARGINAL,
|
gst::Rank::Marginal,
|
||||||
ClaxonDec::static_type(),
|
ClaxonDec::static_type(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
[package]
|
[package]
|
||||||
name = "gst-plugin-csound"
|
name = "gst-plugin-csound"
|
||||||
version.workspace = true
|
version = "0.10.3"
|
||||||
authors = ["Natanael Mojica <neithanmo@gmail.com>"]
|
authors = ["Natanael Mojica <neithanmo@gmail.com>"]
|
||||||
repository.workspace = true
|
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
edition.workspace = true
|
edition = "2021"
|
||||||
rust-version.workspace = true
|
rust-version = "1.63"
|
||||||
description = "GStreamer Audio Filter plugin based on Csound"
|
description = "GStreamer Audio Filter plugin based on Csound"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gst.workspace = true
|
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
|
||||||
gst-base.workspace = true
|
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
|
||||||
gst-audio.workspace = true
|
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
|
||||||
csound = "0.1.8"
|
csound = "0.1.8"
|
||||||
|
once_cell = "1.0"
|
||||||
byte-slice-cast = "1.0"
|
byte-slice-cast = "1.0"
|
||||||
once_cell.workspace = true
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[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]
|
[lib]
|
||||||
name = "gstcsound"
|
name = "gstcsound"
|
||||||
|
@ -29,7 +29,7 @@ name = "csound-effect"
|
||||||
path = "examples/effect_example.rs"
|
path = "examples/effect_example.rs"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
gst-plugin-version-helper.workspace = true
|
gst-plugin-version-helper = { path = "../../version-helper", version = "0.7" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
static = []
|
static = []
|
||||||
|
@ -37,7 +37,7 @@ capi = []
|
||||||
doc = ["gst/v1_18"]
|
doc = ["gst/v1_18"]
|
||||||
|
|
||||||
[package.metadata.capi]
|
[package.metadata.capi]
|
||||||
min_version = "0.9.21"
|
min_version = "0.8.0"
|
||||||
|
|
||||||
[package.metadata.capi.header]
|
[package.metadata.capi.header]
|
||||||
enabled = false
|
enabled = false
|
||||||
|
@ -45,7 +45,6 @@ enabled = false
|
||||||
[package.metadata.capi.library]
|
[package.metadata.capi.library]
|
||||||
install_subdir = "gstreamer-1.0"
|
install_subdir = "gstreamer-1.0"
|
||||||
versioning = false
|
versioning = false
|
||||||
import_library = false
|
|
||||||
|
|
||||||
[package.metadata.capi.pkg_config]
|
[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"
|
requires_private = "gstreamer-1.0, gstreamer-base-1.0, gstreamer-audio-1.0, gobject-2.0, glib-2.0, gmodule-2.0, csound"
|
||||||
|
|
|
@ -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.
|
|
1
audio/csound/LICENSE-MPL-2.0
Symbolic link
1
audio/csound/LICENSE-MPL-2.0
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../LICENSE-MPL-2.0
|
|
@ -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,
|
// 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.
|
// 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.
|
// 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.
|
// this creates an space effect using just one channel audio input.
|
||||||
const CSD: &str = "
|
const CSD: &str = "
|
||||||
<CsoundSynthesizer>
|
<CsoundSynthesizer>
|
||||||
|
@ -75,16 +75,16 @@ const CSD: &str = "
|
||||||
fn create_pipeline() -> Result<gst::Pipeline, Box<dyn Error>> {
|
fn create_pipeline() -> Result<gst::Pipeline, Box<dyn Error>> {
|
||||||
let pipeline = gst::Pipeline::default();
|
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")
|
let csoundfilter = gst::ElementFactory::make("csoundfilter")
|
||||||
.property("csd-text", CSD)
|
.property("csd-text", CSD)
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.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"))?;
|
audio_src.link_pads(Some("src"), &csoundfilter, Some("sink"))?;
|
||||||
csoundfilter.link_pads(Some("src"), &audio_sink, Some("sink"))?;
|
csoundfilter.link_pads(Some("src"), &audio_sink, Some("sink"))?;
|
||||||
|
|
|
@ -17,6 +17,7 @@ use gst_base::subclass::prelude::*;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
use std::{f64, i32};
|
||||||
|
|
||||||
use byte_slice_cast::*;
|
use byte_slice_cast::*;
|
||||||
|
|
||||||
|
@ -126,9 +127,9 @@ impl CsoundFilter {
|
||||||
let spout = csound.get_spout().unwrap();
|
let spout = csound.get_spout().unwrap();
|
||||||
|
|
||||||
let in_chunks = idata.chunks_exact(spin.len());
|
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;
|
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);
|
spin.copy_from_slice(ichunk);
|
||||||
end_score = csound.perform_ksmps();
|
end_score = csound.perform_ksmps();
|
||||||
spout.copy_to_slice(ochunk);
|
spout.copy_to_slice(ochunk);
|
||||||
|
@ -143,11 +144,11 @@ impl CsoundFilter {
|
||||||
if let Some(ref location) = settings.location {
|
if let Some(ref location) = settings.location {
|
||||||
csound
|
csound
|
||||||
.compile_csd(location)
|
.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 {
|
} else if let Some(ref text) = settings.csd_text {
|
||||||
csound
|
csound
|
||||||
.compile_csd_text(text)
|
.compile_csd_text(text)
|
||||||
.map_err(|e| error_msg!(gst::LibraryError::Failed, ["{e}"]))?;
|
.map_err(|e| error_msg!(gst::LibraryError::Failed, [e]))?;
|
||||||
} else {
|
} else {
|
||||||
return Err(error_msg!(
|
return Err(error_msg!(
|
||||||
gst::LibraryError::Failed,
|
gst::LibraryError::Failed,
|
||||||
|
@ -191,7 +192,7 @@ impl CsoundFilter {
|
||||||
(avail / state.in_info.channels() as usize) * state.out_info.channels() as usize;
|
(avail / state.in_info.channels() as usize) * state.out_info.channels() as usize;
|
||||||
|
|
||||||
let mut buffer = gst::Buffer::with_size(out_bytes).map_err(|e| {
|
let mut buffer = gst::Buffer::with_size(out_bytes).map_err(|e| {
|
||||||
gst::error!(CAT, imp = self, "Failed to allocate buffer at EOS {:?}", e);
|
gst::error!(CAT, imp: self, "Failed to allocate buffer at EOS {:?}", e);
|
||||||
gst::FlowError::Flushing
|
gst::FlowError::Flushing
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -246,14 +247,14 @@ impl CsoundFilter {
|
||||||
|
|
||||||
gst::log!(
|
gst::log!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = self,
|
imp: self,
|
||||||
"Generating output at: {} - duration: {}",
|
"Generating output at: {} - duration: {}",
|
||||||
pts.display(),
|
pts.display(),
|
||||||
duration.display(),
|
duration.display(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Get the required amount of bytes to be read from
|
// 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 bytes_to_read = state.bytes_to_read(output_size);
|
||||||
|
|
||||||
let indata = state
|
let indata = state
|
||||||
|
@ -469,7 +470,7 @@ impl BaseTransformImpl for CsoundFilter {
|
||||||
csound.set_score_offset_seconds(settings.offset);
|
csound.set_score_offset_seconds(settings.offset);
|
||||||
|
|
||||||
if let Err(e) = csound.start() {
|
if let Err(e) = csound.start() {
|
||||||
return Err(error_msg!(gst::LibraryError::Failed, ["{e}"]));
|
return Err(error_msg!(gst::LibraryError::Failed, [e]));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -481,7 +482,7 @@ impl BaseTransformImpl for CsoundFilter {
|
||||||
csound.reset();
|
csound.reset();
|
||||||
let _ = self.state.lock().unwrap().take();
|
let _ = self.state.lock().unwrap().take();
|
||||||
|
|
||||||
gst::info!(CAT, imp = self, "Stopped");
|
gst::info!(CAT, imp: self, "Stopped");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -490,7 +491,7 @@ impl BaseTransformImpl for CsoundFilter {
|
||||||
use gst::EventView;
|
use gst::EventView;
|
||||||
|
|
||||||
if let EventView::Eos(_) = event.view() {
|
if let EventView::Eos(_) = event.view() {
|
||||||
gst::log!(CAT, imp = self, "Handling Eos");
|
gst::log!(CAT, imp: self, "Handling Eos");
|
||||||
if self.drain().is_err() {
|
if self.drain().is_err() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -535,7 +536,7 @@ impl BaseTransformImpl for CsoundFilter {
|
||||||
|
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = self,
|
imp: self,
|
||||||
"Transformed caps from {} to {} in direction {:?}",
|
"Transformed caps from {} to {} in direction {:?}",
|
||||||
caps,
|
caps,
|
||||||
other_caps,
|
other_caps,
|
||||||
|
@ -553,7 +554,7 @@ impl BaseTransformImpl for CsoundFilter {
|
||||||
// Flush previous state
|
// Flush previous state
|
||||||
if self.state.lock().unwrap().is_some() {
|
if self.state.lock().unwrap().is_some() {
|
||||||
self.drain()
|
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)
|
let in_info = gst_audio::AudioInfo::from_caps(incaps)
|
||||||
|
|
|
@ -19,7 +19,7 @@ pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
gst::Element::register(
|
gst::Element::register(
|
||||||
Some(plugin),
|
Some(plugin),
|
||||||
"csoundfilter",
|
"csoundfilter",
|
||||||
gst::Rank::NONE,
|
gst::Rank::None,
|
||||||
CsoundFilter::static_type(),
|
CsoundFilter::static_type(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
[package]
|
[package]
|
||||||
name = "gst-plugin-lewton"
|
name = "gst-plugin-lewton"
|
||||||
version.workspace = true
|
version = "0.10.3"
|
||||||
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
|
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"
|
license = "MIT OR Apache-2.0"
|
||||||
description = "GStreamer lewton Vorbis Decoder Plugin"
|
description = "GStreamer lewton Vorbis Decoder Plugin"
|
||||||
edition.workspace = true
|
edition = "2021"
|
||||||
rust-version.workspace = true
|
rust-version = "1.63"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gst.workspace = true
|
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
|
||||||
gst-audio.workspace = true
|
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 }
|
lewton = { version = "0.10", default-features = false }
|
||||||
byte-slice-cast = "1.0"
|
byte-slice-cast = "1.0"
|
||||||
atomic_refcell = "0.1"
|
atomic_refcell = "0.1"
|
||||||
once_cell.workspace = true
|
once_cell = "1.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[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]
|
[lib]
|
||||||
name = "gstlewton"
|
name = "gstlewton"
|
||||||
|
@ -25,7 +25,7 @@ crate-type = ["cdylib", "rlib"]
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
gst-plugin-version-helper.workspace = true
|
gst-plugin-version-helper = { path="../../version-helper", version = "0.7" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
static = []
|
static = []
|
||||||
|
@ -33,7 +33,7 @@ capi = []
|
||||||
doc = ["gst/v1_18"]
|
doc = ["gst/v1_18"]
|
||||||
|
|
||||||
[package.metadata.capi]
|
[package.metadata.capi]
|
||||||
min_version = "0.9.21"
|
min_version = "0.8.0"
|
||||||
|
|
||||||
[package.metadata.capi.header]
|
[package.metadata.capi.header]
|
||||||
enabled = false
|
enabled = false
|
||||||
|
@ -41,7 +41,6 @@ enabled = false
|
||||||
[package.metadata.capi.library]
|
[package.metadata.capi.library]
|
||||||
install_subdir = "gstreamer-1.0"
|
install_subdir = "gstreamer-1.0"
|
||||||
versioning = false
|
versioning = false
|
||||||
import_library = false
|
|
||||||
|
|
||||||
[package.metadata.capi.pkg_config]
|
[package.metadata.capi.pkg_config]
|
||||||
requires_private = "gstreamer-1.0, gstreamer-audio-1.0, gobject-2.0, glib-2.0, gmodule-2.0"
|
requires_private = "gstreamer-1.0, gstreamer-audio-1.0, gobject-2.0, glib-2.0, gmodule-2.0"
|
||||||
|
|
|
@ -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
1
audio/lewton/LICENSE-APACHE
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../LICENSE-APACHE
|
|
@ -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
1
audio/lewton/LICENSE-MIT
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../LICENSE-MIT
|
|
@ -120,7 +120,7 @@ impl AudioDecoderImpl for LewtonDec {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_format(&self, caps: &gst::Caps) -> Result<(), gst::LoggableError> {
|
fn set_format(&self, caps: &gst::Caps) -> Result<(), gst::LoggableError> {
|
||||||
gst::debug!(CAT, imp = self, "Setting format {:?}", caps);
|
gst::debug!(CAT, imp: self, "Setting format {:?}", caps);
|
||||||
|
|
||||||
// When the caps are changing we require new headers
|
// When the caps are changing we require new headers
|
||||||
let mut state_guard = self.state.borrow_mut();
|
let mut state_guard = self.state.borrow_mut();
|
||||||
|
@ -132,13 +132,13 @@ impl AudioDecoderImpl for LewtonDec {
|
||||||
reorder_map: None,
|
reorder_map: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
let state = state_guard.as_mut().unwrap();
|
let mut state = state_guard.as_mut().unwrap();
|
||||||
|
|
||||||
let s = caps.structure(0).unwrap();
|
let s = caps.structure(0).unwrap();
|
||||||
if let Ok(Some(streamheaders)) = s.get_optional::<gst::ArrayRef>("streamheader") {
|
if let Ok(Some(streamheaders)) = s.get_optional::<gst::ArrayRef>("streamheader") {
|
||||||
let streamheaders = streamheaders.as_slice();
|
let streamheaders = streamheaders.as_slice();
|
||||||
if streamheaders.len() < 3 {
|
if streamheaders.len() < 3 {
|
||||||
gst::debug!(CAT, imp = self, "Not enough streamheaders, trying in-band");
|
gst::debug!(CAT, imp: self, "Not enough streamheaders, trying in-band");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,7 +148,7 @@ impl AudioDecoderImpl for LewtonDec {
|
||||||
if let (Ok(Some(ident_buf)), Ok(Some(comment_buf)), Ok(Some(setup_buf))) =
|
if let (Ok(Some(ident_buf)), Ok(Some(comment_buf)), Ok(Some(setup_buf))) =
|
||||||
(ident_buf, comment_buf, setup_buf)
|
(ident_buf, comment_buf, setup_buf)
|
||||||
{
|
{
|
||||||
gst::debug!(CAT, imp = self, "Got streamheader buffers");
|
gst::debug!(CAT, imp: self, "Got streamheader buffers");
|
||||||
state.header_bufs = (Some(ident_buf), Some(comment_buf), Some(setup_buf));
|
state.header_bufs = (Some(ident_buf), Some(comment_buf), Some(setup_buf));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -157,7 +157,7 @@ impl AudioDecoderImpl for LewtonDec {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&self, _hard: bool) {
|
fn flush(&self, _hard: bool) {
|
||||||
gst::debug!(CAT, imp = self, "Flushing");
|
gst::debug!(CAT, imp: self, "Flushing");
|
||||||
|
|
||||||
let mut state_guard = self.state.borrow_mut();
|
let mut state_guard = self.state.borrow_mut();
|
||||||
if let Some(ref mut state) = *state_guard {
|
if let Some(ref mut state) = *state_guard {
|
||||||
|
@ -169,7 +169,7 @@ impl AudioDecoderImpl for LewtonDec {
|
||||||
&self,
|
&self,
|
||||||
inbuf: Option<&gst::Buffer>,
|
inbuf: Option<&gst::Buffer>,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
gst::debug!(CAT, imp = self, "Handling buffer {:?}", inbuf);
|
gst::debug!(CAT, imp: self, "Handling buffer {:?}", inbuf);
|
||||||
|
|
||||||
let inbuf = match inbuf {
|
let inbuf = match inbuf {
|
||||||
None => return Ok(gst::FlowSuccess::Ok),
|
None => return Ok(gst::FlowSuccess::Ok),
|
||||||
|
@ -177,7 +177,7 @@ impl AudioDecoderImpl for LewtonDec {
|
||||||
};
|
};
|
||||||
|
|
||||||
let inmap = inbuf.map_readable().map_err(|_| {
|
let inmap = inbuf.map_readable().map_err(|_| {
|
||||||
gst::error!(CAT, imp = self, "Failed to buffer readable");
|
gst::error!(CAT, imp: self, "Failed to buffer readable");
|
||||||
gst::FlowError::Error
|
gst::FlowError::Error
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -191,7 +191,7 @@ impl AudioDecoderImpl for LewtonDec {
|
||||||
if state.headerset.is_some() {
|
if state.headerset.is_some() {
|
||||||
return Ok(gst::FlowSuccess::Ok);
|
return Ok(gst::FlowSuccess::Ok);
|
||||||
} else {
|
} else {
|
||||||
gst::error!(CAT, imp = self, "Got empty packet before all headers");
|
gst::error!(CAT, imp: self, "Got empty packet before all headers");
|
||||||
return Err(gst::FlowError::Error);
|
return Err(gst::FlowError::Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,14 +219,14 @@ impl LewtonDec {
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
// ident header
|
// ident header
|
||||||
if indata[0] == 0x01 {
|
if indata[0] == 0x01 {
|
||||||
gst::debug!(CAT, imp = self, "Got ident header buffer");
|
gst::debug!(CAT, imp: self, "Got ident header buffer");
|
||||||
state.header_bufs = (Some(inbuf.clone()), None, None);
|
state.header_bufs = (Some(inbuf.clone()), None, None);
|
||||||
} else if indata[0] == 0x03 {
|
} else if indata[0] == 0x03 {
|
||||||
// comment header
|
// comment header
|
||||||
if state.header_bufs.0.is_none() {
|
if state.header_bufs.0.is_none() {
|
||||||
gst::warning!(CAT, imp = self, "Got comment header before ident header");
|
gst::warning!(CAT, imp: self, "Got comment header before ident header");
|
||||||
} else {
|
} else {
|
||||||
gst::debug!(CAT, imp = self, "Got comment header buffer");
|
gst::debug!(CAT, imp: self, "Got comment header buffer");
|
||||||
state.header_bufs.1 = Some(inbuf.clone());
|
state.header_bufs.1 = Some(inbuf.clone());
|
||||||
}
|
}
|
||||||
} else if indata[0] == 0x05 {
|
} else if indata[0] == 0x05 {
|
||||||
|
@ -234,11 +234,11 @@ impl LewtonDec {
|
||||||
if state.header_bufs.0.is_none() || state.header_bufs.1.is_none() {
|
if state.header_bufs.0.is_none() || state.header_bufs.1.is_none() {
|
||||||
gst::warning!(
|
gst::warning!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = self,
|
imp: self,
|
||||||
"Got setup header before ident/comment header"
|
"Got setup header before ident/comment header"
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
gst::debug!(CAT, imp = self, "Got setup header buffer");
|
gst::debug!(CAT, imp: self, "Got setup header buffer");
|
||||||
state.header_bufs.2 = Some(inbuf.clone());
|
state.header_bufs.2 = Some(inbuf.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -263,7 +263,7 @@ impl LewtonDec {
|
||||||
|
|
||||||
// First try to parse the headers
|
// First try to parse the headers
|
||||||
let ident_map = ident_buf.map_readable().map_err(|_| {
|
let ident_map = ident_buf.map_readable().map_err(|_| {
|
||||||
gst::error!(CAT, imp = self, "Failed to map ident buffer readable");
|
gst::error!(CAT, imp: self, "Failed to map ident buffer readable");
|
||||||
gst::FlowError::Error
|
gst::FlowError::Error
|
||||||
})?;
|
})?;
|
||||||
let ident = lewton::header::read_header_ident(ident_map.as_ref()).map_err(|err| {
|
let ident = lewton::header::read_header_ident(ident_map.as_ref()).map_err(|err| {
|
||||||
|
@ -276,7 +276,7 @@ impl LewtonDec {
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let comment_map = comment_buf.map_readable().map_err(|_| {
|
let comment_map = comment_buf.map_readable().map_err(|_| {
|
||||||
gst::error!(CAT, imp = self, "Failed to map comment buffer readable");
|
gst::error!(CAT, imp: self, "Failed to map comment buffer readable");
|
||||||
gst::FlowError::Error
|
gst::FlowError::Error
|
||||||
})?;
|
})?;
|
||||||
let comment = lewton::header::read_header_comment(comment_map.as_ref()).map_err(|err| {
|
let comment = lewton::header::read_header_comment(comment_map.as_ref()).map_err(|err| {
|
||||||
|
@ -289,7 +289,7 @@ impl LewtonDec {
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let setup_map = setup_buf.map_readable().map_err(|_| {
|
let setup_map = setup_buf.map_readable().map_err(|_| {
|
||||||
gst::error!(CAT, imp = self, "Failed to map setup buffer readable");
|
gst::error!(CAT, imp: self, "Failed to map setup buffer readable");
|
||||||
gst::FlowError::Error
|
gst::FlowError::Error
|
||||||
})?;
|
})?;
|
||||||
let setup = lewton::header::read_header_setup(
|
let setup = lewton::header::read_header_setup(
|
||||||
|
@ -327,7 +327,7 @@ impl LewtonDec {
|
||||||
if gst_audio::channel_reorder_map(from, to, &mut map[..channels]).is_err() {
|
if gst_audio::channel_reorder_map(from, to, &mut map[..channels]).is_err() {
|
||||||
gst::error!(
|
gst::error!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = self,
|
imp: self,
|
||||||
"Failed to generate channel reorder map from {:?} to {:?}",
|
"Failed to generate channel reorder map from {:?} to {:?}",
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
|
@ -343,7 +343,7 @@ impl LewtonDec {
|
||||||
|
|
||||||
gst::debug!(
|
gst::debug!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = self,
|
imp: self,
|
||||||
"Successfully parsed headers: {:?}",
|
"Successfully parsed headers: {:?}",
|
||||||
audio_info
|
audio_info
|
||||||
);
|
);
|
||||||
|
@ -396,7 +396,7 @@ impl LewtonDec {
|
||||||
}
|
}
|
||||||
|
|
||||||
let sample_count = decoded.samples.len() / audio_info.channels() as usize;
|
let sample_count = decoded.samples.len() / audio_info.channels() as usize;
|
||||||
gst::debug!(CAT, imp = self, "Got {} decoded samples", sample_count);
|
gst::debug!(CAT, imp: self, "Got {} decoded samples", sample_count);
|
||||||
|
|
||||||
if sample_count == 0 {
|
if sample_count == 0 {
|
||||||
return self.obj().finish_frame(None, 1);
|
return self.obj().finish_frame(None, 1);
|
||||||
|
|
|
@ -21,7 +21,7 @@ pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
gst::Element::register(
|
gst::Element::register(
|
||||||
Some(plugin),
|
Some(plugin),
|
||||||
"lewtondec",
|
"lewtondec",
|
||||||
gst::Rank::MARGINAL,
|
gst::Rank::Marginal,
|
||||||
LewtonDec::static_type(),
|
LewtonDec::static_type(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "gst-plugin-speechmatics"
|
|
||||||
version.workspace = true
|
|
||||||
authors = ["Mathieu Duponchelle <mathieu@centricular.com>"]
|
|
||||||
repository.workspace = true
|
|
||||||
license = "MPL-2.0"
|
|
||||||
description = "GStreamer Speechmatics plugin"
|
|
||||||
edition.workspace = true
|
|
||||||
rust-version.workspace = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
futures = "0.3"
|
|
||||||
gst.workspace = true
|
|
||||||
gst-base.workspace = true
|
|
||||||
gst-audio = { workspace = true, features = ["v1_16"] }
|
|
||||||
tokio = { version = "1", features = [ "full" ] }
|
|
||||||
async-tungstenite = { version = "0.28", features = ["tokio", "tokio-runtime", "tokio-native-tls"] }
|
|
||||||
once_cell.workspace = true
|
|
||||||
serde = { version = "1", features = ["derive"] }
|
|
||||||
serde_json = "1"
|
|
||||||
atomic_refcell = "0.1"
|
|
||||||
http = { version = "1.0" }
|
|
||||||
url = "2"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "gstspeechmatics"
|
|
||||||
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.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"
|
|
|
@ -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.
|
|
|
@ -1,3 +0,0 @@
|
||||||
fn main() {
|
|
||||||
gst_plugin_version_helper::info()
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
// Copyright (C) 2024 Mathieu Duponchelle <mathieu@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)]
|
|
||||||
#![recursion_limit = "128"]
|
|
||||||
|
|
||||||
/**
|
|
||||||
* plugin-speechmatics:
|
|
||||||
*
|
|
||||||
* Since: plugins-rs-0.14.0
|
|
||||||
*/
|
|
||||||
use gst::glib;
|
|
||||||
|
|
||||||
mod transcriber;
|
|
||||||
|
|
||||||
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
|
||||||
transcriber::register(plugin)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
gst::plugin_define!(
|
|
||||||
speechmatics,
|
|
||||||
env!("CARGO_PKG_DESCRIPTION"),
|
|
||||||
plugin_init,
|
|
||||||
concat!(env!("CARGO_PKG_VERSION"), "-", env!("COMMIT_ID")),
|
|
||||||
"Proprietary",
|
|
||||||
env!("CARGO_PKG_NAME"),
|
|
||||||
env!("CARGO_PKG_NAME"),
|
|
||||||
env!("CARGO_PKG_REPOSITORY"),
|
|
||||||
env!("BUILD_REL_DATE")
|
|
||||||
);
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,33 +0,0 @@
|
||||||
// Copyright (C) 2024 Mathieu Duponchelle <mathieu@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 struct Transcriber(ObjectSubclass<imp::Transcriber>) @extends gst::Element, gst::Object, @implements gst::ChildProxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
glib::wrapper! {
|
|
||||||
pub struct TranscriberSrcPad(ObjectSubclass<imp::TranscriberSrcPad>) @extends gst::Pad, gst::Object;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
|
||||||
#[cfg(feature = "doc")]
|
|
||||||
{
|
|
||||||
TranscriberSrcPad::static_type().mark_as_plugin_api(gst::PluginAPIFlags::empty());
|
|
||||||
}
|
|
||||||
gst::Element::register(
|
|
||||||
Some(plugin),
|
|
||||||
"speechmaticstranscriber",
|
|
||||||
gst::Rank::NONE,
|
|
||||||
Transcriber::static_type(),
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,23 +1,22 @@
|
||||||
[package]
|
[package]
|
||||||
name = "gst-plugin-spotify"
|
name = "gst-plugin-spotify"
|
||||||
version.workspace = true
|
version = "0.10.3"
|
||||||
authors = ["Guillaume Desmottes <guillaume@desmottes.be>"]
|
authors = ["Guillaume Desmottes <guillaume@desmottes.be>"]
|
||||||
repository.workspace = true
|
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
description = "GStreamer Spotify Plugin"
|
description = "GStreamer Spotify Plugin"
|
||||||
edition.workspace = true
|
edition = "2021"
|
||||||
rust-version.workspace = true
|
rust-version = "1.63"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gst.workspace = true
|
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
|
||||||
gst-base.workspace = true
|
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
|
||||||
librespot-core = "0.4"
|
once_cell = "1.0"
|
||||||
librespot-playback = "0.4"
|
librespot = { version = "0.4", default-features = false }
|
||||||
tokio = { version = "1", features = ["rt-multi-thread"] }
|
tokio = "1.0"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
url = "2.3"
|
url = "2.3"
|
||||||
once_cell.workspace = true
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "gstspotify"
|
name = "gstspotify"
|
||||||
|
@ -25,7 +24,7 @@ crate-type = ["cdylib", "rlib"]
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
gst-plugin-version-helper.workspace = true
|
gst-plugin-version-helper = { path="../../version-helper", version = "0.7" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
static = []
|
static = []
|
||||||
|
@ -33,7 +32,7 @@ capi = []
|
||||||
doc = ["gst/v1_18"]
|
doc = ["gst/v1_18"]
|
||||||
|
|
||||||
[package.metadata.capi]
|
[package.metadata.capi]
|
||||||
min_version = "0.9.21"
|
min_version = "0.8.0"
|
||||||
|
|
||||||
[package.metadata.capi.header]
|
[package.metadata.capi.header]
|
||||||
enabled = false
|
enabled = false
|
||||||
|
@ -41,7 +40,6 @@ enabled = false
|
||||||
[package.metadata.capi.library]
|
[package.metadata.capi.library]
|
||||||
install_subdir = "gstreamer-1.0"
|
install_subdir = "gstreamer-1.0"
|
||||||
versioning = false
|
versioning = false
|
||||||
import_library = false
|
|
||||||
|
|
||||||
[package.metadata.capi.pkg_config]
|
[package.metadata.capi.pkg_config]
|
||||||
requires_private = "gstreamer-1.0, gstreamer-base-1.0, gobject-2.0, glib-2.0, gmodule-2.0"
|
requires_private = "gstreamer-1.0, gstreamer-base-1.0, gobject-2.0, glib-2.0, gmodule-2.0"
|
||||||
|
|
|
@ -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.
|
|
1
audio/spotify/LICENSE-MPL-2.0
Symbolic link
1
audio/spotify/LICENSE-MPL-2.0
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../LICENSE-MPL-2.0
|
|
@ -8,11 +8,10 @@ to respect their legal/licensing restrictions.
|
||||||
|
|
||||||
## Spotify Credentials
|
## Spotify Credentials
|
||||||
|
|
||||||
This plugin requires a [Spotify Premium](https://www.spotify.com/premium/) account.
|
This plugin requires a [Spotify Premium](https://www.spotify.com/premium/) account configured
|
||||||
If your account is linked with Facebook, you'll need to setup
|
with a [device password](https://www.spotify.com/us/account/set-device-password/).
|
||||||
a [device username and 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.
|
You may also want to cache credentials and downloaded files, see the `cache-` properties on the element.
|
||||||
|
|
||||||
|
|
|
@ -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::{
|
|
||||||
authentication::Credentials, cache::Cache, config::SessionConfig, session::Session,
|
|
||||||
spotify_id::SpotifyId,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -14,7 +14,6 @@
|
||||||
*/
|
*/
|
||||||
use gst::glib;
|
use gst::glib;
|
||||||
|
|
||||||
mod common;
|
|
||||||
mod spotifyaudiosrc;
|
mod spotifyaudiosrc;
|
||||||
|
|
||||||
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
|
|
|
@ -6,8 +6,9 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// 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 futures::future::{AbortHandle, Abortable, Aborted};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use tokio::{runtime, task::JoinHandle};
|
use tokio::{runtime, task::JoinHandle};
|
||||||
|
@ -17,7 +18,11 @@ use gst::prelude::*;
|
||||||
use gst::subclass::prelude::*;
|
use gst::subclass::prelude::*;
|
||||||
use gst_base::subclass::{base_src::CreateSuccess, prelude::*};
|
use gst_base::subclass::{base_src::CreateSuccess, prelude::*};
|
||||||
|
|
||||||
use librespot_playback::{
|
use librespot::core::{
|
||||||
|
cache::Cache, config::SessionConfig, session::Session, spotify_id::SpotifyId,
|
||||||
|
};
|
||||||
|
use librespot::discovery::Credentials;
|
||||||
|
use librespot::playback::{
|
||||||
audio_backend::{Sink, SinkResult},
|
audio_backend::{Sink, SinkResult},
|
||||||
config::PlayerConfig,
|
config::PlayerConfig,
|
||||||
convert::Converter,
|
convert::Converter,
|
||||||
|
@ -62,46 +67,27 @@ struct State {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct Settings {
|
struct Settings {
|
||||||
common: crate::common::Settings,
|
username: String,
|
||||||
|
password: String,
|
||||||
|
cache_credentials: String,
|
||||||
|
cache_files: String,
|
||||||
|
cache_max_size: u64,
|
||||||
|
track: String,
|
||||||
bitrate: Bitrate,
|
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)]
|
#[derive(Default)]
|
||||||
pub struct SpotifyAudioSrc {
|
pub struct SpotifyAudioSrc {
|
||||||
setup_thread: Mutex<SetupThread>,
|
setup_thread: Mutex<Option<SetupThread>>,
|
||||||
state: Arc<Mutex<Option<State>>>,
|
state: Arc<Mutex<Option<State>>>,
|
||||||
settings: Mutex<Settings>,
|
settings: Mutex<Settings>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SetupThread {
|
||||||
|
thread_handle: std::thread::JoinHandle<Result<anyhow::Result<()>, Aborted>>,
|
||||||
|
abort_handle: AbortHandle,
|
||||||
|
}
|
||||||
|
|
||||||
#[glib::object_subclass]
|
#[glib::object_subclass]
|
||||||
impl ObjectSubclass for SpotifyAudioSrc {
|
impl ObjectSubclass for SpotifyAudioSrc {
|
||||||
const NAME: &'static str = "GstSpotifyAudioSrc";
|
const NAME: &'static str = "GstSpotifyAudioSrc";
|
||||||
|
@ -113,39 +99,118 @@ impl ObjectSubclass for SpotifyAudioSrc {
|
||||||
impl ObjectImpl for SpotifyAudioSrc {
|
impl ObjectImpl for SpotifyAudioSrc {
|
||||||
fn properties() -> &'static [glib::ParamSpec] {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
let mut props = crate::common::Settings::properties();
|
vec![glib::ParamSpecString::builder("username")
|
||||||
let default = Settings::default();
|
.nick("Username")
|
||||||
|
.blurb("Spotify device username from https://www.spotify.com/us/account/set-device-password/")
|
||||||
props.push(
|
.default_value(Some(""))
|
||||||
glib::ParamSpecEnum::builder_with_default::<Bitrate>("bitrate", default.bitrate)
|
|
||||||
.nick("Spotify bitrate")
|
|
||||||
.blurb("Spotify audio bitrate in kbit/s")
|
|
||||||
.mutable_ready()
|
.mutable_ready()
|
||||||
.build(),
|
.build(),
|
||||||
);
|
glib::ParamSpecString::builder("password")
|
||||||
props
|
.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()
|
PROPERTIES.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
|
||||||
|
|
||||||
match pspec.name() {
|
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" => {
|
"bitrate" => {
|
||||||
|
let mut settings = self.settings.lock().unwrap();
|
||||||
settings.bitrate = value.get().expect("type checked upstream");
|
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 {
|
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||||
let settings = self.settings.lock().unwrap();
|
|
||||||
|
|
||||||
match pspec.name() {
|
match pspec.name() {
|
||||||
"bitrate" => settings.bitrate.to_value(),
|
"username" => {
|
||||||
_ => settings.common.property(pspec),
|
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,20 +261,23 @@ impl BaseSrcImpl for SpotifyAudioSrc {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// If not started yet and not cancelled, start the setup
|
let setup_thread = self.setup_thread.lock().unwrap();
|
||||||
let mut setup_thread = self.setup_thread.lock().unwrap();
|
if setup_thread.is_some() {
|
||||||
assert!(!matches!(&*setup_thread, SetupThread::Cancelled));
|
// already starting
|
||||||
if matches!(&*setup_thread, SetupThread::None) {
|
return Ok(());
|
||||||
self.start_setup(&mut setup_thread);
|
|
||||||
}
|
}
|
||||||
|
self.start_setup(setup_thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop(&self) -> Result<(), gst::ErrorMessage> {
|
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() {
|
if let Some(state) = self.state.lock().unwrap().take() {
|
||||||
gst::debug!(CAT, imp = self, "stopping");
|
gst::debug!(CAT, imp: self, "stopping");
|
||||||
state.player.stop();
|
state.player.stop();
|
||||||
state.player_channel_handle.abort();
|
state.player_channel_handle.abort();
|
||||||
// FIXME: not sure why this is needed to unblock BufferSink::write(), dropping State should drop the receiver
|
// FIXME: not sure why this is needed to unblock BufferSink::write(), dropping State should drop the receiver
|
||||||
|
@ -220,17 +288,9 @@ impl BaseSrcImpl for SpotifyAudioSrc {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unlock(&self) -> Result<(), gst::ErrorMessage> {
|
fn unlock(&self) -> Result<(), gst::ErrorMessage> {
|
||||||
let mut setup_thread = self.setup_thread.lock().unwrap();
|
self.cancel_setup();
|
||||||
setup_thread.abort();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unlock_stop(&self) -> Result<(), gst::ErrorMessage> {
|
self.parent_unlock()
|
||||||
let mut setup_thread = self.setup_thread.lock().unwrap();
|
|
||||||
if matches!(&*setup_thread, SetupThread::Cancelled) {
|
|
||||||
*setup_thread = SetupThread::None;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,47 +305,31 @@ impl PushSrcImpl for SpotifyAudioSrc {
|
||||||
};
|
};
|
||||||
|
|
||||||
if !state_set {
|
if !state_set {
|
||||||
// If not started yet and not cancelled, start the setup
|
let setup_thread = self.setup_thread.lock().unwrap();
|
||||||
let mut setup_thread = self.setup_thread.lock().unwrap();
|
if setup_thread.is_none() {
|
||||||
if matches!(&*setup_thread, SetupThread::Cancelled) {
|
// unlock() could potentially cancel the setup, and create() can be called after unlock() without going through start() again.
|
||||||
return Err(gst::FlowError::Flushing);
|
self.start_setup(setup_thread);
|
||||||
}
|
|
||||||
|
|
||||||
if matches!(&*setup_thread, SetupThread::None) {
|
|
||||||
self.start_setup(&mut setup_thread);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// wait for the setup to be completed
|
// wait for the setup to be completed
|
||||||
let mut setup_thread = self.setup_thread.lock().unwrap();
|
let mut setup_thread = self.setup_thread.lock().unwrap();
|
||||||
if let SetupThread::Pending {
|
if let Some(setup) = setup_thread.take() {
|
||||||
ref mut thread_handle,
|
let res = setup.thread_handle.join().unwrap();
|
||||||
..
|
|
||||||
} = *setup_thread
|
|
||||||
{
|
|
||||||
let thread_handle = thread_handle.take().expect("Waiting multiple times");
|
|
||||||
drop(setup_thread);
|
|
||||||
let res = thread_handle.join().unwrap();
|
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Err(_aborted) => {
|
Err(_aborted) => {
|
||||||
gst::debug!(CAT, imp = self, "setup has been cancelled");
|
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);
|
return Err(gst::FlowError::Flushing);
|
||||||
}
|
}
|
||||||
Ok(Err(err)) => {
|
Ok(Err(err)) => {
|
||||||
gst::error!(CAT, imp = self, "failed to start: {err:?}");
|
let details = format!("{err:?}");
|
||||||
gst::element_imp_error!(self, gst::ResourceError::Settings, ["{err:?}"]);
|
gst::error!(CAT, imp: self, "failed to start: {}", details);
|
||||||
setup_thread = self.setup_thread.lock().unwrap();
|
gst::element_imp_error!(self, gst::ResourceError::Settings, [&details]);
|
||||||
*setup_thread = SetupThread::None;
|
|
||||||
return Err(gst::FlowError::Error);
|
return Err(gst::FlowError::Error);
|
||||||
}
|
}
|
||||||
Ok(Ok(_)) => {
|
Ok(Ok(_)) => {}
|
||||||
setup_thread = self.setup_thread.lock().unwrap();
|
|
||||||
*setup_thread = SetupThread::Done;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -295,15 +339,15 @@ impl PushSrcImpl for SpotifyAudioSrc {
|
||||||
|
|
||||||
match state.receiver.recv().unwrap() {
|
match state.receiver.recv().unwrap() {
|
||||||
Message::Buffer(buffer) => {
|
Message::Buffer(buffer) => {
|
||||||
gst::log!(CAT, imp = self, "got buffer of size {}", buffer.size());
|
gst::log!(CAT, imp: self, "got buffer of size {}", buffer.size());
|
||||||
Ok(CreateSuccess::NewBuffer(buffer))
|
Ok(CreateSuccess::NewBuffer(buffer))
|
||||||
}
|
}
|
||||||
Message::Eos => {
|
Message::Eos => {
|
||||||
gst::debug!(CAT, imp = self, "eos");
|
gst::debug!(CAT, imp: self, "eos");
|
||||||
Err(gst::FlowError::Eos)
|
Err(gst::FlowError::Eos)
|
||||||
}
|
}
|
||||||
Message::Unavailable => {
|
Message::Unavailable => {
|
||||||
gst::error!(CAT, imp = self, "track is not available");
|
gst::error!(CAT, imp: self, "track is not available");
|
||||||
gst::element_imp_error!(
|
gst::element_imp_error!(
|
||||||
self,
|
self,
|
||||||
gst::ResourceError::NotFound,
|
gst::ResourceError::NotFound,
|
||||||
|
@ -344,15 +388,15 @@ impl URIHandlerImpl for SpotifyAudioSrc {
|
||||||
fn uri(&self) -> Option<String> {
|
fn uri(&self) -> Option<String> {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
|
|
||||||
if settings.common.track.is_empty() {
|
if settings.track.is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(settings.common.track.clone())
|
Some(settings.track.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_uri(&self, uri: &str) -> Result<(), glib::Error> {
|
fn set_uri(&self, uri: &str) -> Result<(), glib::Error> {
|
||||||
gst::debug!(CAT, imp = self, "set URI: {}", uri);
|
gst::debug!(CAT, imp: self, "set URI: {}", uri);
|
||||||
|
|
||||||
let url = url::Url::parse(uri)
|
let url = url::Url::parse(uri)
|
||||||
.map_err(|e| glib::Error::new(gst::URIError::BadUri, &format!("{e:?}")))?;
|
.map_err(|e| glib::Error::new(gst::URIError::BadUri, &format!("{e:?}")))?;
|
||||||
|
@ -364,7 +408,7 @@ impl URIHandlerImpl for SpotifyAudioSrc {
|
||||||
self.obj().set_property(&key, value.as_ref());
|
self.obj().set_property(&key, value.as_ref());
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
gst::warning!(CAT, imp = self, "unsupported query: {}={}", key, value);
|
gst::warning!(CAT, imp: self, "unsupported query: {}={}", key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -377,9 +421,7 @@ impl URIHandlerImpl for SpotifyAudioSrc {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpotifyAudioSrc {
|
impl SpotifyAudioSrc {
|
||||||
fn start_setup(&self, setup_thread: &mut SetupThread) {
|
fn start_setup(&self, mut setup_thread: MutexGuard<Option<SetupThread>>) {
|
||||||
assert!(matches!(setup_thread, SetupThread::None));
|
|
||||||
|
|
||||||
let self_ = self.to_owned();
|
let self_ = self.to_owned();
|
||||||
|
|
||||||
// run the runtime from another thread to prevent the "start a runtime from within a runtime" panic
|
// 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 {
|
setup_thread.replace(SetupThread {
|
||||||
thread_handle: Some(thread_handle),
|
thread_handle,
|
||||||
abort_handle,
|
abort_handle,
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn setup(&self) -> anyhow::Result<()> {
|
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 credentials_cache = if settings.cache_credentials.is_empty() {
|
||||||
let (common, bitrate) = {
|
None
|
||||||
let settings = self.settings.lock().unwrap();
|
} else {
|
||||||
let bitrate = settings.bitrate.into();
|
Some(&settings.cache_credentials)
|
||||||
|
|
||||||
(settings.common.clone(), bitrate)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let session = common.connect_session(src.clone(), &CAT).await?;
|
let files_cache = if settings.cache_files.is_empty() {
|
||||||
let track = common.track_id()?;
|
None
|
||||||
gst::debug!(CAT, imp = self, "Requesting bitrate {:?}", bitrate);
|
} else {
|
||||||
|
Some(&settings.cache_files)
|
||||||
|
};
|
||||||
|
|
||||||
(session, track, bitrate)
|
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);
|
||||||
|
|
||||||
|
(credentials, cache, settings.track.clone(), bitrate)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let (session, _credentials) =
|
||||||
|
Session::connect(SessionConfig::default(), credentials, Some(cache), false).await?;
|
||||||
|
|
||||||
let player_config = PlayerConfig {
|
let player_config = PlayerConfig {
|
||||||
passthrough: true,
|
passthrough: true,
|
||||||
bitrate,
|
bitrate,
|
||||||
|
@ -440,6 +522,11 @@ impl SpotifyAudioSrc {
|
||||||
Box::new(BufferSink { sender })
|
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);
|
player.load(track, true, 0);
|
||||||
|
|
||||||
let player_channel_handle = RUNTIME.spawn(async move {
|
let player_channel_handle = RUNTIME.spawn(async move {
|
||||||
|
@ -468,4 +555,12 @@ impl SpotifyAudioSrc {
|
||||||
|
|
||||||
Ok(())
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ impl Default for Bitrate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Bitrate> for librespot_playback::config::Bitrate {
|
impl From<Bitrate> for librespot::playback::config::Bitrate {
|
||||||
fn from(value: Bitrate) -> Self {
|
fn from(value: Bitrate) -> Self {
|
||||||
match value {
|
match value {
|
||||||
Bitrate::B96 => Self::Bitrate96,
|
Bitrate::B96 => Self::Bitrate96,
|
||||||
|
@ -50,7 +50,7 @@ pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
gst::Element::register(
|
gst::Element::register(
|
||||||
Some(plugin),
|
Some(plugin),
|
||||||
"spotifyaudiosrc",
|
"spotifyaudiosrc",
|
||||||
gst::Rank::PRIMARY,
|
gst::Rank::Primary,
|
||||||
SpotifyAudioSrc::static_type(),
|
SpotifyAudioSrc::static_type(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import os
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import shlex
|
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from pathlib import Path as P
|
from pathlib import Path as P
|
||||||
|
|
||||||
|
@ -30,12 +29,6 @@ PARSER.add_argument('--depfile')
|
||||||
PARSER.add_argument('--disable-doc', action="store_true", default=False)
|
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):
|
def generate_depfile_for(fpath):
|
||||||
file_stem = fpath.parent / fpath.stem
|
file_stem = fpath.parent / fpath.stem
|
||||||
depfile_content = ""
|
depfile_content = ""
|
||||||
|
@ -89,21 +82,6 @@ if __name__ == "__main__":
|
||||||
pkg_config_path.append(str(opts.root_dir / 'meson-uninstalled'))
|
pkg_config_path.append(str(opts.root_dir / 'meson-uninstalled'))
|
||||||
env['PKG_CONFIG_PATH'] = os.pathsep.join(pkg_config_path)
|
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
|
features = opts.features
|
||||||
if opts.command == 'build':
|
if opts.command == 'build':
|
||||||
cargo_cmd = ['cargo']
|
cargo_cmd = ['cargo']
|
||||||
|
@ -122,8 +100,6 @@ if __name__ == "__main__":
|
||||||
print("Unknown command:", opts.command, file=logfile)
|
print("Unknown command:", opts.command, file=logfile)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if rustc_target:
|
|
||||||
cargo_cmd += ['--target', rustc_target]
|
|
||||||
if features:
|
if features:
|
||||||
cargo_cmd += ['--features', ','.join(features)]
|
cargo_cmd += ['--features', ','.join(features)]
|
||||||
cargo_cmd += ['--target-dir', cargo_target_dir]
|
cargo_cmd += ['--target-dir', cargo_target_dir]
|
||||||
|
|
|
@ -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(".")
|
|
|
@ -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
|
|
|
@ -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
|
|
11
ci/install-dav1d.sh
Normal file
11
ci/install-dav1d.sh
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
set -e
|
||||||
|
|
||||||
|
RELEASE=1.0.0
|
||||||
|
|
||||||
|
git clone https://code.videolan.org/videolan/dav1d.git --branch $RELEASE
|
||||||
|
cd dav1d
|
||||||
|
meson build -D prefix=/usr/local
|
||||||
|
ninja -C build
|
||||||
|
ninja -C build install
|
||||||
|
cd ..
|
||||||
|
rm -rf dav1d
|
6
ci/install-rust-ext.sh
Executable file
6
ci/install-rust-ext.sh
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
source ./ci/env.sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
export CARGO_HOME='/usr/local/cargo'
|
||||||
|
|
||||||
|
cargo install cargo-c --version 0.9.14+cargo-0.66
|
|
@ -36,7 +36,6 @@ function Run-Tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
$env:G_DEBUG="fatal_warnings"
|
$env:G_DEBUG="fatal_warnings"
|
||||||
$env:RUST_BACKTRACE="1"
|
|
||||||
cargo test --no-fail-fast --color=always --workspace $local_exclude --all-targets $Features
|
cargo test --no-fail-fast --color=always --workspace $local_exclude --all-targets $Features
|
||||||
|
|
||||||
if (!$?) {
|
if (!$?) {
|
||||||
|
|
|
@ -22,8 +22,6 @@ RS_PREFIXED = [
|
||||||
'png',
|
'png',
|
||||||
'tracers',
|
'tracers',
|
||||||
'rtp',
|
'rtp',
|
||||||
'rtsp',
|
|
||||||
'inter',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
OVERRIDE = {
|
OVERRIDE = {
|
||||||
|
|
282
deny.toml
282
deny.toml
|
@ -1,35 +1,37 @@
|
||||||
[advisories]
|
[advisories]
|
||||||
version = 2
|
|
||||||
db-path = "~/.cargo/advisory-db"
|
db-path = "~/.cargo/advisory-db"
|
||||||
db-urls = ["https://github.com/rustsec/advisory-db"]
|
db-urls = ["https://github.com/rustsec/advisory-db"]
|
||||||
|
vulnerability = "deny"
|
||||||
|
unmaintained = "warn"
|
||||||
|
notice = "warn"
|
||||||
ignore = [
|
ignore = [
|
||||||
# Waiting for https://github.com/librespot-org/librespot/issues/937
|
# Waiting for https://github.com/librespot-org/librespot/issues/937
|
||||||
"RUSTSEC-2021-0059",
|
"RUSTSEC-2021-0059",
|
||||||
"RUSTSEC-2021-0060",
|
"RUSTSEC-2021-0060",
|
||||||
"RUSTSEC-2021-0061",
|
"RUSTSEC-2021-0061",
|
||||||
"RUSTSEC-2021-0145",
|
"RUSTSEC-2021-0145",
|
||||||
|
# https://github.com/chronotope/chrono/issues/499
|
||||||
|
"RUSTSEC-2020-0071",
|
||||||
# sodiumoxide is deprecated
|
# sodiumoxide is deprecated
|
||||||
# https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/530
|
|
||||||
"RUSTSEC-2021-0137",
|
"RUSTSEC-2021-0137",
|
||||||
# proc-macro-error is unmaintained
|
# https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/256
|
||||||
# https://github.com/yanganto/test-with/issues/91
|
"RUSTSEC-2022-0048",
|
||||||
"RUSTSEC-2024-0370",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[licenses]
|
[licenses]
|
||||||
version = 2
|
unlicensed = "deny"
|
||||||
allow = [
|
allow = [
|
||||||
"MIT",
|
|
||||||
"BSD-2-Clause",
|
|
||||||
"BSD-3-Clause",
|
|
||||||
"ISC",
|
|
||||||
"OpenSSL",
|
|
||||||
"Zlib",
|
|
||||||
"Unicode-DFS-2016",
|
|
||||||
"Apache-2.0",
|
"Apache-2.0",
|
||||||
"Apache-2.0 WITH LLVM-exception",
|
|
||||||
"MPL-2.0",
|
|
||||||
]
|
]
|
||||||
|
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
|
confidence-threshold = 0.8
|
||||||
|
|
||||||
[[licenses.clarify]]
|
[[licenses.clarify]]
|
||||||
|
@ -40,22 +42,30 @@ license-files = [
|
||||||
{ path = "LICENSE", hash = 0xbd0eed23 }
|
{ 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]
|
[bans]
|
||||||
multiple-versions = "deny"
|
multiple-versions = "deny"
|
||||||
highlight = "all"
|
highlight = "all"
|
||||||
wildcards = "allow"
|
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
|
# ignore duplicated crc dependency because ffv1 depends on an old version
|
||||||
# https://github.com/rust-av/ffv1/issues/21
|
# https://github.com/rust-av/ffv1/issues/21
|
||||||
[[bans.skip]]
|
[[bans.skip]]
|
||||||
|
@ -78,18 +88,28 @@ version = "0.9"
|
||||||
[[bans.skip]]
|
[[bans.skip]]
|
||||||
name = "hmac"
|
name = "hmac"
|
||||||
version = "0.11"
|
version = "0.11"
|
||||||
|
|
||||||
|
# ignore duplicated wasi dependency because various crates depends on an old version
|
||||||
[[bans.skip]]
|
[[bans.skip]]
|
||||||
name = "zerocopy"
|
name = "wasi"
|
||||||
version = "0.6"
|
version = "0.10"
|
||||||
|
|
||||||
|
# ignore duplicated spin dependency because various crates depend on an old version
|
||||||
[[bans.skip]]
|
[[bans.skip]]
|
||||||
name = "zerocopy-derive"
|
name = "spin"
|
||||||
version = "0.6"
|
version = "0.5"
|
||||||
|
|
||||||
|
# cookie_store depends on older idna
|
||||||
|
# https://github.com/pfernie/cookie_store/commit/b9c710f45550c5c8997f18a83e6fcc5998cf1726
|
||||||
[[bans.skip]]
|
[[bans.skip]]
|
||||||
name = "multimap"
|
name = "idna"
|
||||||
version = "0.8"
|
version = "0.2"
|
||||||
|
|
||||||
|
# image depends on older gif
|
||||||
|
# https://github.com/image-rs/image/pull/1826
|
||||||
[[bans.skip]]
|
[[bans.skip]]
|
||||||
name = "nix"
|
name = "gif"
|
||||||
version = "0.23"
|
version = "0.11"
|
||||||
|
|
||||||
# field-offset and nix depend on an older memoffset
|
# field-offset and nix depend on an older memoffset
|
||||||
# https://github.com/Diggsey/rust-field-offset/pull/23
|
# https://github.com/Diggsey/rust-field-offset/pull/23
|
||||||
|
@ -104,207 +124,23 @@ name = "hermit-abi"
|
||||||
version = "0.1"
|
version = "0.1"
|
||||||
[[bans.skip]]
|
[[bans.skip]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.3"
|
version = "0.2"
|
||||||
|
|
||||||
# Various crates depend on an older version of base64
|
# Various crates depend on an older version of base64
|
||||||
[[bans.skip]]
|
[[bans.skip]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.13"
|
version = "0.13"
|
||||||
|
|
||||||
|
# Various crates depend on an older version of windows-sys
|
||||||
[[bans.skip]]
|
[[bans.skip]]
|
||||||
name = "base64"
|
name = "windows-sys"
|
||||||
version = "0.21"
|
version = "0.42"
|
||||||
|
|
||||||
# Various crates depend on an older version of socket2
|
# Various crates depend on an older version of socket2
|
||||||
[[bans.skip]]
|
[[bans.skip]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.4"
|
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"
|
|
||||||
|
|
||||||
# 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"
|
|
||||||
[[bans.skip]]
|
|
||||||
name = "system-configuration"
|
|
||||||
version = "0.5"
|
|
||||||
[[bans.skip]]
|
|
||||||
name = "system-configuration-sys"
|
|
||||||
version = "0.5"
|
|
||||||
|
|
||||||
# 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"
|
|
||||||
|
|
||||||
# various crates depend on an older version of windows-sys
|
|
||||||
[[bans.skip]]
|
|
||||||
name = "windows-sys"
|
|
||||||
version = "0.52"
|
|
||||||
|
|
||||||
# derived-into-owned (via pcap-file) depends on old syn / quote
|
|
||||||
[[bans.skip]]
|
|
||||||
name = "syn"
|
|
||||||
version = "0.11"
|
|
||||||
[[bans.skip]]
|
|
||||||
name = "quote"
|
|
||||||
version = "0.3"
|
|
||||||
|
|
||||||
# dav1d depends on old system-deps which depends on old cfg-expr
|
|
||||||
[[bans.skip]]
|
|
||||||
name = "cfg-expr"
|
|
||||||
version = "0.15"
|
|
||||||
|
|
||||||
# backtrace and png depend on old miniz_oxide
|
|
||||||
[[bans.skip]]
|
|
||||||
name = "miniz_oxide"
|
|
||||||
version = "0.7"
|
|
||||||
|
|
||||||
# tokio-rustls via warp depends on old rustls
|
|
||||||
[[bans.skip]]
|
|
||||||
name = "rustls"
|
|
||||||
version = "0.22"
|
|
||||||
|
|
||||||
# aws-smithy-runtime depends on old tokio-rustls
|
|
||||||
[[bans.skip]]
|
|
||||||
name = "tokio-rustls"
|
|
||||||
version = "0.24"
|
|
||||||
|
|
||||||
[sources]
|
[sources]
|
||||||
unknown-registry = "deny"
|
unknown-registry = "deny"
|
||||||
unknown-git = "deny"
|
unknown-git = "deny"
|
||||||
|
|
|
@ -19,8 +19,6 @@ except ImportError:
|
||||||
PARSER = ArgumentParser()
|
PARSER = ArgumentParser()
|
||||||
PARSER.add_argument('src_dir', type=Path)
|
PARSER.add_argument('src_dir', type=Path)
|
||||||
PARSER.add_argument('plugins', nargs='*')
|
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.
|
# Map plugin name to directory name, for those that does not match.
|
||||||
|
@ -29,7 +27,6 @@ RENAMES = {
|
||||||
'rsfile': 'file',
|
'rsfile': 'file',
|
||||||
'rsflv': 'flavors',
|
'rsflv': 'flavors',
|
||||||
'rsrtp': 'rtp',
|
'rsrtp': 'rtp',
|
||||||
'rsrtsp': 'rtsp',
|
|
||||||
'rswebp': 'webp',
|
'rswebp': 'webp',
|
||||||
'rsonvif': 'onvif',
|
'rsonvif': 'onvif',
|
||||||
'rstracers': 'tracers',
|
'rstracers': 'tracers',
|
||||||
|
@ -37,78 +34,31 @@ RENAMES = {
|
||||||
'rswebrtc': 'webrtc',
|
'rswebrtc': 'webrtc',
|
||||||
'rspng': 'png',
|
'rspng': 'png',
|
||||||
'rsvideofx': 'videofx',
|
'rsvideofx': 'videofx',
|
||||||
'rsinter': 'inter',
|
|
||||||
'textahead': 'ahead',
|
'textahead': 'ahead',
|
||||||
'textwrap': 'wrap',
|
'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__":
|
if __name__ == "__main__":
|
||||||
analyzer = CargoAnalyzer()
|
opts = PARSER.parse_args()
|
||||||
opts = PARSER.parse_args(namespace=analyzer)
|
|
||||||
|
|
||||||
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))
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
build_hotdoc = false
|
build_hotdoc = false
|
||||||
|
|
||||||
if get_option('doc').disabled()
|
|
||||||
subdir_done()
|
|
||||||
endif
|
|
||||||
|
|
||||||
if meson.is_cross_build()
|
if meson.is_cross_build()
|
||||||
if get_option('doc').enabled()
|
if get_option('doc').enabled()
|
||||||
error('Documentation enabled but building the doc while cross building is not supported yet.')
|
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',
|
gst_index: 'plugins/index.md',
|
||||||
include_paths: join_paths(meson.current_source_dir(), '..'),
|
include_paths: join_paths(meson.current_source_dir(), '..'),
|
||||||
gst_smart_index: true,
|
gst_smart_index: true,
|
||||||
gst_c_source_filters: [
|
|
||||||
'../target/*/*.rs',
|
|
||||||
'../target/*/*/*.rs',
|
|
||||||
'../target/*/*/*/*.rs',
|
|
||||||
'../target/*/*/*/*/*.rs',
|
|
||||||
'../target/*/*/*/*/*/*.rs',
|
|
||||||
],
|
|
||||||
gst_c_sources: [
|
gst_c_sources: [
|
||||||
'../*/*/*/*.rs',
|
'../*/*/*/*.rs',
|
||||||
'../*/*/*/*/*.rs',
|
'../*/*/*/*/*.rs',
|
||||||
'../*/*/*/*/*/*.rs',
|
|
||||||
],
|
],
|
||||||
dependencies: [gst_dep],
|
dependencies: [gst_dep],
|
||||||
gst_order_generated_subpages: true,
|
gst_order_generated_subpages: true,
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,18 +1,18 @@
|
||||||
[package]
|
[package]
|
||||||
name = "gst-plugin-file"
|
name = "gst-plugin-file"
|
||||||
version.workspace = true
|
version = "0.10.3"
|
||||||
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
|
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"
|
license = "MIT OR Apache-2.0"
|
||||||
description = "GStreamer Rust File Source/Sink Plugin"
|
description = "GStreamer Rust File Source/Sink Plugin"
|
||||||
edition.workspace = true
|
edition = "2021"
|
||||||
rust-version.workspace = true
|
rust-version = "1.63"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
url = "2"
|
url = "2"
|
||||||
gst.workspace = true
|
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
|
||||||
gst-base.workspace = true
|
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.20", version = "0.20" }
|
||||||
once_cell.workspace = true
|
once_cell = "1.0"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "gstrsfile"
|
name = "gstrsfile"
|
||||||
|
@ -20,7 +20,7 @@ crate-type = ["cdylib", "rlib"]
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
gst-plugin-version-helper.workspace = true
|
gst-plugin-version-helper = { path="../../version-helper", version = "0.7" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
static = []
|
static = []
|
||||||
|
@ -28,7 +28,7 @@ capi = []
|
||||||
doc = ["gst/v1_18"]
|
doc = ["gst/v1_18"]
|
||||||
|
|
||||||
[package.metadata.capi]
|
[package.metadata.capi]
|
||||||
min_version = "0.9.21"
|
min_version = "0.8.0"
|
||||||
|
|
||||||
[package.metadata.capi.header]
|
[package.metadata.capi.header]
|
||||||
enabled = false
|
enabled = false
|
||||||
|
@ -36,7 +36,6 @@ enabled = false
|
||||||
[package.metadata.capi.library]
|
[package.metadata.capi.library]
|
||||||
install_subdir = "gstreamer-1.0"
|
install_subdir = "gstreamer-1.0"
|
||||||
versioning = false
|
versioning = false
|
||||||
import_library = false
|
|
||||||
|
|
||||||
[package.metadata.capi.pkg_config]
|
[package.metadata.capi.pkg_config]
|
||||||
requires_private = "gstreamer-1.0, gstreamer-base-1.0, gobject-2.0, glib-2.0, gmodule-2.0"
|
requires_private = "gstreamer-1.0, gstreamer-base-1.0, gobject-2.0, glib-2.0, gmodule-2.0"
|
||||||
|
|
|
@ -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
1
generic/file/LICENSE-APACHE
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../LICENSE-APACHE
|
|
@ -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
1
generic/file/LICENSE-MIT
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../LICENSE-MIT
|
|
@ -13,7 +13,6 @@
|
||||||
use gst::glib;
|
use gst::glib;
|
||||||
use gst::prelude::*;
|
use gst::prelude::*;
|
||||||
use gst::subclass::prelude::*;
|
use gst::subclass::prelude::*;
|
||||||
use gst_base::prelude::*;
|
|
||||||
use gst_base::subclass::prelude::*;
|
use gst_base::subclass::prelude::*;
|
||||||
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
@ -39,14 +38,15 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
enum State {
|
enum State {
|
||||||
#[default]
|
|
||||||
Stopped,
|
Stopped,
|
||||||
Started {
|
Started { file: File, position: u64 },
|
||||||
file: File,
|
}
|
||||||
position: u64,
|
|
||||||
},
|
impl Default for State {
|
||||||
|
fn default() -> State {
|
||||||
|
State::Stopped
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -81,20 +81,20 @@ impl FileSink {
|
||||||
Some(ref location_cur) => {
|
Some(ref location_cur) => {
|
||||||
gst::info!(
|
gst::info!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = self,
|
imp: self,
|
||||||
"Changing `location` from {:?} to {}",
|
"Changing `location` from {:?} to {}",
|
||||||
location_cur,
|
location_cur,
|
||||||
location,
|
location,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
gst::info!(CAT, imp = self, "Setting `location` to {}", location,);
|
gst::info!(CAT, imp: self, "Setting `location` to {}", location,);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(location)
|
Some(location)
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
gst::info!(CAT, imp = self, "Resetting `location` to None",);
|
gst::info!(CAT, imp: self, "Resetting `location` to None",);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -112,12 +112,6 @@ impl ObjectSubclass for FileSink {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectImpl for FileSink {
|
impl ObjectImpl for FileSink {
|
||||||
fn constructed(&self) {
|
|
||||||
self.parent_constructed();
|
|
||||||
|
|
||||||
self.obj().set_sync(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn properties() -> &'static [glib::ParamSpec] {
|
fn properties() -> &'static [glib::ParamSpec] {
|
||||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||||
vec![glib::ParamSpecString::builder("location")
|
vec![glib::ParamSpecString::builder("location")
|
||||||
|
@ -140,12 +134,7 @@ impl ObjectImpl for FileSink {
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(err) = res {
|
if let Err(err) = res {
|
||||||
gst::error!(
|
gst::error!(CAT, imp: self, "Failed to set property `location`: {}", err);
|
||||||
CAT,
|
|
||||||
imp = self,
|
|
||||||
"Failed to set property `location`: {}",
|
|
||||||
err
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
|
@ -227,10 +216,10 @@ impl BaseSinkImpl for FileSink {
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
gst::debug!(CAT, imp = self, "Opened file {:?}", file);
|
gst::debug!(CAT, imp: self, "Opened file {:?}", file);
|
||||||
|
|
||||||
*state = State::Started { file, position: 0 };
|
*state = State::Started { file, position: 0 };
|
||||||
gst::info!(CAT, imp = self, "Started");
|
gst::info!(CAT, imp: self, "Started");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -245,7 +234,7 @@ impl BaseSinkImpl for FileSink {
|
||||||
}
|
}
|
||||||
|
|
||||||
*state = State::Stopped;
|
*state = State::Stopped;
|
||||||
gst::info!(CAT, imp = self, "Stopped");
|
gst::info!(CAT, imp: self, "Stopped");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -265,7 +254,7 @@ impl BaseSinkImpl for FileSink {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
gst::trace!(CAT, imp = self, "Rendering {:?}", buffer);
|
gst::trace!(CAT, imp: self, "Rendering {:?}", buffer);
|
||||||
let map = buffer.map_readable().map_err(|_| {
|
let map = buffer.map_readable().map_err(|_| {
|
||||||
gst::element_imp_error!(self, gst::CoreError::Failed, ["Failed to map buffer"]);
|
gst::element_imp_error!(self, gst::CoreError::Failed, ["Failed to map buffer"]);
|
||||||
gst::FlowError::Error
|
gst::FlowError::Error
|
||||||
|
|
|
@ -23,7 +23,7 @@ pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
gst::Element::register(
|
gst::Element::register(
|
||||||
Some(plugin),
|
Some(plugin),
|
||||||
"rsfilesink",
|
"rsfilesink",
|
||||||
gst::Rank::NONE,
|
gst::Rank::None,
|
||||||
FileSink::static_type(),
|
FileSink::static_type(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,14 +38,15 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
enum State {
|
enum State {
|
||||||
#[default]
|
|
||||||
Stopped,
|
Stopped,
|
||||||
Started {
|
Started { file: File, position: u64 },
|
||||||
file: File,
|
}
|
||||||
position: u64,
|
|
||||||
},
|
impl Default for State {
|
||||||
|
fn default() -> State {
|
||||||
|
State::Stopped
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -94,20 +95,20 @@ impl FileSrc {
|
||||||
Some(ref location_cur) => {
|
Some(ref location_cur) => {
|
||||||
gst::info!(
|
gst::info!(
|
||||||
CAT,
|
CAT,
|
||||||
imp = self,
|
imp: self,
|
||||||
"Changing `location` from {:?} to {}",
|
"Changing `location` from {:?} to {}",
|
||||||
location_cur,
|
location_cur,
|
||||||
location,
|
location,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
gst::info!(CAT, imp = self, "Setting `location to {}", location,);
|
gst::info!(CAT, imp: self, "Setting `location to {}", location,);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(location)
|
Some(location)
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
gst::info!(CAT, imp = self, "Resetting `location` to None",);
|
gst::info!(CAT, imp: self, "Resetting `location` to None",);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -148,12 +149,7 @@ impl ObjectImpl for FileSrc {
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(err) = res {
|
if let Err(err) = res {
|
||||||
gst::error!(
|
gst::error!(CAT, imp: self, "Failed to set property `location`: {}", err);
|
||||||
CAT,
|
|
||||||
imp = self,
|
|
||||||
"Failed to set property `location`: {}",
|
|
||||||
err
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
|
@ -255,11 +251,11 @@ impl BaseSrcImpl for FileSrc {
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
gst::debug!(CAT, imp = self, "Opened file {:?}", file);
|
gst::debug!(CAT, imp: self, "Opened file {:?}", file);
|
||||||
|
|
||||||
*state = State::Started { file, position: 0 };
|
*state = State::Started { file, position: 0 };
|
||||||
|
|
||||||
gst::info!(CAT, imp = self, "Started");
|
gst::info!(CAT, imp: self, "Started");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -275,7 +271,7 @@ impl BaseSrcImpl for FileSrc {
|
||||||
|
|
||||||
*state = State::Stopped;
|
*state = State::Stopped;
|
||||||
|
|
||||||
gst::info!(CAT, imp = self, "Stopped");
|
gst::info!(CAT, imp: self, "Stopped");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
gst::Element::register(
|
gst::Element::register(
|
||||||
Some(plugin),
|
Some(plugin),
|
||||||
"rsfilesrc",
|
"rsfilesrc",
|
||||||
gst::Rank::NONE,
|
gst::Rank::None,
|
||||||
FileSrc::static_type(),
|
FileSrc::static_type(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.workspace = true
|
|
||||||
|
|
||||||
[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"
|
|
|
@ -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.
|
|
|
@ -1,3 +0,0 @@
|
||||||
fn main() {
|
|
||||||
gst_plugin_version_helper::info()
|
|
||||||
}
|
|
|
@ -1,897 +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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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(())
|
|
||||||
}
|
|
|
@ -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")
|
|
||||||
);
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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"
|
|
|
@ -1,5 +0,0 @@
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
gst_plugin_version_helper::info()
|
|
||||||
}
|
|
|
@ -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(())
|
|
||||||
}
|
|
|
@ -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(())
|
|
||||||
}
|
|
|
@ -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")
|
|
||||||
);
|
|
|
@ -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 {}
|
|
|
@ -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(),
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -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 {}
|
|
|
@ -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(),
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -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"
|
|
|
@ -1,3 +0,0 @@
|
||||||
fn main() {
|
|
||||||
gst_plugin_version_helper::info()
|
|
||||||
}
|
|
|
@ -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")
|
|
||||||
);
|
|
|
@ -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);
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue