mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2024-06-01 19:11:07 +00:00
Compare commits
439 commits
main
...
gstreamer-
Author | SHA1 | Date | |
---|---|---|---|
a84bbc66f3 | |||
d0ea7587d2 | |||
289fdaf490 | |||
ea93b8896d | |||
32cb2877fd | |||
2d796e5108 | |||
0f1fde5d0a | |||
43b9aab88e | |||
de64699eaa | |||
e782fd14c2 | |||
3bc6f7c614 | |||
90342ff90e | |||
0e9d33b38b | |||
231389a990 | |||
3cfb28d048 | |||
e3190c888a | |||
c03e108487 | |||
7bb277884b | |||
9d802b1969 | |||
630a1120ba | |||
55d2b9483c | |||
2136befbef | |||
11ea962f81 | |||
b81b2cee81 | |||
ee3a272859 | |||
f5d633d293 | |||
4e05fc5999 | |||
eff3618ebd | |||
9ffcb14617 | |||
8114f94f73 | |||
72506b94e3 | |||
dafdb48ff6 | |||
4f9d5b7a79 | |||
1eaac4e0a2 | |||
b8e891b5b7 | |||
474f00f8d6 | |||
98d839a920 | |||
f565875b6c | |||
873b91d7d5 | |||
395ef40fc9 | |||
ad97b21011 | |||
fa05a305b6 | |||
274e3cd71b | |||
ab84da6d32 | |||
7a70863152 | |||
4c4aff3a5b | |||
d35fc2eb6d | |||
8f97d691e1 | |||
7d2e849bbe | |||
7bb6f0265c | |||
beb524e950 | |||
ac3ca76c9f | |||
3627e52673 | |||
19597b3737 | |||
8355f93f5f | |||
ecabf02b1b | |||
2aacc74a44 | |||
d2e5cb43cc | |||
29cbfbf970 | |||
9e46f6c411 | |||
98b104dc7b | |||
fe333063ab | |||
0bb381aad9 | |||
32a730f6fa | |||
1d52139e35 | |||
c9c49dc54c | |||
2e4135ceff | |||
755f021a30 | |||
4da3bbf1d3 | |||
c261dd4445 | |||
2a504bbf17 | |||
04136b6cc1 | |||
4fc905c9ea | |||
581787f651 | |||
2151df4d70 | |||
94ab108069 | |||
e92084885c | |||
390e7ff47a | |||
31ad311d7f | |||
d6f42a2037 | |||
379d3de31c | |||
aa4ba44794 | |||
1ccc93df07 | |||
257a0de483 | |||
17f7b04b82 | |||
0205aa43d6 | |||
36cdf84655 | |||
38cfc72a72 | |||
96b595d942 | |||
b6168c7255 | |||
b4b2ca9a82 | |||
fc75502ee4 | |||
9854b299a2 | |||
bdcdbfeaaf | |||
482b7e1469 | |||
eda38a637e | |||
081a90fefc | |||
4b0330f680 | |||
6eaeb2e127 | |||
774dda249e | |||
01f96ca40c | |||
fafe52475f | |||
8fc469b8e0 | |||
c74def0b12 | |||
72941b5dc5 | |||
0954af10c7 | |||
dfe2442c92 | |||
84d6298729 | |||
82f3910453 | |||
460d6e1a74 | |||
55a6609fdb | |||
9f6b0d426c | |||
6f75243c8f | |||
68faccdacf | |||
aa1d89aa9f | |||
05b2caec74 | |||
c9b2c88469 | |||
530aab7af6 | |||
e64e12e478 | |||
3a98bacd39 | |||
e33543dbfa | |||
0285cb1339 | |||
9b97e68da3 | |||
9ae514f986 | |||
e8bd521154 | |||
60ae3fc0b9 | |||
2002c54582 | |||
8248425905 | |||
e9d32fb221 | |||
8ff2c6609c | |||
d2947ed1f3 | |||
df22c37268 | |||
0b65a2f8af | |||
482ff879a4 | |||
cc59ff9052 | |||
015edb7d37 | |||
e632d24dda | |||
5bbbd7bd23 | |||
25fce80de6 | |||
3c58f495ab | |||
adfceaea77 | |||
fc5ff9503d | |||
e31fd2dcc5 | |||
790383313d | |||
a434607515 | |||
57fe64b748 | |||
f4a565d4ea | |||
5462ed0e95 | |||
d67eb1d405 | |||
1b007178af | |||
e0a7c93d46 | |||
00d1c73ff1 | |||
2d56989f5c | |||
f7c81e78f5 | |||
25e9fc55b7 | |||
f56f40a943 | |||
c2d6273786 | |||
d38e2751cc | |||
e80068deca | |||
1219c27fa4 | |||
bd6b2e0fab | |||
fe15b06ab6 | |||
5c3b5842b0 | |||
8274be3c0b | |||
882e456b3e | |||
cb92c31aa9 | |||
c5dbacb6a7 | |||
b3b8d73ce1 | |||
2da4701b2a | |||
748b0530c4 | |||
35a1d8f504 | |||
6d248c70dd | |||
62cac113fb | |||
c9719e4231 | |||
01f0c0a330 | |||
4b867d27fe | |||
f2b03d3796 | |||
9a779607c7 | |||
ccfa25aa60 | |||
a0cfe054c8 | |||
e2c7e7ebe1 | |||
f5feb34fcb | |||
a0fe1aba5f | |||
fef81b1c97 | |||
e4c9ba43df | |||
e14777573f | |||
4aacf4d3ad | |||
0d3dc25414 | |||
ef7f0d12bf | |||
2723fc4713 | |||
e100506194 | |||
5c21b10841 | |||
8f612b9003 | |||
365dcfa730 | |||
611c7d6cd3 | |||
bfbde450db | |||
562b429388 | |||
09cffe0e70 | |||
eb3d3b3088 | |||
ee46b103b8 | |||
8a384aa8b2 | |||
01d3b0f9da | |||
58a6dbacca | |||
e52bb160c7 | |||
19c527a9c5 | |||
fbf50b395d | |||
9d67753fd6 | |||
07adcf5ecf | |||
35552dc73c | |||
1e67259462 | |||
cccf90f59e | |||
fffecca624 | |||
f96b64e1c1 | |||
c805c3bb3a | |||
5f70c0f5fe | |||
da7743d2e7 | |||
dae1d8b5ef | |||
7ba1100a92 | |||
a65feb7ef9 | |||
1029669427 | |||
40cada5f69 | |||
0e55e19d57 | |||
7a8ecb5343 | |||
17dec1cb26 | |||
ba0904630d | |||
af0e6281d2 | |||
e79221f386 | |||
dc47b35536 | |||
3520fc67de | |||
402d96b80c | |||
68bec4a0db | |||
adbb8b6495 | |||
a01437b675 | |||
b9e203d6c1 | |||
f3b8288ef9 | |||
4eeb8ffb63 | |||
01aa9380d4 | |||
d9e9468f9a | |||
59f575888e | |||
74a40060ce | |||
5c2582d105 | |||
4b9392938f | |||
407a367529 | |||
88a437ac32 | |||
853acfc4fe | |||
3cd6074a8e | |||
ab5ee0511b | |||
4ba452dcc3 | |||
711313c4c5 | |||
c83f48f0a1 | |||
101bcbc1a0 | |||
2a68be2000 | |||
3ea77d7a74 | |||
c818a575b4 | |||
43e5bd7b3a | |||
c6158b7a4e | |||
27f5b5cc33 | |||
d02508a7d0 | |||
df3b90881f | |||
53ae335d22 | |||
c8e8af3e81 | |||
8e0fc8b063 | |||
cc8da54adb | |||
e213ba9618 | |||
e8df0a0cb7 | |||
408d439631 | |||
2f623e15c2 | |||
2a8a90f76f | |||
cd3e333a0c | |||
1bfe6f9142 | |||
db9ef0b2af | |||
85a03f5ff0 | |||
4b936950c2 | |||
698ab100b3 | |||
517dc286d0 | |||
514a8e48ef | |||
ff1c99df98 | |||
696944c08e | |||
37dedfd4d0 | |||
bb2f632c9c | |||
f6b092d2af | |||
cd5a93dc09 | |||
a5a3c44951 | |||
a0dbb94e01 | |||
7013416a39 | |||
9f8fa99089 | |||
34434bd877 | |||
d67baa7668 | |||
8cdb30bd39 | |||
efc07cecf7 | |||
7db53aba22 | |||
2045847bd6 | |||
f4cb4b9da6 | |||
b0bd55c4d2 | |||
b9e6c817b7 | |||
e95a2c1016 | |||
31760b8f9a | |||
8d7ce380c4 | |||
e8701652e2 | |||
993619d654 | |||
d9d5571641 | |||
ba889c143c | |||
4b95bde38f | |||
041f51c4bb | |||
161c6db641 | |||
deeff67f94 | |||
568c2be582 | |||
548fe54ba9 | |||
a1afef2207 | |||
778c4da27e | |||
cbc99fb198 | |||
b701003352 | |||
5f9645bb74 | |||
bae5294e8f | |||
19957d1d23 | |||
bc9408840f | |||
08668a4bbb | |||
fb745f077b | |||
1eea2219c6 | |||
9b2d9ba4f9 | |||
00615ab478 | |||
6ceccac1be | |||
6596b6cdd1 | |||
b5641d838e | |||
fffd7dc542 | |||
b4185134d1 | |||
dc7e3c9f28 | |||
5f6afce842 | |||
7b1ee9f948 | |||
b6c9c14ccf | |||
3936211b55 | |||
2a981132b4 | |||
a1fd847f70 | |||
71558bd086 | |||
b689a0825e | |||
b3e33e329b | |||
8c27aefe76 | |||
44ec9eba7f | |||
fd5b31fb43 | |||
d79edce517 | |||
412c191fc2 | |||
e46d2dfa54 | |||
e4788662b9 | |||
cab5410782 | |||
8ac5632561 | |||
4f67623c22 | |||
1d4d9b3bdb | |||
3202c4dc39 | |||
3bc9df7e71 | |||
929c48e19a | |||
4e9ec324e1 | |||
3fc0326084 | |||
420716fb63 | |||
baf3da86cc | |||
2e529fa152 | |||
60772d2c06 | |||
184f879bf7 | |||
b2ad89cf06 | |||
506c96e8aa | |||
2227b41342 | |||
e6789fc338 | |||
b7534643be | |||
0b2aa2646f | |||
507377c052 | |||
f590b7e62f | |||
9fa3d88a63 | |||
b8d2d98027 | |||
bfe62488f4 | |||
922f14ea19 | |||
204e9af663 | |||
a5f48507c4 | |||
7df114e0e9 | |||
a7c75f8066 | |||
08799d242c | |||
a59a0340cf | |||
cadf36ff01 | |||
6a05b7f56a | |||
1f4a035dc0 | |||
b41d1e3f34 | |||
649434bd04 | |||
10813ed621 | |||
ea0d5751a2 | |||
c771c86631 | |||
516b561191 | |||
23e8fea170 | |||
81a46ee33d | |||
e7f5e73e3f | |||
969be7ab52 | |||
931917e559 | |||
1fb0062059 | |||
93ba677b18 | |||
9491c77540 | |||
6c15bba592 | |||
2b287bcd61 | |||
582cc34895 | |||
8bd9de8d48 | |||
b015688447 | |||
274e57a536 | |||
e434fd19ca | |||
28065de413 | |||
a6f64b5b20 | |||
5295fe9e67 | |||
331d053516 | |||
5c9bc03eab | |||
8c454c5c37 | |||
a9f3ff2925 | |||
cdf07dd860 | |||
2e52fece61 | |||
01816e2a8a | |||
ea82881e1c | |||
429e545e5c | |||
2e3373647a | |||
43ac186e69 | |||
1ef9a46508 | |||
b7891e77e5 | |||
bdb423e2b9 | |||
4556657602 | |||
cb5a956ee7 | |||
d0228ed544 | |||
6b3f0f764e | |||
07f3b0f504 | |||
8dc22d3bf1 | |||
f2f0eb30e0 | |||
b596b407f6 | |||
2b6d87cf66 | |||
18fa678a5c | |||
8e2a6500aa | |||
e268577994 | |||
f2a6a8d3de | |||
51ff099221 | |||
eefa8540ba | |||
790453364d | |||
32d2372e90 | |||
15955758b6 | |||
54fe3f1c02 | |||
a54318fbb4 | |||
46152533ba | |||
ba5270d30a | |||
2ff40142db |
143
.gitlab-ci.yml
143
.gitlab-ci.yml
|
@ -6,7 +6,7 @@ include:
|
|||
file: '/templates/debian.yml'
|
||||
|
||||
- project: 'gstreamer/gstreamer-rs'
|
||||
ref: main
|
||||
ref: '0.19'
|
||||
file: '/ci/images_template.yml'
|
||||
|
||||
- project: 'gstreamer/gstreamer'
|
||||
|
@ -38,10 +38,33 @@ default:
|
|||
interruptible: true
|
||||
|
||||
stages:
|
||||
- "trigger"
|
||||
- "prep"
|
||||
- "lint"
|
||||
- "test"
|
||||
- "extras"
|
||||
- "integration"
|
||||
|
||||
# This is an empty job that is used to trigger the pipeline.
|
||||
trigger:
|
||||
image: alpine:latest
|
||||
stage: 'trigger'
|
||||
variables:
|
||||
GIT_STRATEGY: none
|
||||
script:
|
||||
- echo "Trigger job done, now running the pipeline."
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "schedule"
|
||||
# If the MR is assigned to the Merge bot, trigger the pipeline automatically
|
||||
- if: '$CI_MERGE_REQUEST_ASSIGNEES == "gstreamer-merge-bot"'
|
||||
# Require explicit action to trigger tests post merge
|
||||
- if: '$CI_PROJECT_NAMESPACE == "gstreamer" && $CI_COMMIT_BRANCH == "main"'
|
||||
when: 'manual'
|
||||
# When the assignee isn't the merge bot, require an explicit action to trigger the pipeline
|
||||
# to avoid wasting CI resources
|
||||
- if: '$CI_MERGE_REQUEST_ASSIGNEES != "gstreamer-merge-bot"'
|
||||
when: 'manual'
|
||||
allow_failure: false
|
||||
|
||||
.debian:11:
|
||||
variables:
|
||||
|
@ -49,30 +72,23 @@ stages:
|
|||
before_script:
|
||||
- source ./ci/env.sh
|
||||
- mkdir .cargo && echo -e "[net]\ngit-fetch-with-cli = true" > .cargo/config
|
||||
# If cargo exists assume we probably will want to update
|
||||
# the lockfile
|
||||
- |
|
||||
if command -v cargo; then
|
||||
cargo generate-lockfile
|
||||
cargo update
|
||||
fi
|
||||
|
||||
.debian:11-stable:
|
||||
extends: .debian:11
|
||||
variables:
|
||||
FDO_DISTRIBUTION_TAG: '$GST_RS_STABLE-${GST_RS_IMG_TAG}_2022-09-07.0'
|
||||
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-09-07.0'
|
||||
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-09-07.0'
|
||||
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:
|
||||
|
@ -86,6 +102,8 @@ stages:
|
|||
apt clean &&
|
||||
bash ./ci/install-rust-ext.sh &&
|
||||
pip install tomli
|
||||
needs:
|
||||
- "trigger"
|
||||
rules:
|
||||
- if: '$UPDATE_IMG == null'
|
||||
|
||||
|
@ -230,7 +248,7 @@ meson shared:
|
|||
meson static:
|
||||
extends: .meson
|
||||
script:
|
||||
- meson build --default-library=static --prefix=$(pwd)/install -Dsodium=built-in
|
||||
- meson build --default-library=static --prefix=$(pwd)/install -Dsodium-source=built-in
|
||||
- ninja -C build install
|
||||
- ./ci/generate-static-test.py test-static-link-all
|
||||
- cd test-static-link-all
|
||||
|
@ -246,10 +264,10 @@ meson static:
|
|||
# Check that the gstreamer documentation keeps working
|
||||
documentation:
|
||||
image: $GSTREAMER_DOC_IMAGE
|
||||
stage: 'integration'
|
||||
variables:
|
||||
MESON_ARGS: >
|
||||
-Ddoc=enabled
|
||||
-Domx=disabled
|
||||
-Dpython=disabled
|
||||
-Dlibav=disabled
|
||||
-Dlibnice=disabled
|
||||
|
@ -258,7 +276,7 @@ documentation:
|
|||
-Dsharp=disabled
|
||||
-Dgst-examples=disabled
|
||||
-Drs=enabled
|
||||
-Dgst-plugins-rs:sodium=system
|
||||
-Dgst-plugins-rs:sodium-source=system
|
||||
-Dgst-docs:fatal_warnings=true
|
||||
-Dorc=disabled
|
||||
script:
|
||||
|
@ -279,54 +297,65 @@ documentation:
|
|||
paths:
|
||||
- documentation/
|
||||
needs: []
|
||||
rules:
|
||||
# Run job if the MR is assigned to the Merge bot or it a post-merge pipeline on main branch
|
||||
- if: '$CI_MERGE_REQUEST_ASSIGNEES == "gstreamer-merge-bot"'
|
||||
when: 'always'
|
||||
- if: '$CI_PROJECT_NAMESPACE == "gstreamer" && $CI_COMMIT_BRANCH == "main"'
|
||||
when: 'always'
|
||||
# Require explicit action to trigger otherwise
|
||||
- if: '$CI_PROJECT_NAMESPACE != "gstreamer" || $CI_COMMIT_BRANCH != "main"'
|
||||
when: 'manual'
|
||||
|
||||
# build gst-plugins-rs as a gst-build subproject
|
||||
gst-build:
|
||||
extends: .meson
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
when: 'manual'
|
||||
allow_failure: true
|
||||
variables:
|
||||
MESON_ARGS: >
|
||||
-Domx=disabled
|
||||
-Dpython=disabled
|
||||
-Dlibav=disabled
|
||||
-Dlibnice=disabled
|
||||
-Dugly=disabled
|
||||
-Dbad=disabled
|
||||
-Ddevtools=disabled
|
||||
-Dges=disabled
|
||||
-Drtsp_server=disabled
|
||||
-Dvaapi=disabled
|
||||
-Dsharp=disabled
|
||||
-Dgst-examples=disabled
|
||||
-Drs=enabled
|
||||
-Dgst-plugins-rs:sodium=system
|
||||
script:
|
||||
- P=$(pwd)
|
||||
- cd ..
|
||||
- rm -rf gstreamer
|
||||
- git clone --depth 1 https://gitlab.freedesktop.org/gstreamer/gstreamer.git --branch main
|
||||
- cd gstreamer
|
||||
- ln -s $P subprojects/gst-plugins-rs
|
||||
- meson build $MESON_ARGS
|
||||
- ninja -C build
|
||||
# Check static Rust plugins can be linked into gst-full
|
||||
- meson build-gst-full --default-library=static $MESON_ARGS
|
||||
- ninja -C build-gst-full
|
||||
- meson devenv -C build-gst-full ./gst-inspect-1.0 rsaudiofx
|
||||
artifacts:
|
||||
expire_in: '7 days'
|
||||
when: always
|
||||
paths:
|
||||
- 'build/meson-logs/'
|
||||
- 'build-gst-full/meson-logs/'
|
||||
# Disabled because of https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/262
|
||||
#gst-build:
|
||||
# extends: .meson
|
||||
# rules:
|
||||
# - if: '$CI_PIPELINE_SOURCE == "schedule"'
|
||||
# - if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
# when: 'manual'
|
||||
# allow_failure: true
|
||||
# variables:
|
||||
# MESON_ARGS: >
|
||||
# -Domx=disabled
|
||||
# -Dpython=disabled
|
||||
# -Dlibav=disabled
|
||||
# -Dlibnice=disabled
|
||||
# -Dugly=disabled
|
||||
# -Dbad=disabled
|
||||
# -Ddevtools=disabled
|
||||
# -Dges=disabled
|
||||
# -Drtsp_server=disabled
|
||||
# -Dvaapi=disabled
|
||||
# -Dsharp=disabled
|
||||
# -Dgst-examples=disabled
|
||||
# -Drs=enabled
|
||||
# -Dgst-plugins-rs:sodium-source=system
|
||||
# script:
|
||||
# - P=$(pwd)
|
||||
# - cd ..
|
||||
# - rm -rf gstreamer
|
||||
# - git clone --depth 1 https://gitlab.freedesktop.org/gstreamer/gstreamer.git --branch main
|
||||
# - cd gstreamer
|
||||
# - ln -s $P subprojects/gst-plugins-rs
|
||||
# - meson build $MESON_ARGS
|
||||
# - ninja -C build
|
||||
# # Check static Rust plugins can be linked into gst-full
|
||||
# - meson build-gst-full --default-library=static $MESON_ARGS
|
||||
# - ninja -C build-gst-full
|
||||
# - meson devenv -C build-gst-full ./gst-inspect-1.0 rsaudiofx
|
||||
# artifacts:
|
||||
# expire_in: '7 days'
|
||||
# when: always
|
||||
# paths:
|
||||
# - 'build/meson-logs/'
|
||||
# - 'build-gst-full/meson-logs/'
|
||||
|
||||
.msvc2019 build:
|
||||
stage: 'test'
|
||||
needs: []
|
||||
needs:
|
||||
- 'trigger'
|
||||
tags:
|
||||
- 'docker'
|
||||
- 'windows'
|
||||
|
|
6471
Cargo.lock
generated
Normal file
6471
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -16,6 +16,7 @@ members = [
|
|||
|
||||
"mux/flavors",
|
||||
"mux/fmp4",
|
||||
"mux/mp4",
|
||||
|
||||
"net/aws",
|
||||
"net/hlssink3",
|
||||
|
@ -35,6 +36,7 @@ members = [
|
|||
"text/wrap",
|
||||
|
||||
"utils/fallbackswitch",
|
||||
"utils/livesync",
|
||||
"utils/togglerecord",
|
||||
"utils/tracers",
|
||||
"utils/uriplaylistbin",
|
||||
|
@ -63,6 +65,7 @@ default-members = [
|
|||
"generic/threadshare",
|
||||
|
||||
"mux/fmp4",
|
||||
"mux/mp4",
|
||||
|
||||
"net/aws",
|
||||
"net/hlssink3",
|
||||
|
@ -82,6 +85,7 @@ default-members = [
|
|||
"text/wrap",
|
||||
|
||||
"utils/fallbackswitch",
|
||||
"utils/livesync",
|
||||
"utils/togglerecord",
|
||||
"utils/tracers",
|
||||
"utils/uriplaylistbin",
|
||||
|
|
|
@ -110,6 +110,8 @@ You will find the following plugins in this repository:
|
|||
|
||||
- `fmp4`: A fragmented MP4/ISOBMFF/CMAF muxer for generating e.g. DASH/HLS media fragments.
|
||||
|
||||
- `mp4`: A non-fragmented MP4 muxer for generating MP4 files.
|
||||
|
||||
* `text`
|
||||
- `ahead`: A plugin to display upcoming text buffers ahead.
|
||||
|
||||
|
@ -127,6 +129,9 @@ You will find the following plugins in this repository:
|
|||
configuring a fallback audio/video if there are problems with the main
|
||||
source.
|
||||
|
||||
- `livesync`: Element to maintain a continuous live stream from a
|
||||
potentially unstable source.
|
||||
|
||||
- `togglerecord`: Element to enable starting and stopping multiple streams together.
|
||||
|
||||
- `tracers`: Plugin with multiple tracers:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "gst-plugin-audiofx"
|
||||
version = "0.9.0-alpha.1"
|
||||
version = "0.9.13"
|
||||
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
|
||||
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
|
||||
license = "MPL-2.0"
|
||||
|
@ -9,9 +9,9 @@ edition = "2021"
|
|||
rust-version = "1.63"
|
||||
|
||||
[dependencies]
|
||||
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["v1_16"] }
|
||||
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["v1_16"] }
|
||||
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["v1_16"] }
|
||||
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19.1", features = ["v1_16"] }
|
||||
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19", features = ["v1_16"] }
|
||||
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19", features = ["v1_16"] }
|
||||
anyhow = "1"
|
||||
byte-slice-cast = "1.0"
|
||||
num-traits = "0.2"
|
||||
|
@ -29,11 +29,11 @@ crate-type = ["cdylib", "rlib"]
|
|||
path = "src/lib.rs"
|
||||
|
||||
[dev-dependencies]
|
||||
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["v1_18"] }
|
||||
gst-app = { package = "gstreamer-app", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
||||
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19", features = ["v1_18"] }
|
||||
gst-app = { package = "gstreamer-app", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
|
||||
|
||||
[build-dependencies]
|
||||
gst-plugin-version-helper = { path="../../version-helper" }
|
||||
gst-plugin-version-helper = { version = "0.7", path="../../version-helper" }
|
||||
|
||||
[features]
|
||||
static = []
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
../../LICENSE-MPL-2.0
|
373
audio/audiofx/LICENSE-MPL-2.0
Normal file
373
audio/audiofx/LICENSE-MPL-2.0
Normal file
|
@ -0,0 +1,373 @@
|
|||
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.
|
|
@ -90,10 +90,10 @@ fn run() -> Result<(), Error> {
|
|||
let new_z = -x * f32::sin(ROTATION) + z * f32::cos(ROTATION);
|
||||
|
||||
let objs = [gst::Structure::builder("application/spatial-object")
|
||||
.field("x", &new_x)
|
||||
.field("y", &y)
|
||||
.field("z", &new_z)
|
||||
.field("distance-gain", &gain)
|
||||
.field("x", new_x)
|
||||
.field("y", y)
|
||||
.field("z", new_z)
|
||||
.field("distance-gain", gain)
|
||||
.build()];
|
||||
|
||||
hrtf.set_property("spatial-objects", gst::Array::new(objs));
|
||||
|
|
|
@ -15,8 +15,8 @@ pub struct RingBuffer {
|
|||
|
||||
impl RingBuffer {
|
||||
pub fn new(size: usize) -> Self {
|
||||
let mut buffer = Vec::with_capacity(size as usize);
|
||||
buffer.extend(iter::repeat(0.0).take(size as usize));
|
||||
let mut buffer = Vec::with_capacity(size);
|
||||
buffer.extend(iter::repeat(0.0).take(size));
|
||||
|
||||
Self {
|
||||
buffer: buffer.into_boxed_slice(),
|
||||
|
|
|
@ -611,10 +611,9 @@ impl State {
|
|||
// the position where we have to start writing the next 100ms in the next
|
||||
// iteration.
|
||||
|
||||
let mut outbuf = gst::Buffer::with_size(
|
||||
self.current_samples_per_frame as usize * self.info.bpf() as usize,
|
||||
)
|
||||
.map_err(|_| gst::FlowError::Error)?;
|
||||
let mut outbuf =
|
||||
gst::Buffer::with_size(self.current_samples_per_frame * self.info.bpf() as usize)
|
||||
.map_err(|_| gst::FlowError::Error)?;
|
||||
{
|
||||
let outbuf = outbuf.get_mut().unwrap();
|
||||
let mut dst = outbuf.map_writable().map_err(|_| gst::FlowError::Error)?;
|
||||
|
@ -819,7 +818,7 @@ impl State {
|
|||
// adjustment. frame_type should only ever be set to Final at the end if we ended up in
|
||||
// Inner state before.
|
||||
if self.frame_type == FrameType::First
|
||||
&& (src.len() / self.info.channels() as usize) < self.current_samples_per_frame as usize
|
||||
&& (src.len() / self.info.channels() as usize) < self.current_samples_per_frame
|
||||
{
|
||||
self.process_first_frame_is_last(imp)?;
|
||||
}
|
||||
|
@ -1560,7 +1559,7 @@ impl AudioLoudNorm {
|
|||
}
|
||||
|
||||
// Need to reset the state now
|
||||
*state = State::new(&*self.settings.lock().unwrap(), state.info.clone());
|
||||
*state = State::new(&self.settings.lock().unwrap(), state.info.clone());
|
||||
}
|
||||
|
||||
state.adapter.push(buffer);
|
||||
|
@ -1602,7 +1601,7 @@ impl AudioLoudNorm {
|
|||
Err(_) => return false,
|
||||
};
|
||||
}
|
||||
*state = Some(State::new(&*self.settings.lock().unwrap(), info));
|
||||
*state = Some(State::new(&self.settings.lock().unwrap(), info));
|
||||
drop(state);
|
||||
|
||||
if let Some(outbuf) = outbuf {
|
||||
|
@ -1623,7 +1622,7 @@ impl AudioLoudNorm {
|
|||
Err(gst::FlowError::Eos) => None,
|
||||
Err(_) => return false,
|
||||
};
|
||||
*state = State::new(&*self.settings.lock().unwrap(), state.info.clone());
|
||||
*state = State::new(&self.settings.lock().unwrap(), state.info.clone());
|
||||
}
|
||||
drop(state);
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use std::sync::Mutex;
|
||||
|
||||
use gst::glib;
|
||||
use gst::prelude::*;
|
||||
use gst::subclass::prelude::*;
|
||||
|
@ -31,8 +33,22 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
|||
)
|
||||
});
|
||||
|
||||
const DEFAULT_VOICE_ACTIVITY_THRESHOLD: f32 = 0.0;
|
||||
const FRAME_SIZE: usize = DenoiseState::FRAME_SIZE;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct Settings {
|
||||
vad_threshold: f32,
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Settings {
|
||||
vad_threshold: DEFAULT_VOICE_ACTIVITY_THRESHOLD,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ChannelDenoiser {
|
||||
denoiser: Box<DenoiseState<'static>>,
|
||||
frame_chunk: Box<[f32; FRAME_SIZE]>,
|
||||
|
@ -47,6 +63,7 @@ struct State {
|
|||
|
||||
#[derive(Default)]
|
||||
pub struct AudioRNNoise {
|
||||
settings: Mutex<Settings>,
|
||||
state: AtomicRefCell<Option<State>>,
|
||||
}
|
||||
|
||||
|
@ -82,43 +99,6 @@ impl State {
|
|||
fn needs_more_data(&self) -> bool {
|
||||
self.adapter.available() < (FRAME_SIZE * self.in_info.bpf() as usize)
|
||||
}
|
||||
|
||||
fn process(&mut self, input_plane: &[f32], output_plane: &mut [f32]) {
|
||||
let channels = self.in_info.channels() as usize;
|
||||
let size = FRAME_SIZE * channels;
|
||||
|
||||
for (out_frame, in_frame) in output_plane.chunks_mut(size).zip(input_plane.chunks(size)) {
|
||||
for (index, item) in in_frame.iter().enumerate() {
|
||||
let channel_index = index % channels;
|
||||
let channel_denoiser = &mut self.denoisers[channel_index];
|
||||
let pos = index / channels;
|
||||
channel_denoiser.frame_chunk[pos] = *item;
|
||||
}
|
||||
|
||||
for i in (in_frame.len() / channels)..(size / channels) {
|
||||
for c in 0..channels {
|
||||
let channel_denoiser = &mut self.denoisers[c];
|
||||
channel_denoiser.frame_chunk[i] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: The first chunks coming out of the denoisers contains some
|
||||
// fade-in artifacts. We might want to discard those.
|
||||
for channel_denoiser in &mut self.denoisers {
|
||||
channel_denoiser.denoiser.process_frame(
|
||||
&mut channel_denoiser.out_chunk[..],
|
||||
&channel_denoiser.frame_chunk[..],
|
||||
);
|
||||
}
|
||||
|
||||
for (index, item) in out_frame.iter_mut().enumerate() {
|
||||
let channel_index = index % channels;
|
||||
let channel_denoiser = &self.denoisers[channel_index];
|
||||
let pos = index / channels;
|
||||
*item = channel_denoiser.out_chunk[pos];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AudioRNNoise {
|
||||
|
@ -131,6 +111,7 @@ impl AudioRNNoise {
|
|||
return Ok(gst::FlowSuccess::Ok);
|
||||
}
|
||||
|
||||
let settings = *self.settings.lock().unwrap();
|
||||
let mut buffer = gst::Buffer::with_size(available).map_err(|e| {
|
||||
gst::error!(CAT, imp: self, "Failed to allocate buffer at EOS {:?}", e);
|
||||
gst::FlowError::Flushing
|
||||
|
@ -151,7 +132,7 @@ impl AudioRNNoise {
|
|||
let mut out_map = buffer.map_writable().map_err(|_| gst::FlowError::Error)?;
|
||||
let out_data = out_map.as_mut_slice_of::<f32>().unwrap();
|
||||
|
||||
state.process(in_data, out_data);
|
||||
self.process(state, &settings, in_data, out_data);
|
||||
}
|
||||
|
||||
self.obj().src_pad().push(buffer)
|
||||
|
@ -164,6 +145,7 @@ impl AudioRNNoise {
|
|||
let duration = state.buffer_duration(output_size as _);
|
||||
let pts = state.current_pts();
|
||||
|
||||
let settings = *self.settings.lock().unwrap();
|
||||
let mut buffer = gst::Buffer::with_size(output_size).map_err(|_| gst::FlowError::Error)?;
|
||||
|
||||
{
|
||||
|
@ -181,11 +163,64 @@ impl AudioRNNoise {
|
|||
let mut out_map = buffer.map_writable().map_err(|_| gst::FlowError::Error)?;
|
||||
let out_data = out_map.as_mut_slice_of::<f32>().unwrap();
|
||||
|
||||
state.process(in_data, out_data);
|
||||
self.process(state, &settings, in_data, out_data);
|
||||
}
|
||||
|
||||
Ok(GenerateOutputSuccess::Buffer(buffer))
|
||||
}
|
||||
|
||||
fn process(
|
||||
&self,
|
||||
state: &mut State,
|
||||
settings: &Settings,
|
||||
input_plane: &[f32],
|
||||
output_plane: &mut [f32],
|
||||
) {
|
||||
let channels = state.in_info.channels() as usize;
|
||||
let size = FRAME_SIZE * channels;
|
||||
|
||||
for (out_frame, in_frame) in output_plane.chunks_mut(size).zip(input_plane.chunks(size)) {
|
||||
for (index, item) in in_frame.iter().enumerate() {
|
||||
let channel_index = index % channels;
|
||||
let channel_denoiser = &mut state.denoisers[channel_index];
|
||||
let pos = index / channels;
|
||||
channel_denoiser.frame_chunk[pos] = *item * 32767.0;
|
||||
}
|
||||
|
||||
for i in (in_frame.len() / channels)..(size / channels) {
|
||||
for c in 0..channels {
|
||||
let channel_denoiser = &mut state.denoisers[c];
|
||||
channel_denoiser.frame_chunk[i] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: The first chunks coming out of the denoisers contains some
|
||||
// fade-in artifacts. We might want to discard those.
|
||||
let mut vad: f32 = 0.0;
|
||||
for channel_denoiser in &mut state.denoisers {
|
||||
vad = f32::max(
|
||||
vad,
|
||||
channel_denoiser.denoiser.process_frame(
|
||||
&mut channel_denoiser.out_chunk[..],
|
||||
&channel_denoiser.frame_chunk[..],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
gst::debug!(CAT, imp: self, "Voice activity: {}", vad);
|
||||
|
||||
if vad < settings.vad_threshold {
|
||||
out_frame.fill(0.0);
|
||||
} else {
|
||||
for (index, item) in out_frame.iter_mut().enumerate() {
|
||||
let channel_index = index % channels;
|
||||
let channel_denoiser = &state.denoisers[channel_index];
|
||||
let pos = index / channels;
|
||||
*item = channel_denoiser.out_chunk[pos] / 32767.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
|
@ -195,7 +230,42 @@ impl ObjectSubclass for AudioRNNoise {
|
|||
type ParentType = gst_base::BaseTransform;
|
||||
}
|
||||
|
||||
impl ObjectImpl for AudioRNNoise {}
|
||||
impl ObjectImpl for AudioRNNoise {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![glib::ParamSpecFloat::builder("voice-activity-threshold")
|
||||
.nick("Voice activity threshold")
|
||||
.blurb("Threshold of the voice activity detector below which to mute the output")
|
||||
.minimum(0.0)
|
||||
.maximum(1.0)
|
||||
.default_value(DEFAULT_VOICE_ACTIVITY_THRESHOLD)
|
||||
.mutable_playing()
|
||||
.build()]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"voice-activity-threshold" => {
|
||||
let mut settings = self.settings.lock().unwrap();
|
||||
settings.vad_threshold = value.get().expect("type checked upstream");
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
match pspec.name() {
|
||||
"voice-activity-threshold" => {
|
||||
let settings = self.settings.lock().unwrap();
|
||||
settings.vad_threshold.to_value()
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GstObjectImpl for AudioRNNoise {}
|
||||
|
||||
|
|
|
@ -138,7 +138,7 @@ impl ObjectImpl for EbuR128Level {
|
|||
.build()]
|
||||
});
|
||||
|
||||
&*SIGNALS
|
||||
&SIGNALS
|
||||
}
|
||||
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
|
@ -482,7 +482,7 @@ impl BaseTransformImpl for EbuR128Level {
|
|||
|
||||
if state.ebur128.mode().contains(ebur128::Mode::M) {
|
||||
match state.ebur128.loudness_momentary() {
|
||||
Ok(loudness) => s.set("momentary-loudness", &loudness),
|
||||
Ok(loudness) => s.set("momentary-loudness", loudness),
|
||||
Err(err) => gst::error!(
|
||||
CAT,
|
||||
imp: self,
|
||||
|
@ -494,7 +494,7 @@ impl BaseTransformImpl for EbuR128Level {
|
|||
|
||||
if state.ebur128.mode().contains(ebur128::Mode::S) {
|
||||
match state.ebur128.loudness_shortterm() {
|
||||
Ok(loudness) => s.set("shortterm-loudness", &loudness),
|
||||
Ok(loudness) => s.set("shortterm-loudness", loudness),
|
||||
Err(err) => gst::error!(
|
||||
CAT,
|
||||
imp: self,
|
||||
|
@ -506,7 +506,7 @@ impl BaseTransformImpl for EbuR128Level {
|
|||
|
||||
if state.ebur128.mode().contains(ebur128::Mode::I) {
|
||||
match state.ebur128.loudness_global() {
|
||||
Ok(loudness) => s.set("global-loudness", &loudness),
|
||||
Ok(loudness) => s.set("global-loudness", loudness),
|
||||
Err(err) => gst::error!(
|
||||
CAT,
|
||||
imp: self,
|
||||
|
@ -516,7 +516,7 @@ impl BaseTransformImpl for EbuR128Level {
|
|||
}
|
||||
|
||||
match state.ebur128.relative_threshold() {
|
||||
Ok(threshold) => s.set("relative-threshold", &threshold),
|
||||
Ok(threshold) => s.set("relative-threshold", threshold),
|
||||
Err(err) => gst::error!(
|
||||
CAT,
|
||||
imp: self,
|
||||
|
@ -528,7 +528,7 @@ impl BaseTransformImpl for EbuR128Level {
|
|||
|
||||
if state.ebur128.mode().contains(ebur128::Mode::LRA) {
|
||||
match state.ebur128.loudness_range() {
|
||||
Ok(range) => s.set("loudness-range", &range),
|
||||
Ok(range) => s.set("loudness-range", range),
|
||||
Err(err) => {
|
||||
gst::error!(CAT, imp: self, "Failed to get loudness range: {}", err)
|
||||
}
|
||||
|
|
|
@ -373,7 +373,7 @@ impl HrtfRender {
|
|||
let (prev_offset, _) = state.adapter.prev_offset();
|
||||
let offset = prev_offset.checked_add(distance_samples).unwrap_or(0);
|
||||
|
||||
let duration_samples = outputsz / outbpf as usize;
|
||||
let duration_samples = outputsz / outbpf;
|
||||
let duration = samples_to_time(duration_samples as u64);
|
||||
|
||||
(pts, offset, duration)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "gst-plugin-claxon"
|
||||
version = "0.9.0-alpha.1"
|
||||
version = "0.9.13"
|
||||
authors = ["Ruben Gonzalez <rgonzalez@fluendo.com>"]
|
||||
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
@ -9,15 +9,15 @@ edition = "2021"
|
|||
rust-version = "1.63"
|
||||
|
||||
[dependencies]
|
||||
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
||||
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
||||
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19.1" }
|
||||
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
|
||||
claxon = { version = "0.4" }
|
||||
byte-slice-cast = "1.0"
|
||||
atomic_refcell = "0.1"
|
||||
once_cell = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
||||
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
|
||||
|
||||
[lib]
|
||||
name = "gstclaxon"
|
||||
|
@ -25,7 +25,7 @@ crate-type = ["cdylib", "rlib"]
|
|||
path = "src/lib.rs"
|
||||
|
||||
[build-dependencies]
|
||||
gst-plugin-version-helper = { path="../../version-helper" }
|
||||
gst-plugin-version-helper = { version = "0.7", path="../../version-helper" }
|
||||
|
||||
[features]
|
||||
static = []
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
../../LICENSE-APACHE
|
201
audio/claxon/LICENSE-APACHE
Normal file
201
audio/claxon/LICENSE-APACHE
Normal file
|
@ -0,0 +1,201 @@
|
|||
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 +0,0 @@
|
|||
../../LICENSE-MIT
|
23
audio/claxon/LICENSE-MIT
Normal file
23
audio/claxon/LICENSE-MIT
Normal file
|
@ -0,0 +1,23 @@
|
|||
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,6 +1,6 @@
|
|||
[package]
|
||||
name = "gst-plugin-csound"
|
||||
version = "0.9.0-alpha.1"
|
||||
version = "0.9.13"
|
||||
authors = ["Natanael Mojica <neithanmo@gmail.com>"]
|
||||
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
|
||||
license = "MPL-2.0"
|
||||
|
@ -9,15 +9,15 @@ rust-version = "1.63"
|
|||
description = "GStreamer Audio Filter plugin based on Csound"
|
||||
|
||||
[dependencies]
|
||||
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
||||
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
||||
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
||||
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19.1" }
|
||||
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
|
||||
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
|
||||
csound = "0.1.8"
|
||||
once_cell = "1.0"
|
||||
byte-slice-cast = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
||||
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
|
||||
|
||||
[lib]
|
||||
name = "gstcsound"
|
||||
|
@ -29,7 +29,7 @@ name = "csound-effect"
|
|||
path = "examples/effect_example.rs"
|
||||
|
||||
[build-dependencies]
|
||||
gst-plugin-version-helper = { path = "../../version-helper" }
|
||||
gst-plugin-version-helper = { version = "0.7", path = "../../version-helper" }
|
||||
|
||||
[features]
|
||||
static = []
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
../../LICENSE-MPL-2.0
|
373
audio/csound/LICENSE-MPL-2.0
Normal file
373
audio/csound/LICENSE-MPL-2.0
Normal file
|
@ -0,0 +1,373 @@
|
|||
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.
|
|
@ -80,7 +80,7 @@ fn create_pipeline() -> Result<gst::Pipeline, Box<dyn Error>> {
|
|||
let audio_sink = gst::parse_bin_from_description(AUDIO_SINK, true)?.upcast();
|
||||
|
||||
let csoundfilter = gst::ElementFactory::make("csoundfilter")
|
||||
.property("csd-text", &CSD)
|
||||
.property("csd-text", CSD)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
|
|
|
@ -517,15 +517,15 @@ impl BaseTransformImpl for CsoundFilter {
|
|||
let ichannels = csound.input_channels() as i32;
|
||||
let ochannels = csound.output_channels() as i32;
|
||||
for s in new_caps.make_mut().iter_mut() {
|
||||
s.set("format", &gst_audio::AUDIO_FORMAT_F64.to_str());
|
||||
s.set("rate", &sr);
|
||||
s.set("format", gst_audio::AUDIO_FORMAT_F64.to_str());
|
||||
s.set("rate", sr);
|
||||
|
||||
// replace the channel property with our values,
|
||||
// if they are not supported, the negotiation will fail.
|
||||
if direction == gst::PadDirection::Src {
|
||||
s.set("channels", &ichannels);
|
||||
s.set("channels", ichannels);
|
||||
} else {
|
||||
s.set("channels", &ochannels);
|
||||
s.set("channels", ochannels);
|
||||
}
|
||||
// Csound does not have a concept of channel-mask
|
||||
s.remove_field("channel-mask");
|
||||
|
|
|
@ -57,7 +57,7 @@ fn init() {
|
|||
|
||||
fn build_harness(src_caps: gst::Caps, sink_caps: gst::Caps, csd: &str) -> gst_check::Harness {
|
||||
let filter = gst::ElementFactory::make("csoundfilter")
|
||||
.property("csd-text", &csd)
|
||||
.property("csd-text", csd)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
|
@ -260,10 +260,7 @@ fn csound_filter_underflow() {
|
|||
}
|
||||
|
||||
assert_eq!(num_buffers, UNDERFLOW_NUM_BUFFERS / 2);
|
||||
assert_eq!(
|
||||
num_samples as usize,
|
||||
UNDERFLOW_NUM_SAMPLES * UNDERFLOW_NUM_BUFFERS
|
||||
);
|
||||
assert_eq!(num_samples, UNDERFLOW_NUM_SAMPLES * UNDERFLOW_NUM_BUFFERS);
|
||||
}
|
||||
|
||||
// Verifies that the caps negotiation is properly done, by pushing buffers whose caps
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "gst-plugin-lewton"
|
||||
version = "0.9.0-alpha.1"
|
||||
version = "0.9.13"
|
||||
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
|
||||
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
@ -9,15 +9,15 @@ edition = "2021"
|
|||
rust-version = "1.63"
|
||||
|
||||
[dependencies]
|
||||
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
||||
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
||||
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19.1" }
|
||||
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
|
||||
lewton = { version = "0.10", default-features = false }
|
||||
byte-slice-cast = "1.0"
|
||||
atomic_refcell = "0.1"
|
||||
once_cell = "1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
||||
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
|
||||
|
||||
[lib]
|
||||
name = "gstlewton"
|
||||
|
@ -25,7 +25,7 @@ crate-type = ["cdylib", "rlib"]
|
|||
path = "src/lib.rs"
|
||||
|
||||
[build-dependencies]
|
||||
gst-plugin-version-helper = { path="../../version-helper" }
|
||||
gst-plugin-version-helper = { version = "0.7", path="../../version-helper" }
|
||||
|
||||
[features]
|
||||
static = []
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
../../LICENSE-APACHE
|
201
audio/lewton/LICENSE-APACHE
Normal file
201
audio/lewton/LICENSE-APACHE
Normal file
|
@ -0,0 +1,201 @@
|
|||
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 +0,0 @@
|
|||
../../LICENSE-MIT
|
23
audio/lewton/LICENSE-MIT
Normal file
23
audio/lewton/LICENSE-MIT
Normal file
|
@ -0,0 +1,23 @@
|
|||
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.
|
|
@ -405,7 +405,7 @@ impl LewtonDec {
|
|||
let outbuf = if let Some(ref reorder_map) = state.reorder_map {
|
||||
let mut outbuf = self
|
||||
.obj()
|
||||
.allocate_output_buffer(sample_count as usize * audio_info.bpf() as usize);
|
||||
.allocate_output_buffer(sample_count * audio_info.bpf() as usize);
|
||||
{
|
||||
// And copy the decoded data into our output buffer while reordering the channels to the
|
||||
// GStreamer channel order
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "gst-plugin-spotify"
|
||||
version = "0.9.0-alpha.1"
|
||||
version = "0.9.13"
|
||||
authors = ["Guillaume Desmottes <guillaume@desmottes.be>"]
|
||||
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
|
||||
license = "MPL-2.0"
|
||||
|
@ -9,8 +9,8 @@ edition = "2021"
|
|||
rust-version = "1.63"
|
||||
|
||||
[dependencies]
|
||||
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
||||
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
||||
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19.1" }
|
||||
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
|
||||
once_cell = "1.0"
|
||||
librespot = { version = "0.4", default-features = false }
|
||||
tokio = "1.0"
|
||||
|
@ -24,7 +24,7 @@ crate-type = ["cdylib", "rlib"]
|
|||
path = "src/lib.rs"
|
||||
|
||||
[build-dependencies]
|
||||
gst-plugin-version-helper = { path="../../version-helper" }
|
||||
gst-plugin-version-helper = { version = "0.7", path="../../version-helper" }
|
||||
|
||||
[features]
|
||||
static = []
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
../../LICENSE-MPL-2.0
|
373
audio/spotify/LICENSE-MPL-2.0
Normal file
373
audio/spotify/LICENSE-MPL-2.0
Normal file
|
@ -0,0 +1,373 @@
|
|||
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.
|
|
@ -6,9 +6,10 @@
|
|||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use std::sync::{mpsc, Arc, Mutex};
|
||||
use std::sync::{mpsc, Arc, Mutex, MutexGuard};
|
||||
|
||||
use anyhow::bail;
|
||||
use futures::future::{AbortHandle, Abortable, Aborted};
|
||||
use once_cell::sync::Lazy;
|
||||
use tokio::{runtime, task::JoinHandle};
|
||||
|
||||
|
@ -30,6 +31,8 @@ use librespot::playback::{
|
|||
player::{Player, PlayerEvent},
|
||||
};
|
||||
|
||||
use super::Bitrate;
|
||||
|
||||
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||
gst::DebugCategory::new(
|
||||
"spotifyaudiosrc",
|
||||
|
@ -70,14 +73,21 @@ struct Settings {
|
|||
cache_files: String,
|
||||
cache_max_size: u64,
|
||||
track: String,
|
||||
bitrate: Bitrate,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SpotifyAudioSrc {
|
||||
setup_thread: Mutex<Option<SetupThread>>,
|
||||
state: Arc<Mutex<Option<State>>>,
|
||||
settings: Mutex<Settings>,
|
||||
}
|
||||
|
||||
struct SetupThread {
|
||||
thread_handle: std::thread::JoinHandle<Result<anyhow::Result<()>, Aborted>>,
|
||||
abort_handle: AbortHandle,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for SpotifyAudioSrc {
|
||||
const NAME: &'static str = "GstSpotifyAudioSrc";
|
||||
|
@ -125,6 +135,11 @@ impl ObjectImpl for SpotifyAudioSrc {
|
|||
.default_value(Some(""))
|
||||
.mutable_ready()
|
||||
.build(),
|
||||
glib::ParamSpecEnum::builder("bitrate", Bitrate::default())
|
||||
.nick("Spotify bitrate")
|
||||
.blurb("Spotify audio bitrate in kbit/s")
|
||||
.mutable_ready()
|
||||
.build()
|
||||
]
|
||||
});
|
||||
|
||||
|
@ -157,6 +172,10 @@ impl ObjectImpl for SpotifyAudioSrc {
|
|||
let mut settings = self.settings.lock().unwrap();
|
||||
settings.track = value.get().expect("type checked upstream");
|
||||
}
|
||||
"bitrate" => {
|
||||
let mut settings = self.settings.lock().unwrap();
|
||||
settings.bitrate = value.get().expect("type checked upstream");
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
@ -187,6 +206,10 @@ impl ObjectImpl for SpotifyAudioSrc {
|
|||
let settings = self.settings.lock().unwrap();
|
||||
settings.track.to_value()
|
||||
}
|
||||
"bitrate" => {
|
||||
let settings = self.settings.lock().unwrap();
|
||||
settings.bitrate.to_value()
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
@ -237,17 +260,22 @@ impl BaseSrcImpl for SpotifyAudioSrc {
|
|||
}
|
||||
}
|
||||
|
||||
if let Err(err) = RUNTIME.block_on(async move { self.setup().await }) {
|
||||
let details = format!("{:?}", err);
|
||||
gst::error!(CAT, imp: self, "failed to start: {}", details);
|
||||
gst::element_imp_error!(self, gst::ResourceError::Settings, [&details]);
|
||||
return Err(gst::error_msg!(gst::ResourceError::Settings, [&details]));
|
||||
{
|
||||
let setup_thread = self.setup_thread.lock().unwrap();
|
||||
if setup_thread.is_some() {
|
||||
// already starting
|
||||
return Ok(());
|
||||
}
|
||||
self.start_setup(setup_thread);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn stop(&self) -> Result<(), gst::ErrorMessage> {
|
||||
// stop the setup if it's not completed yet
|
||||
self.cancel_setup();
|
||||
|
||||
if let Some(state) = self.state.lock().unwrap().take() {
|
||||
gst::debug!(CAT, imp: self, "stopping");
|
||||
state.player.stop();
|
||||
|
@ -258,6 +286,12 @@ impl BaseSrcImpl for SpotifyAudioSrc {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unlock(&self) -> Result<(), gst::ErrorMessage> {
|
||||
self.cancel_setup();
|
||||
|
||||
self.parent_unlock()
|
||||
}
|
||||
}
|
||||
|
||||
impl PushSrcImpl for SpotifyAudioSrc {
|
||||
|
@ -265,6 +299,41 @@ impl PushSrcImpl for SpotifyAudioSrc {
|
|||
&self,
|
||||
_buffer: Option<&mut gst::BufferRef>,
|
||||
) -> Result<CreateSuccess, gst::FlowError> {
|
||||
let state_set = {
|
||||
let state = self.state.lock().unwrap();
|
||||
state.is_some()
|
||||
};
|
||||
|
||||
if !state_set {
|
||||
let setup_thread = self.setup_thread.lock().unwrap();
|
||||
if setup_thread.is_none() {
|
||||
// unlock() could potentially cancel the setup, and create() can be called after unlock() without going through start() again.
|
||||
self.start_setup(setup_thread);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// wait for the setup to be completed
|
||||
let mut setup_thread = self.setup_thread.lock().unwrap();
|
||||
if let Some(setup) = setup_thread.take() {
|
||||
let res = setup.thread_handle.join().unwrap();
|
||||
|
||||
match res {
|
||||
Err(_aborted) => {
|
||||
gst::debug!(CAT, imp: self, "setup has been cancelled");
|
||||
return Err(gst::FlowError::Flushing);
|
||||
}
|
||||
Ok(Err(err)) => {
|
||||
let details = format!("{:?}", err);
|
||||
gst::error!(CAT, imp: self, "failed to start: {}", details);
|
||||
gst::element_imp_error!(self, gst::ResourceError::Settings, [&details]);
|
||||
return Err(gst::FlowError::Error);
|
||||
}
|
||||
Ok(Ok(_)) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let state = self.state.lock().unwrap();
|
||||
let state = state.as_ref().unwrap();
|
||||
|
||||
|
@ -290,112 +359,6 @@ impl PushSrcImpl for SpotifyAudioSrc {
|
|||
}
|
||||
}
|
||||
|
||||
impl SpotifyAudioSrc {
|
||||
async fn setup(&self) -> anyhow::Result<()> {
|
||||
let (credentials, cache, track) = {
|
||||
let settings = self.settings.lock().unwrap();
|
||||
|
||||
let credentials_cache = if settings.cache_credentials.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(&settings.cache_credentials)
|
||||
};
|
||||
|
||||
let files_cache = if settings.cache_files.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(&settings.cache_files)
|
||||
};
|
||||
|
||||
let max_size = if settings.cache_max_size != 0 {
|
||||
Some(settings.cache_max_size)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let cache = Cache::new(credentials_cache, None, files_cache, max_size)?;
|
||||
|
||||
let credentials = match cache.credentials() {
|
||||
Some(cached_cred) => {
|
||||
gst::debug!(CAT, imp: self, "reuse credentials from cache",);
|
||||
cached_cred
|
||||
}
|
||||
None => {
|
||||
gst::debug!(CAT, imp: self, "credentials not in cache",);
|
||||
|
||||
if settings.username.is_empty() {
|
||||
bail!("username is not set and credentials are not in cache");
|
||||
}
|
||||
if settings.password.is_empty() {
|
||||
bail!("password is not set and credentials are not in cache");
|
||||
}
|
||||
|
||||
let cred = Credentials::with_password(&settings.username, &settings.password);
|
||||
cache.save_credentials(&cred);
|
||||
cred
|
||||
}
|
||||
};
|
||||
|
||||
if settings.track.is_empty() {
|
||||
bail!("track is not set")
|
||||
}
|
||||
|
||||
(credentials, cache, settings.track.clone())
|
||||
};
|
||||
|
||||
let state = self.state.clone();
|
||||
|
||||
let (session, _credentials) =
|
||||
Session::connect(SessionConfig::default(), credentials, Some(cache), false).await?;
|
||||
|
||||
let player_config = PlayerConfig {
|
||||
passthrough: true,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// use a sync channel to prevent buffering the whole track inside the channel
|
||||
let (sender, receiver) = mpsc::sync_channel(2);
|
||||
let sender_clone = sender.clone();
|
||||
|
||||
let (mut player, mut player_event_channel) =
|
||||
Player::new(player_config, session, Box::new(NoOpVolume), || {
|
||||
Box::new(BufferSink { sender })
|
||||
});
|
||||
|
||||
let track = match SpotifyId::from_uri(&track) {
|
||||
Ok(track) => track,
|
||||
Err(_) => bail!("Failed to create Spotify URI from track"),
|
||||
};
|
||||
|
||||
player.load(track, true, 0);
|
||||
|
||||
let player_channel_handle = RUNTIME.spawn(async move {
|
||||
let sender = sender_clone;
|
||||
|
||||
while let Some(event) = player_event_channel.recv().await {
|
||||
match event {
|
||||
PlayerEvent::EndOfTrack { .. } => {
|
||||
let _ = sender.send(Message::Eos);
|
||||
}
|
||||
PlayerEvent::Unavailable { .. } => {
|
||||
let _ = sender.send(Message::Unavailable);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let mut state = state.lock().unwrap();
|
||||
state.replace(State {
|
||||
player,
|
||||
receiver,
|
||||
player_channel_handle,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct BufferSink {
|
||||
sender: mpsc::SyncSender<Message>,
|
||||
}
|
||||
|
@ -456,3 +419,149 @@ impl URIHandlerImpl for SpotifyAudioSrc {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SpotifyAudioSrc {
|
||||
fn start_setup(&self, mut setup_thread: MutexGuard<Option<SetupThread>>) {
|
||||
let self_ = self.to_owned();
|
||||
|
||||
// run the runtime from another thread to prevent the "start a runtime from within a runtime" panic
|
||||
// when the plugin is statically linked.
|
||||
let (abort_handle, abort_registration) = AbortHandle::new_pair();
|
||||
let thread_handle = std::thread::spawn(move || {
|
||||
RUNTIME.block_on(async move {
|
||||
let future = Abortable::new(self_.setup(), abort_registration);
|
||||
future.await
|
||||
})
|
||||
});
|
||||
|
||||
setup_thread.replace(SetupThread {
|
||||
thread_handle,
|
||||
abort_handle,
|
||||
});
|
||||
}
|
||||
|
||||
async fn setup(&self) -> anyhow::Result<()> {
|
||||
{
|
||||
let state = self.state.lock().unwrap();
|
||||
|
||||
if state.is_some() {
|
||||
// already setup
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let (credentials, cache, track, bitrate) = {
|
||||
let settings = self.settings.lock().unwrap();
|
||||
|
||||
let credentials_cache = if settings.cache_credentials.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(&settings.cache_credentials)
|
||||
};
|
||||
|
||||
let files_cache = if settings.cache_files.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(&settings.cache_files)
|
||||
};
|
||||
|
||||
let max_size = if settings.cache_max_size != 0 {
|
||||
Some(settings.cache_max_size)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let cache = Cache::new(credentials_cache, None, files_cache, max_size)?;
|
||||
|
||||
let credentials = match cache.credentials() {
|
||||
Some(cached_cred)
|
||||
if settings.username.is_empty()
|
||||
|| settings.username == cached_cred.username =>
|
||||
{
|
||||
gst::debug!(CAT, imp: self, "reuse credentials from cache",);
|
||||
cached_cred
|
||||
}
|
||||
_ => {
|
||||
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");
|
||||
}
|
||||
|
||||
Credentials::with_password(&settings.username, &settings.password)
|
||||
}
|
||||
};
|
||||
|
||||
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), true).await?;
|
||||
|
||||
let player_config = PlayerConfig {
|
||||
passthrough: true,
|
||||
bitrate,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// use a sync channel to prevent buffering the whole track inside the channel
|
||||
let (sender, receiver) = mpsc::sync_channel(2);
|
||||
let sender_clone = sender.clone();
|
||||
|
||||
let (mut player, mut player_event_channel) =
|
||||
Player::new(player_config, session, Box::new(NoOpVolume), || {
|
||||
Box::new(BufferSink { sender })
|
||||
});
|
||||
|
||||
let track = match SpotifyId::from_uri(&track) {
|
||||
Ok(track) => track,
|
||||
Err(_) => bail!("Failed to create Spotify URI from track"),
|
||||
};
|
||||
|
||||
player.load(track, true, 0);
|
||||
|
||||
let player_channel_handle = RUNTIME.spawn(async move {
|
||||
let sender = sender_clone;
|
||||
|
||||
while let Some(event) = player_event_channel.recv().await {
|
||||
match event {
|
||||
PlayerEvent::EndOfTrack { .. } => {
|
||||
let _ = sender.send(Message::Eos);
|
||||
}
|
||||
PlayerEvent::Unavailable { .. } => {
|
||||
let _ = sender.send(Message::Unavailable);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let mut state = self.state.lock().unwrap();
|
||||
|
||||
state.replace(State {
|
||||
player,
|
||||
receiver,
|
||||
player_channel_handle,
|
||||
});
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,11 +11,42 @@ use gst::prelude::*;
|
|||
|
||||
mod imp;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, glib::Enum)]
|
||||
#[repr(u32)]
|
||||
#[enum_type(name = "GstRsSpotifyBitrate")]
|
||||
enum Bitrate {
|
||||
#[enum_value(name = "96 kbit/s", nick = "96")]
|
||||
B96,
|
||||
#[enum_value(name = "160 kbit/s", nick = "160")]
|
||||
B160,
|
||||
#[enum_value(name = "320 kbit/s", nick = "320")]
|
||||
B320,
|
||||
}
|
||||
|
||||
impl Default for Bitrate {
|
||||
fn default() -> Self {
|
||||
Self::B160
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Bitrate> for librespot::playback::config::Bitrate {
|
||||
fn from(value: Bitrate) -> Self {
|
||||
match value {
|
||||
Bitrate::B96 => Self::Bitrate96,
|
||||
Bitrate::B160 => Self::Bitrate160,
|
||||
Bitrate::B320 => Self::Bitrate320,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct SpotifyAudioSrc(ObjectSubclass<imp::SpotifyAudioSrc>) @extends gst_base::PushSrc, gst_base::BaseSrc, gst::Element, gst::Object, @implements gst::URIHandler;
|
||||
}
|
||||
|
||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
#[cfg(feature = "doc")]
|
||||
Bitrate::static_type().mark_as_plugin_api(gst::PluginAPIFlags::empty());
|
||||
|
||||
gst::Element::register(
|
||||
Some(plugin),
|
||||
"spotifyaudiosrc",
|
||||
|
|
114
cargo_wrapper.py
114
cargo_wrapper.py
|
@ -7,6 +7,7 @@ import os
|
|||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import shlex
|
||||
from argparse import ArgumentParser
|
||||
from pathlib import Path as P
|
||||
|
||||
|
@ -16,24 +17,44 @@ PARSER.add_argument('build_dir', type=P)
|
|||
PARSER.add_argument('src_dir', type=P)
|
||||
PARSER.add_argument('root_dir', type=P)
|
||||
PARSER.add_argument('target', choices=['release', 'debug'])
|
||||
PARSER.add_argument('include')
|
||||
PARSER.add_argument('extra_env')
|
||||
PARSER.add_argument('prefix', type=P)
|
||||
PARSER.add_argument('libdir', type=P)
|
||||
PARSER.add_argument('--version', default=None)
|
||||
PARSER.add_argument('--bin', default=None, type=P)
|
||||
PARSER.add_argument('--exts', nargs="+", default=[])
|
||||
PARSER.add_argument('--features', nargs="+", default=[])
|
||||
PARSER.add_argument('--packages', nargs="+", default=[])
|
||||
PARSER.add_argument('--examples', nargs="+", default=[])
|
||||
PARSER.add_argument('--lib-suffixes', nargs="+", default=[])
|
||||
PARSER.add_argument('--exe-suffix')
|
||||
PARSER.add_argument('--depfile')
|
||||
PARSER.add_argument('--disable-doc', action="store_true", default=False)
|
||||
|
||||
|
||||
def shlex_join(args):
|
||||
if hasattr(shlex, 'join'):
|
||||
return shlex.join(args)
|
||||
return ' '.join([shlex.quote(arg) for arg in args])
|
||||
|
||||
|
||||
def generate_depfile_for(fpath):
|
||||
file_stem = fpath.parent / fpath.stem
|
||||
depfile_content = ""
|
||||
with open(f"{file_stem}.d", 'r') as depfile:
|
||||
for l in depfile.readlines():
|
||||
if l.startswith(str(file_stem)):
|
||||
output, srcs = l.split(":", maxsplit=2)
|
||||
# We can't blindly split on `:` because on Windows that's part
|
||||
# of the drive letter. Lucky for us, the format of the dep file
|
||||
# is one of:
|
||||
#
|
||||
# /path/to/output: /path/to/src1 /path/to/src2
|
||||
# /path/to/output:
|
||||
#
|
||||
# So we parse these two cases specifically
|
||||
if l.endswith(':'):
|
||||
output = l[:-1]
|
||||
srcs = ''
|
||||
else:
|
||||
output, srcs = l.split(": ", maxsplit=2)
|
||||
|
||||
all_deps = []
|
||||
for src in srcs.split(" "):
|
||||
|
@ -53,33 +74,45 @@ def generate_depfile_for(fpath):
|
|||
|
||||
if __name__ == "__main__":
|
||||
opts = PARSER.parse_args()
|
||||
|
||||
logfile = open(opts.root_dir / 'meson-logs' /
|
||||
f'{opts.src_dir.name}-cargo-wrapper.log', 'w')
|
||||
logdir = opts.root_dir / 'meson-logs'
|
||||
logfile_path = logdir / f'{opts.src_dir.name}-cargo-wrapper.log'
|
||||
logfile = open(logfile_path, mode='w', buffering=1)
|
||||
|
||||
print(opts, file=logfile)
|
||||
cargo_target_dir = opts.build_dir / 'target'
|
||||
|
||||
env = os.environ.copy()
|
||||
env['CARGO_TARGET_DIR'] = str(cargo_target_dir)
|
||||
|
||||
pkg_config_path = env.get('PKG_CONFIG_PATH', '').split(':')
|
||||
if 'PKG_CONFIG_PATH' in env:
|
||||
pkg_config_path = env['PKG_CONFIG_PATH'].split(os.pathsep)
|
||||
else:
|
||||
pkg_config_path = []
|
||||
pkg_config_path.append(str(opts.root_dir / 'meson-uninstalled'))
|
||||
env['PKG_CONFIG_PATH'] = ':'.join(pkg_config_path)
|
||||
env['PKG_CONFIG_PATH'] = os.pathsep.join(pkg_config_path)
|
||||
|
||||
if opts.extra_env:
|
||||
for e in opts.extra_env.split(','):
|
||||
k, v = e.split(':')
|
||||
env[k] = v
|
||||
if 'NASM' in env:
|
||||
env['PATH'] = os.pathsep.join([os.path.dirname(env['NASM']), env['PATH']])
|
||||
|
||||
rustc_target = None
|
||||
if 'RUSTC' in env:
|
||||
rustc_cmdline = shlex.split(env['RUSTC'])
|
||||
# grab target from RUSTFLAGS
|
||||
rust_flags = rustc_cmdline[1:] + shlex.split(env.get('RUSTFLAGS', ''))
|
||||
if '--target' in rust_flags:
|
||||
rustc_target_idx = rust_flags.index('--target')
|
||||
_ = rust_flags.pop(rustc_target_idx) # drop '--target'
|
||||
rustc_target = rust_flags.pop(rustc_target_idx)
|
||||
env['RUSTFLAGS'] = shlex_join(rust_flags)
|
||||
env['RUSTC'] = rustc_cmdline[0]
|
||||
|
||||
features = opts.features
|
||||
if opts.command == 'build':
|
||||
cargo_cmd = ['cargo']
|
||||
if opts.bin:
|
||||
if opts.bin or opts.examples:
|
||||
cargo_cmd += ['build']
|
||||
else:
|
||||
cargo_cmd += ['cbuild']
|
||||
if not opts.disable_doc:
|
||||
cargo_cmd += ['--features', "doc"]
|
||||
features += ['doc']
|
||||
if opts.target == 'release':
|
||||
cargo_cmd.append('--release')
|
||||
elif opts.command == 'test':
|
||||
|
@ -89,40 +122,43 @@ if __name__ == "__main__":
|
|||
print("Unknown command:", opts.command, file=logfile)
|
||||
sys.exit(1)
|
||||
|
||||
cwd = None
|
||||
if not opts.bin:
|
||||
cargo_cmd.extend(['--manifest-path', opts.src_dir / 'Cargo.toml'])
|
||||
cargo_cmd.extend(['--prefix', opts.prefix, '--libdir',
|
||||
opts.prefix / opts.libdir])
|
||||
for p in opts.include.split(','):
|
||||
cargo_cmd.extend(['-p', p])
|
||||
else:
|
||||
if rustc_target:
|
||||
cargo_cmd += ['--target', rustc_target]
|
||||
if features:
|
||||
cargo_cmd += ['--features', ','.join(features)]
|
||||
cargo_cmd += ['--target-dir', cargo_target_dir]
|
||||
cargo_cmd += ['--manifest-path', opts.src_dir / 'Cargo.toml']
|
||||
if opts.bin:
|
||||
cargo_cmd.extend(['--bin', opts.bin.name])
|
||||
cwd = opts.src_dir
|
||||
else:
|
||||
if not opts.examples:
|
||||
cargo_cmd.extend(['--prefix', opts.prefix, '--libdir',
|
||||
opts.prefix / opts.libdir])
|
||||
for p in opts.packages:
|
||||
cargo_cmd.extend(['-p', p])
|
||||
for e in opts.examples:
|
||||
cargo_cmd.extend(['--example', e])
|
||||
|
||||
def run(cargo_cmd, env, cwd=cwd):
|
||||
def run(cargo_cmd, env):
|
||||
print(cargo_cmd, env, file=logfile)
|
||||
try:
|
||||
subprocess.run(cargo_cmd, env=env, check=True, cwd=cwd)
|
||||
subprocess.run(cargo_cmd, env=env, cwd=opts.src_dir, check=True)
|
||||
except subprocess.SubprocessError:
|
||||
sys.exit(1)
|
||||
|
||||
run(cargo_cmd, env, cwd)
|
||||
run(cargo_cmd, env)
|
||||
|
||||
if opts.command == 'build':
|
||||
target_dir = cargo_target_dir / '**' / opts.target
|
||||
if opts.bin:
|
||||
if opts.exts[0]:
|
||||
ext = f'.{opts.exts[0]}'
|
||||
else:
|
||||
ext = ''
|
||||
exe = glob.glob(str(target_dir / opts.bin) + ext, recursive=True)[0]
|
||||
exe = glob.glob(str(target_dir / opts.bin) + opts.exe_suffix, recursive=True)[0]
|
||||
shutil.copy2(exe, opts.build_dir)
|
||||
depfile_content = generate_depfile_for(P(exe))
|
||||
else:
|
||||
# Copy so files to build dir
|
||||
depfile_content = ""
|
||||
for ext in opts.exts:
|
||||
for f in glob.glob(str(target_dir / f'*.{ext}'), recursive=True):
|
||||
for suffix in opts.lib_suffixes:
|
||||
for f in glob.glob(str(target_dir / f'*.{suffix}'), recursive=True):
|
||||
libfile = P(f)
|
||||
|
||||
depfile_content += generate_depfile_for(libfile)
|
||||
|
@ -137,6 +173,12 @@ if __name__ == "__main__":
|
|||
|
||||
print(f"Copying {copied_file}", file=logfile)
|
||||
shutil.copy2(f, opts.build_dir)
|
||||
# Copy examples to builddir
|
||||
for example in opts.examples:
|
||||
example_glob = str(target_dir / 'examples' / example) + opts.exe_suffix
|
||||
exe = glob.glob(example_glob, recursive=True)[0]
|
||||
shutil.copy2(exe, opts.build_dir)
|
||||
depfile_content += generate_depfile_for(P(exe))
|
||||
|
||||
with open(opts.depfile, 'w') as depfile:
|
||||
depfile.write(depfile_content)
|
||||
|
|
|
@ -7,7 +7,8 @@ import os
|
|||
from utils import iterate_plugins
|
||||
|
||||
# the csound version used on ci does not ship a .pc file
|
||||
IGNORE = ['csound']
|
||||
# threadshare we skip in meson static build as well
|
||||
IGNORE = ['csound', 'threadshare', 'gtk4']
|
||||
|
||||
outdir = sys.argv[1]
|
||||
|
||||
|
|
|
@ -3,4 +3,4 @@ source ./ci/env.sh
|
|||
set -e
|
||||
export CARGO_HOME='/usr/local/cargo'
|
||||
|
||||
cargo install cargo-c --version 0.9.12+cargo-0.64
|
||||
cargo install cargo-c --version 0.9.14+cargo-0.66
|
||||
|
|
|
@ -17,11 +17,18 @@ function Run-Tests {
|
|||
param (
|
||||
$Features
|
||||
)
|
||||
$local_exclude = $exclude_crates;
|
||||
|
||||
# In this case the plugin will pull x11/wayland features
|
||||
# which will fail to build on windows.
|
||||
if (($Features -eq '--all-features') -or ($Features -eq '')) {
|
||||
$local_exclude += @("--exclude", "gst-plugin-gtk4")
|
||||
}
|
||||
|
||||
Write-Host "Features: $Features"
|
||||
Write-Host "Exclude string: $exclude_crates"
|
||||
Write-Host "Exclude string: $local_exclude"
|
||||
|
||||
cargo build --color=always --workspace $exclude_crates --all-targets $Features
|
||||
cargo build --color=always --workspace $local_exclude --all-targets $Features
|
||||
|
||||
if (!$?) {
|
||||
Write-Host "Build failed"
|
||||
|
@ -29,7 +36,7 @@ function Run-Tests {
|
|||
}
|
||||
|
||||
$env:G_DEBUG="fatal_warnings"
|
||||
cargo test --no-fail-fast --color=always --workspace $exclude_crates --all-targets $Features
|
||||
cargo test --no-fail-fast --color=always --workspace $local_exclude --all-targets $Features
|
||||
|
||||
if (!$?) {
|
||||
Write-Host "Tests failed"
|
||||
|
|
117
deny.toml
117
deny.toml
|
@ -9,6 +9,7 @@ ignore = [
|
|||
"RUSTSEC-2021-0059",
|
||||
"RUSTSEC-2021-0060",
|
||||
"RUSTSEC-2021-0061",
|
||||
"RUSTSEC-2021-0145",
|
||||
# https://github.com/chronotope/chrono/issues/499
|
||||
"RUSTSEC-2020-0071",
|
||||
# sodiumoxide is deprecated
|
||||
|
@ -55,12 +56,6 @@ wildcards = "allow"
|
|||
name = "time"
|
||||
version = "0.1"
|
||||
|
||||
# ignore duplicated textwrap dependency because clap depends on an old version
|
||||
# https://github.com/clap-rs/clap/pull/1994
|
||||
[[bans.skip]]
|
||||
name = "textwrap"
|
||||
version = "0.11"
|
||||
|
||||
# ignore duplicated rustc_version dependency because rav1e depends on an old version
|
||||
[[bans.skip]]
|
||||
name = "rustc_version"
|
||||
|
@ -69,31 +64,13 @@ version = "0.3"
|
|||
name = "semver"
|
||||
version = "0.11"
|
||||
|
||||
# ignore duplicated system-deps dependency because dav1d depends on an old version
|
||||
[[bans.skip]]
|
||||
name = "system-deps"
|
||||
version = "3"
|
||||
|
||||
[[bans.skip]]
|
||||
name = "version-compare"
|
||||
version = "0.0"
|
||||
|
||||
[[bans.skip]]
|
||||
name = "cfg-expr"
|
||||
version = "0.7"
|
||||
|
||||
# ignore duplicated crc dependency because ffv1 depends on an old version
|
||||
# https://github.com/rust-av/ffv1/issues/21
|
||||
[[bans.skip]]
|
||||
name = "crc"
|
||||
version = "1.8"
|
||||
|
||||
# ignore duplicated heck dependency because various crates depend on an old version
|
||||
[[bans.skip]]
|
||||
name = "heck"
|
||||
version = "0.3"
|
||||
|
||||
# ignore duplicated sha-1/digest/block-buffer dependencies because librespot depends on an old version
|
||||
# Ignore various duplicated dependencies because librespot depends on an old versions
|
||||
[[bans.skip]]
|
||||
name = "block-buffer"
|
||||
version = "0.9"
|
||||
|
@ -103,6 +80,12 @@ version = "0.9"
|
|||
[[bans.skip]]
|
||||
name = "sha-1"
|
||||
version = "0.9"
|
||||
[[bans.skip]]
|
||||
name = "env_logger"
|
||||
version = "0.9"
|
||||
[[bans.skip]]
|
||||
name = "hmac"
|
||||
version = "0.11"
|
||||
|
||||
# ignore duplicated wasi dependency because various crates depends on an old version
|
||||
[[bans.skip]]
|
||||
|
@ -114,6 +97,90 @@ version = "0.10"
|
|||
name = "spin"
|
||||
version = "0.5"
|
||||
|
||||
# cookie_store depends on older idna
|
||||
# https://github.com/pfernie/cookie_store/commit/b9c710f45550c5c8997f18a83e6fcc5998cf1726
|
||||
[[bans.skip]]
|
||||
name = "idna"
|
||||
version = "0.2"
|
||||
|
||||
# field-offset and nix depend on an older memoffset
|
||||
# https://github.com/Diggsey/rust-field-offset/pull/23
|
||||
# https://github.com/nix-rust/nix/pull/1885
|
||||
[[bans.skip]]
|
||||
name = "memoffset"
|
||||
version = "0.6"
|
||||
|
||||
# Various crates depend on an older version of hermit-abi
|
||||
[[bans.skip]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1"
|
||||
[[bans.skip]]
|
||||
name = "hermit-abi"
|
||||
version = "0.2"
|
||||
|
||||
# Various crates depend on an older version of base64
|
||||
[[bans.skip]]
|
||||
name = "base64"
|
||||
version = "0.13"
|
||||
|
||||
# Various crates depend on an older version of windows-sys
|
||||
[[bans.skip]]
|
||||
name = "windows-sys"
|
||||
version = "0.42"
|
||||
[[bans.skip]]
|
||||
name = "windows-sys"
|
||||
version = "0.45"
|
||||
[[bans.skip]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42"
|
||||
[[bans.skip]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42"
|
||||
[[bans.skip]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42"
|
||||
[[bans.skip]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42"
|
||||
[[bans.skip]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42"
|
||||
[[bans.skip]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42"
|
||||
[[bans.skip]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42"
|
||||
[[bans.skip]]
|
||||
name = "windows-targets"
|
||||
version = "0.42"
|
||||
|
||||
# Various crates depend on an older version of socket2
|
||||
[[bans.skip]]
|
||||
name = "socket2"
|
||||
version = "0.4"
|
||||
|
||||
# Various crates depend on an older version of syn
|
||||
[[bans.skip]]
|
||||
name = "syn"
|
||||
version = "1.0"
|
||||
|
||||
# Various crates depend on an older version of bitflags
|
||||
[[bans.skip]]
|
||||
name = "bitflags"
|
||||
version = "1.0"
|
||||
|
||||
# cargo-lock depends on an old version of the toml crate
|
||||
# https://github.com/rustsec/rustsec/pull/805
|
||||
[[bans.skip]]
|
||||
name = "toml"
|
||||
version = "0.5"
|
||||
|
||||
# Various crates depend on an older version of redox_syscall
|
||||
[[bans.skip]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2"
|
||||
|
||||
[sources]
|
||||
unknown-registry = "deny"
|
||||
unknown-git = "deny"
|
||||
|
|
|
@ -19,6 +19,8 @@ except ImportError:
|
|||
PARSER = ArgumentParser()
|
||||
PARSER.add_argument('src_dir', type=Path)
|
||||
PARSER.add_argument('plugins', nargs='*')
|
||||
PARSER.add_argument('--features', action="store_true", help="Get list of features to activate")
|
||||
PARSER.add_argument('--gst-version', help="Get list of features to activate")
|
||||
|
||||
|
||||
# Map plugin name to directory name, for those that does not match.
|
||||
|
@ -38,27 +40,73 @@ RENAMES = {
|
|||
'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__":
|
||||
opts = PARSER.parse_args()
|
||||
analyzer = CargoAnalyzer()
|
||||
opts = PARSER.parse_args(namespace=analyzer)
|
||||
|
||||
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))
|
||||
print(','.join(analyzer.run()))
|
||||
|
|
|
@ -9,7 +9,7 @@ if meson.is_cross_build()
|
|||
subdir_done()
|
||||
endif
|
||||
|
||||
if static_build
|
||||
if default_library == 'static'
|
||||
if get_option('doc').enabled()
|
||||
error('Documentation enabled but not supported when building statically.')
|
||||
endif
|
||||
|
@ -99,9 +99,17 @@ foreach plugin_name: list_plugin_res.stdout().split(':')
|
|||
gst_index: 'plugins/index.md',
|
||||
include_paths: join_paths(meson.current_source_dir(), '..'),
|
||||
gst_smart_index: true,
|
||||
gst_c_source_filters: [
|
||||
'../target/*/*.rs',
|
||||
'../target/*/*/*.rs',
|
||||
'../target/*/*/*/*.rs',
|
||||
'../target/*/*/*/*/*.rs',
|
||||
'../target/*/*/*/*/*/*.rs',
|
||||
],
|
||||
gst_c_sources: [
|
||||
'../*/*/*/*.rs',
|
||||
'../*/*/*/*/*.rs',
|
||||
'../*/*/*/*/*/*.rs',
|
||||
],
|
||||
dependencies: [gst_dep],
|
||||
gst_order_generated_subpages: true,
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "gst-plugin-file"
|
||||
version = "0.9.0-alpha.1"
|
||||
version = "0.9.13"
|
||||
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
|
||||
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
@ -10,8 +10,8 @@ rust-version = "1.63"
|
|||
|
||||
[dependencies]
|
||||
url = "2"
|
||||
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
||||
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
||||
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19.1" }
|
||||
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
|
||||
once_cell = "1.0"
|
||||
|
||||
[lib]
|
||||
|
@ -20,7 +20,7 @@ crate-type = ["cdylib", "rlib"]
|
|||
path = "src/lib.rs"
|
||||
|
||||
[build-dependencies]
|
||||
gst-plugin-version-helper = { path="../../version-helper" }
|
||||
gst-plugin-version-helper = { version = "0.7", path="../../version-helper" }
|
||||
|
||||
[features]
|
||||
static = []
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
../../LICENSE-APACHE
|
201
generic/file/LICENSE-APACHE
Normal file
201
generic/file/LICENSE-APACHE
Normal file
|
@ -0,0 +1,201 @@
|
|||
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 +0,0 @@
|
|||
../../LICENSE-MIT
|
23
generic/file/LICENSE-MIT
Normal file
23
generic/file/LICENSE-MIT
Normal file
|
@ -0,0 +1,23 @@
|
|||
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.
|
|
@ -74,7 +74,7 @@ impl FileLocation {
|
|||
.parent()
|
||||
.expect("FileSink::set_location `location` with filename but without a parent")
|
||||
.to_owned();
|
||||
if parent_dir.is_relative() && parent_dir.components().next() == None {
|
||||
if parent_dir.is_relative() && parent_dir.components().next().is_none() {
|
||||
// `location` only contains the filename
|
||||
// need to specify "." for `canonicalize` to resolve the actual path
|
||||
parent_dir = PathBuf::from(".");
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
use gst::glib;
|
||||
use gst::prelude::*;
|
||||
use gst::subclass::prelude::*;
|
||||
use gst_base::prelude::*;
|
||||
use gst_base::subclass::prelude::*;
|
||||
|
||||
use std::fs::File;
|
||||
|
@ -112,6 +113,12 @@ impl ObjectSubclass for FileSink {
|
|||
}
|
||||
|
||||
impl ObjectImpl for FileSink {
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
self.obj().set_sync(false);
|
||||
}
|
||||
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![glib::ParamSpecString::builder("location")
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "gst-plugin-sodium"
|
||||
version = "0.9.0-alpha.1"
|
||||
version = "0.9.13"
|
||||
authors = ["Jordan Petridis <jordan@centricular.com>"]
|
||||
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
|
||||
description = "GStreamer plugin for libsodium-based file encryption and decryption"
|
||||
|
@ -9,8 +9,8 @@ edition = "2021"
|
|||
rust-version = "1.63"
|
||||
|
||||
[dependencies]
|
||||
gst = { package="gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
||||
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
||||
gst = { package="gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
|
||||
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
|
||||
sodiumoxide = "0.2.1"
|
||||
once_cell = "1.3.0"
|
||||
hex = "0.4"
|
||||
|
@ -27,10 +27,14 @@ rand = "0.8"
|
|||
|
||||
[dev-dependencies.gst-check]
|
||||
git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs"
|
||||
branch = "0.19"
|
||||
version = "0.19"
|
||||
package="gstreamer-check"
|
||||
|
||||
[dev-dependencies.gst-app]
|
||||
git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs"
|
||||
branch = "0.19"
|
||||
version = "0.19"
|
||||
package="gstreamer-app"
|
||||
|
||||
[lib]
|
||||
|
@ -54,7 +58,7 @@ path = "examples/decrypt_example.rs"
|
|||
required-features = ["serde", "serde_json", "clap"]
|
||||
|
||||
[build-dependencies]
|
||||
gst-plugin-version-helper = { path="../../version-helper" }
|
||||
gst-plugin-version-helper = { version = "0.7", path="../../version-helper" }
|
||||
|
||||
[features]
|
||||
static = []
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
../../LICENSE-MIT
|
23
generic/sodium/LICENSE-MIT
Normal file
23
generic/sodium/LICENSE-MIT
Normal file
|
@ -0,0 +1,23 @@
|
|||
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.
|
|
@ -53,7 +53,7 @@ struct Keys {
|
|||
|
||||
impl Keys {
|
||||
fn from_file(file: &Path) -> Result<Self, Box<dyn Error>> {
|
||||
let f = File::open(&file)?;
|
||||
let f = File::open(file)?;
|
||||
serde_json::from_reader(f).map_err(From::from)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ struct Keys {
|
|||
|
||||
impl Keys {
|
||||
fn from_file(file: &Path) -> Result<Self, Box<dyn Error>> {
|
||||
let f = File::open(&file)?;
|
||||
let f = File::open(file)?;
|
||||
serde_json::from_reader(f).map_err(From::from)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -498,7 +498,7 @@ impl Decrypter {
|
|||
gst::debug!(CAT, obj: pad, "Requested offset: {}", offset);
|
||||
gst::debug!(CAT, obj: pad, "Requested size: {}", requested_size);
|
||||
|
||||
let chunk_index = offset as u64 / block_size as u64;
|
||||
let chunk_index = offset / block_size as u64;
|
||||
gst::debug!(CAT, obj: pad, "Stream Block index: {}", chunk_index);
|
||||
|
||||
let pull_offset = offset - (chunk_index * block_size as u64);
|
||||
|
|
|
@ -75,7 +75,7 @@ fn test_pipeline() {
|
|||
};
|
||||
|
||||
let filesrc = gst::ElementFactory::make("filesrc")
|
||||
.property("location", &input_path.to_str().unwrap())
|
||||
.property("location", input_path.to_str().unwrap())
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "gst-plugin-threadshare"
|
||||
version = "0.9.0-alpha.1"
|
||||
version = "0.9.13"
|
||||
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
|
||||
license = "LGPL-2.1-or-later"
|
||||
description = "GStreamer Threadshare Plugin"
|
||||
|
@ -10,20 +10,21 @@ rust-version = "1.63"
|
|||
|
||||
[dependencies]
|
||||
async-task = "4.3.0"
|
||||
concurrent-queue = "1.2.2"
|
||||
concurrent-queue = "2"
|
||||
flume = "0.10.13"
|
||||
futures = "0.3.21"
|
||||
libc = "0.2"
|
||||
gio = { git = "https://github.com/gtk-rs/gtk-rs-core" }
|
||||
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
||||
gst-net = { package = "gstreamer-net", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
||||
gst-rtp = { package = "gstreamer-rtp", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
||||
gio = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "0.16", version = "0.16" }
|
||||
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19.1" }
|
||||
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
|
||||
gst-net = { package = "gstreamer-net", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
|
||||
gst-rtp = { package = "gstreamer-rtp", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
|
||||
once_cell = "1"
|
||||
pin-project-lite = "0.2.0"
|
||||
polling = "2.2.0"
|
||||
rand = "0.8"
|
||||
slab = "0.4.7"
|
||||
socket2 = {features = ["all"], version = "0.4"}
|
||||
socket2 = {features = ["all"], version = "0.5"}
|
||||
waker-fn = "1.1"
|
||||
|
||||
# Used by examples
|
||||
|
@ -33,8 +34,8 @@ clap = { version = "4", features = ["derive"], optional = true }
|
|||
winapi = { version = "0.3.9", features = ["winsock2", "processthreadsapi"] }
|
||||
|
||||
[dev-dependencies]
|
||||
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
||||
gst-app = { package = "gstreamer-app", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
||||
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
|
||||
gst-app = { package = "gstreamer-app", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
|
||||
|
||||
[lib]
|
||||
name = "gstthreadshare"
|
||||
|
@ -42,7 +43,7 @@ crate-type = ["cdylib", "rlib"]
|
|||
path = "src/lib.rs"
|
||||
|
||||
[[example]]
|
||||
name = "benchmark"
|
||||
name = "ts-benchmark"
|
||||
path = "examples/benchmark.rs"
|
||||
|
||||
[[example]]
|
||||
|
@ -58,7 +59,7 @@ name = "ts-standalone"
|
|||
path = "examples/standalone/main.rs"
|
||||
|
||||
[build-dependencies]
|
||||
gst-plugin-version-helper = { path="../../version-helper" }
|
||||
gst-plugin-version-helper = { version = "0.7", path="../../version-helper" }
|
||||
cc = "1.0.38"
|
||||
pkg-config = "0.3.15"
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
../../LICENSE-LGPLv2
|
502
generic/threadshare/LICENSE-LGPLv2
Normal file
502
generic/threadshare/LICENSE-LGPLv2
Normal file
|
@ -0,0 +1,502 @@
|
|||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
|
@ -63,7 +63,7 @@ fn main() {
|
|||
};
|
||||
let is_rtp = args.len() > 6 && (args[6] == "rtp");
|
||||
|
||||
let rtp_caps = gst::Caps::builder("audio/x-rtp")
|
||||
let rtp_caps = gst::Caps::builder("application/x-rtp")
|
||||
.field("media", "audio")
|
||||
.field("payload", 8i32)
|
||||
.field("clock-rate", 8000)
|
||||
|
@ -97,7 +97,7 @@ fn main() {
|
|||
"udpsrc" => {
|
||||
let source = gst::ElementFactory::make("udpsrc")
|
||||
.name(format!("source-{}", i).as_str())
|
||||
.property("port", 40000i32 + i as i32)
|
||||
.property("port", 5004i32 + i as i32)
|
||||
.property("retrieve-sender-address", false)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
@ -108,7 +108,7 @@ fn main() {
|
|||
let context = build_context();
|
||||
let source = gst::ElementFactory::make("ts-udpsrc")
|
||||
.name(format!("source-{}", i).as_str())
|
||||
.property("port", 40000i32 + i as i32)
|
||||
.property("port", 5004i32 + i as i32)
|
||||
.property("context", &context)
|
||||
.property("context-wait", wait)
|
||||
.build()
|
||||
|
@ -158,7 +158,7 @@ fn main() {
|
|||
let context = build_context();
|
||||
let source = gst::ElementFactory::make("ts-tonesrc")
|
||||
.name(format!("source-{}", i).as_str())
|
||||
.property("samples-per-buffer", (wait as u32) * 8000 / 1000)
|
||||
.property("samples-per-buffer", wait * 8000 / 1000)
|
||||
.property("context", &context)
|
||||
.property("context-wait", wait)
|
||||
.build()
|
||||
|
@ -184,22 +184,7 @@ fn main() {
|
|||
pipeline.add_many(elements).unwrap();
|
||||
gst::Element::link_many(elements).unwrap();
|
||||
} else {
|
||||
let queue = if let Some(context) = context {
|
||||
let queue = gst::ElementFactory::make("ts-queue")
|
||||
.name(format!("queue-{}", i).as_str())
|
||||
.property("context", &context)
|
||||
.property("context-wait", wait)
|
||||
.build()
|
||||
.unwrap();
|
||||
queue
|
||||
} else {
|
||||
gst::ElementFactory::make("queue")
|
||||
.name(format!("queue-{}", i).as_str())
|
||||
.build()
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let elements = &[&source, &queue, &sink];
|
||||
let elements = &[&source, &sink];
|
||||
pipeline.add_many(elements).unwrap();
|
||||
gst::Element::link_many(elements).unwrap();
|
||||
}
|
||||
|
@ -268,14 +253,14 @@ fn main() {
|
|||
let elapsed = init.elapsed();
|
||||
gst::info!(
|
||||
CAT,
|
||||
"{:>6.2} / s / stream",
|
||||
"Thrpt: {:>6.2}",
|
||||
total_count * 1_000.0 / elapsed.as_millis() as f32
|
||||
);
|
||||
|
||||
#[cfg(feature = "tuning")]
|
||||
gst::info!(
|
||||
CAT,
|
||||
"{:>6.2}% parked",
|
||||
"Parked: {:>6.2}%",
|
||||
(ctx_0.parked_duration() - parked_init).as_nanos() as f32 * 100.0
|
||||
/ elapsed.as_nanos() as f32
|
||||
);
|
||||
|
|
66
generic/threadshare/examples/standalone/args/clap_args.rs
Normal file
66
generic/threadshare/examples/standalone/args/clap_args.rs
Normal file
|
@ -0,0 +1,66 @@
|
|||
use super::super::CAT;
|
||||
use clap::Parser;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)]
|
||||
pub enum Sink {
|
||||
/// Item handling in PadHandler with async Mutex
|
||||
AsyncMutex,
|
||||
/// Item handling in PadHandler with sync Mutex
|
||||
SyncMutex,
|
||||
/// Item handling in runtime::Task
|
||||
Task,
|
||||
}
|
||||
|
||||
impl Sink {
|
||||
pub fn element_name(self) -> &'static str {
|
||||
use super::super::sink;
|
||||
use Sink::*;
|
||||
match self {
|
||||
AsyncMutex => sink::ASYNC_MUTEX_ELEMENT_NAME,
|
||||
SyncMutex => sink::SYNC_MUTEX_ELEMENT_NAME,
|
||||
Task => sink::TASK_ELEMENT_NAME,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(version)]
|
||||
#[clap(
|
||||
about = "Standalone pipeline threadshare runtime test. Use `GST_DEBUG=ts-standalone*:4` for stats"
|
||||
)]
|
||||
pub struct Args {
|
||||
/// Parallel streams to process.
|
||||
#[clap(short, long, default_value_t = 5000)]
|
||||
pub streams: u32,
|
||||
|
||||
/// Threadshare groups.
|
||||
#[clap(short, long, default_value_t = 2)]
|
||||
pub groups: u32,
|
||||
|
||||
/// Threadshare Context wait in ms (max throttling duration).
|
||||
#[clap(short, long, default_value_t = 20)]
|
||||
pub wait: u32,
|
||||
|
||||
/// Buffer push period in ms.
|
||||
#[clap(short, long, default_value_t = 20)]
|
||||
pub push_period: u32,
|
||||
|
||||
/// Number of buffers per stream to output before sending EOS (-1 = unlimited).
|
||||
#[clap(short, long, default_value_t = 5000)]
|
||||
pub num_buffers: i32,
|
||||
|
||||
/// The Sink variant to use.
|
||||
#[clap(long, value_enum, default_value_t = Sink::SyncMutex)]
|
||||
pub sink: Sink,
|
||||
|
||||
/// Disables statistics logging.
|
||||
#[clap(short, long)]
|
||||
pub disable_stats_log: bool,
|
||||
}
|
||||
|
||||
pub fn args() -> Args {
|
||||
let args = Args::parse();
|
||||
gst::info!(CAT, "{:?}", args);
|
||||
|
||||
args
|
||||
}
|
47
generic/threadshare/examples/standalone/args/default_args.rs
Normal file
47
generic/threadshare/examples/standalone/args/default_args.rs
Normal file
|
@ -0,0 +1,47 @@
|
|||
use super::super::CAT;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct SyncMutexSink;
|
||||
|
||||
impl SyncMutexSink {
|
||||
pub fn element_name(self) -> &'static str {
|
||||
super::super::sink::SYNC_MUTEX_ELEMENT_NAME
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Args {
|
||||
pub streams: u32,
|
||||
pub groups: u32,
|
||||
pub wait: u32,
|
||||
pub push_period: u32,
|
||||
pub num_buffers: i32,
|
||||
pub sink: SyncMutexSink,
|
||||
pub disable_stats_log: bool,
|
||||
}
|
||||
|
||||
impl Default for Args {
|
||||
fn default() -> Self {
|
||||
Args {
|
||||
streams: 5000,
|
||||
groups: 2,
|
||||
wait: 20,
|
||||
push_period: 20,
|
||||
num_buffers: 5000,
|
||||
sink: SyncMutexSink,
|
||||
disable_stats_log: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn args() -> Args {
|
||||
if std::env::args().len() > 1 {
|
||||
gst::warning!(CAT, "Ignoring command line arguments");
|
||||
gst::warning!(CAT, "Build with `--features=clap`");
|
||||
}
|
||||
|
||||
let args = Args::default();
|
||||
gst::warning!(CAT, "{:?}", args);
|
||||
|
||||
args
|
||||
}
|
9
generic/threadshare/examples/standalone/args/mod.rs
Normal file
9
generic/threadshare/examples/standalone/args/mod.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
#[cfg(not(feature = "clap"))]
|
||||
mod default_args;
|
||||
#[cfg(not(feature = "clap"))]
|
||||
pub use default_args::*;
|
||||
|
||||
#[cfg(feature = "clap")]
|
||||
mod clap_args;
|
||||
#[cfg(feature = "clap")]
|
||||
pub use clap_args::*;
|
19
generic/threadshare/examples/standalone/macros.rs
Normal file
19
generic/threadshare/examples/standalone/macros.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
macro_rules! debug_or_trace {
|
||||
($cat:expr, $raise_log_level:expr, $qual:ident: $obj:expr, $rest:tt $(,)?) => {
|
||||
if $raise_log_level {
|
||||
gst::debug!($cat, $qual: $obj, $rest);
|
||||
} else {
|
||||
gst::trace!($cat, $qual: $obj, $rest);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! log_or_trace {
|
||||
($cat:expr, $raise_log_level:expr, $qual:ident: $obj:expr, $rest:tt $(,)?) => {
|
||||
if $raise_log_level {
|
||||
gst::log!($cat, $qual: $obj, $rest);
|
||||
} else {
|
||||
gst::trace!($cat, $qual: $obj, $rest);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,12 +1,21 @@
|
|||
use gst::glib;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
mod args;
|
||||
use args::*;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
mod sink;
|
||||
mod src;
|
||||
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||
gst::DebugCategory::new(
|
||||
"ts-standalone-test-main",
|
||||
"ts-standalone-main",
|
||||
gst::DebugColorFlags::empty(),
|
||||
Some("Thread-sharing standalone test main"),
|
||||
)
|
||||
|
@ -14,7 +23,9 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
|||
|
||||
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
src::register(plugin)?;
|
||||
sink::register(plugin)?;
|
||||
sink::async_mutex::register(plugin)?;
|
||||
sink::sync_mutex::register(plugin)?;
|
||||
sink::task::register(plugin)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -31,91 +42,6 @@ gst::plugin_define!(
|
|||
env!("BUILD_REL_DATE")
|
||||
);
|
||||
|
||||
#[cfg(feature = "clap")]
|
||||
use clap::Parser;
|
||||
|
||||
#[cfg(feature = "clap")]
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(version)]
|
||||
#[clap(
|
||||
about = "Standalone pipeline threadshare runtime test. Use `GST_DEBUG=ts-standalone*:4` for stats"
|
||||
)]
|
||||
struct Args {
|
||||
/// Parallel streams to process.
|
||||
#[clap(short, long, default_value_t = 5000)]
|
||||
streams: u32,
|
||||
|
||||
/// Threadshare groups.
|
||||
#[clap(short, long, default_value_t = 2)]
|
||||
groups: u32,
|
||||
|
||||
/// Threadshare Context wait in ms (max throttling duration).
|
||||
#[clap(short, long, default_value_t = 20)]
|
||||
wait: u32,
|
||||
|
||||
/// Buffer push period in ms.
|
||||
#[clap(short, long, default_value_t = 20)]
|
||||
push_period: u32,
|
||||
|
||||
/// Number of buffers per stream to output before sending EOS (-1 = unlimited).
|
||||
#[clap(short, long, default_value_t = 5000)]
|
||||
num_buffers: i32,
|
||||
|
||||
/// Disables statistics logging.
|
||||
#[clap(short, long)]
|
||||
disable_stats_log: bool,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "clap"))]
|
||||
#[derive(Debug)]
|
||||
struct Args {
|
||||
streams: u32,
|
||||
groups: u32,
|
||||
wait: u32,
|
||||
push_period: u32,
|
||||
num_buffers: i32,
|
||||
disable_stats_log: bool,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "clap"))]
|
||||
impl Default for Args {
|
||||
fn default() -> Self {
|
||||
Args {
|
||||
streams: 5000,
|
||||
groups: 2,
|
||||
wait: 20,
|
||||
push_period: 20,
|
||||
num_buffers: 5000,
|
||||
disable_stats_log: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn args() -> Args {
|
||||
#[cfg(feature = "clap")]
|
||||
let args = {
|
||||
let args = Args::parse();
|
||||
gst::info!(CAT, "{:?}", args);
|
||||
|
||||
args
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "clap"))]
|
||||
let args = {
|
||||
if std::env::args().len() > 1 {
|
||||
gst::warning!(CAT, "Ignoring command line arguments");
|
||||
gst::warning!(CAT, "Build with `--features=clap`");
|
||||
}
|
||||
|
||||
let args = Args::default();
|
||||
gst::warning!(CAT, "{:?}", args);
|
||||
|
||||
args
|
||||
};
|
||||
|
||||
args
|
||||
}
|
||||
|
||||
fn main() {
|
||||
use gst::prelude::*;
|
||||
use std::time::Instant;
|
||||
|
@ -133,8 +59,8 @@ fn main() {
|
|||
for i in 0..args.streams {
|
||||
let ctx_name = format!("standalone {}", i % args.groups);
|
||||
|
||||
let src = gst::ElementFactory::make("ts-standalone-test-src")
|
||||
.name(format!("src-{}", i).as_str())
|
||||
let src = gst::ElementFactory::make(src::ELEMENT_NAME)
|
||||
.name(format!("src-{i}").as_str())
|
||||
.property("context", &ctx_name)
|
||||
.property("context-wait", args.wait)
|
||||
.property("push-period", args.push_period)
|
||||
|
@ -142,16 +68,16 @@ fn main() {
|
|||
.build()
|
||||
.unwrap();
|
||||
|
||||
let sink = gst::ElementFactory::make("ts-standalone-test-sink")
|
||||
.name(format!("sink-{}", i).as_str())
|
||||
let sink = gst::ElementFactory::make(args.sink.element_name())
|
||||
.name(format!("sink-{i}").as_str())
|
||||
.property("context", &ctx_name)
|
||||
.property("context-wait", args.wait)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
if i == 0 {
|
||||
src.set_property("raise-log-level", true);
|
||||
sink.set_property("raise-log-level", true);
|
||||
src.set_property("main-elem", true);
|
||||
sink.set_property("main-elem", true);
|
||||
|
||||
if !args.disable_stats_log {
|
||||
// Don't use the last 5 secs in stats
|
||||
|
@ -179,30 +105,46 @@ fn main() {
|
|||
let l = glib::MainLoop::new(None, false);
|
||||
|
||||
let bus = pipeline.bus().unwrap();
|
||||
let terminated_count = Arc::new(AtomicU32::new(0));
|
||||
let pipeline_clone = pipeline.clone();
|
||||
let l_clone = l.clone();
|
||||
bus.add_watch(move |_, msg| {
|
||||
use gst::MessageView;
|
||||
|
||||
match msg.view() {
|
||||
MessageView::Eos(_) => {
|
||||
// Actually, we don't post EOS (see sinks impl).
|
||||
gst::info!(CAT, "Received eos");
|
||||
l_clone.quit();
|
||||
|
||||
glib::Continue(false)
|
||||
}
|
||||
MessageView::Error(err) => {
|
||||
MessageView::Error(msg) => {
|
||||
if let gst::MessageView::Error(msg) = msg.message().view() {
|
||||
if msg.error().matches(gst::LibraryError::Shutdown) {
|
||||
if terminated_count.fetch_add(1, Ordering::SeqCst) == args.streams - 1 {
|
||||
gst::info!(CAT, "Received all shutdown requests");
|
||||
l_clone.quit();
|
||||
|
||||
return glib::Continue(false);
|
||||
} else {
|
||||
return glib::Continue(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gst::error!(
|
||||
CAT,
|
||||
"Error from {:?}: {} ({:?})",
|
||||
err.src().map(|s| s.path_string()),
|
||||
err.error(),
|
||||
err.debug()
|
||||
msg.src().map(|s| s.path_string()),
|
||||
msg.error(),
|
||||
msg.debug()
|
||||
);
|
||||
l_clone.quit();
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
|
||||
glib::Continue(true)
|
||||
glib::Continue(false)
|
||||
}
|
||||
_ => glib::Continue(true),
|
||||
}
|
||||
})
|
||||
.expect("Failed to add bus watch");
|
||||
|
||||
|
|
334
generic/threadshare/examples/standalone/sink/async_mutex/imp.rs
Normal file
334
generic/threadshare/examples/standalone/sink/async_mutex/imp.rs
Normal file
|
@ -0,0 +1,334 @@
|
|||
// Copyright (C) 2022 François Laignel <fengalin@free.fr>
|
||||
//
|
||||
// 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 futures::future::BoxFuture;
|
||||
use futures::prelude::*;
|
||||
|
||||
use gst::glib;
|
||||
use gst::prelude::*;
|
||||
use gst::subclass::prelude::*;
|
||||
use gst::EventView;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use gstthreadshare::runtime::executor::block_on_or_add_sub_task;
|
||||
use gstthreadshare::runtime::{prelude::*, PadSink};
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
|
||||
use super::super::{Settings, Stats, CAT};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct PadSinkHandlerInner {
|
||||
is_flushing: bool,
|
||||
is_main_elem: bool,
|
||||
last_dts: Option<gst::ClockTime>,
|
||||
segment_start: Option<gst::ClockTime>,
|
||||
stats: Option<Box<Stats>>,
|
||||
}
|
||||
|
||||
impl PadSinkHandlerInner {
|
||||
fn handle_buffer(
|
||||
&mut self,
|
||||
elem: &super::AsyncMutexSink,
|
||||
buffer: gst::Buffer,
|
||||
) -> Result<(), gst::FlowError> {
|
||||
if self.is_flushing {
|
||||
log_or_trace!(
|
||||
CAT,
|
||||
self.is_main_elem,
|
||||
obj: elem,
|
||||
"Discarding {buffer:?} (flushing)"
|
||||
);
|
||||
|
||||
return Err(gst::FlowError::Flushing);
|
||||
}
|
||||
|
||||
debug_or_trace!(CAT, self.is_main_elem, obj: elem, "Received {buffer:?}");
|
||||
|
||||
let dts = buffer
|
||||
.dts()
|
||||
.expect("Buffer without dts")
|
||||
.checked_sub(self.segment_start.expect("Buffer without Time Segment"))
|
||||
.expect("dts before Segment start");
|
||||
|
||||
if let Some(last_dts) = self.last_dts {
|
||||
let cur_ts = elem.current_running_time().unwrap();
|
||||
let latency: Duration = (cur_ts - dts).into();
|
||||
let interval: Duration = (dts - last_dts).into();
|
||||
|
||||
if let Some(stats) = self.stats.as_mut() {
|
||||
stats.add_buffer(latency, interval);
|
||||
}
|
||||
|
||||
debug_or_trace!(CAT, self.is_main_elem, obj: elem, "o latency {latency:.2?}");
|
||||
debug_or_trace!(
|
||||
CAT,
|
||||
self.is_main_elem,
|
||||
obj: elem,
|
||||
"o interval {interval:.2?}",
|
||||
);
|
||||
}
|
||||
|
||||
self.last_dts = Some(dts);
|
||||
|
||||
log_or_trace!(CAT, self.is_main_elem, obj: elem, "Buffer processed");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct AsyncPadSinkHandler(Arc<futures::lock::Mutex<PadSinkHandlerInner>>);
|
||||
|
||||
impl PadSinkHandler for AsyncPadSinkHandler {
|
||||
type ElementImpl = AsyncMutexSink;
|
||||
|
||||
fn sink_chain(
|
||||
self,
|
||||
_pad: gst::Pad,
|
||||
elem: super::AsyncMutexSink,
|
||||
buffer: gst::Buffer,
|
||||
) -> BoxFuture<'static, Result<gst::FlowSuccess, gst::FlowError>> {
|
||||
async move {
|
||||
if self.0.lock().await.handle_buffer(&elem, buffer).is_err() {
|
||||
return Err(gst::FlowError::Flushing);
|
||||
}
|
||||
|
||||
Ok(gst::FlowSuccess::Ok)
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn sink_event_serialized(
|
||||
self,
|
||||
_pad: gst::Pad,
|
||||
elem: super::AsyncMutexSink,
|
||||
event: gst::Event,
|
||||
) -> BoxFuture<'static, bool> {
|
||||
async move {
|
||||
match event.view() {
|
||||
EventView::Eos(_) => {
|
||||
{
|
||||
let mut inner = self.0.lock().await;
|
||||
debug_or_trace!(CAT, inner.is_main_elem, obj: elem, "EOS");
|
||||
inner.is_flushing = true;
|
||||
}
|
||||
|
||||
// When each element sends its own EOS message,
|
||||
// it takes ages for the pipeline to process all of them.
|
||||
// Let's just post an error message and let main shuts down
|
||||
// after all streams have posted this message.
|
||||
let _ = elem
|
||||
.post_message(gst::message::Error::new(gst::LibraryError::Shutdown, "EOS"));
|
||||
}
|
||||
EventView::FlushStop(_) => {
|
||||
self.0.lock().await.is_flushing = false;
|
||||
}
|
||||
EventView::Segment(evt) => {
|
||||
if let Some(time_seg) = evt.segment().downcast_ref::<gst::ClockTime>() {
|
||||
self.0.lock().await.segment_start = time_seg.start();
|
||||
}
|
||||
}
|
||||
EventView::SinkMessage(evt) => {
|
||||
let _ = elem.post_message(evt.message());
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn sink_event(self, _pad: &gst::Pad, _imp: &AsyncMutexSink, event: gst::Event) -> bool {
|
||||
if let EventView::FlushStart(..) = event.view() {
|
||||
block_on_or_add_sub_task(async move { self.0.lock().await.is_flushing = true });
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncPadSinkHandler {
|
||||
fn prepare(&self, is_main_elem: bool, stats: Option<Stats>) {
|
||||
futures::executor::block_on(async move {
|
||||
let mut inner = self.0.lock().await;
|
||||
inner.is_main_elem = is_main_elem;
|
||||
inner.stats = stats.map(Box::new);
|
||||
});
|
||||
}
|
||||
|
||||
fn start(&self) {
|
||||
futures::executor::block_on(async move {
|
||||
let mut inner = self.0.lock().await;
|
||||
|
||||
inner.is_flushing = false;
|
||||
inner.last_dts = None;
|
||||
|
||||
if let Some(stats) = inner.stats.as_mut() {
|
||||
stats.start();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn stop(&self) {
|
||||
futures::executor::block_on(async move {
|
||||
let mut inner = self.0.lock().await;
|
||||
inner.is_flushing = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AsyncMutexSink {
|
||||
sink_pad: PadSink,
|
||||
sink_pad_handler: AsyncPadSinkHandler,
|
||||
settings: Mutex<Settings>,
|
||||
}
|
||||
|
||||
impl AsyncMutexSink {
|
||||
fn prepare(&self) -> Result<(), gst::ErrorMessage> {
|
||||
let settings = self.settings.lock().unwrap();
|
||||
debug_or_trace!(CAT, settings.is_main_elem, imp: self, "Preparing");
|
||||
let stats = if settings.logs_stats {
|
||||
Some(Stats::new(
|
||||
settings.max_buffers,
|
||||
settings.push_period + settings.context_wait / 2,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
self.sink_pad_handler.prepare(settings.is_main_elem, stats);
|
||||
debug_or_trace!(CAT, settings.is_main_elem, imp: self, "Prepared");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn stop(&self) -> Result<(), gst::ErrorMessage> {
|
||||
let is_main_elem = self.settings.lock().unwrap().is_main_elem;
|
||||
debug_or_trace!(CAT, is_main_elem, imp: self, "Stopping");
|
||||
self.sink_pad_handler.stop();
|
||||
debug_or_trace!(CAT, is_main_elem, imp: self, "Stopped");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn start(&self) -> Result<(), gst::ErrorMessage> {
|
||||
let is_main_elem = self.settings.lock().unwrap().is_main_elem;
|
||||
debug_or_trace!(CAT, is_main_elem, imp: self, "Starting");
|
||||
self.sink_pad_handler.start();
|
||||
debug_or_trace!(CAT, is_main_elem, imp: self, "Started");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for AsyncMutexSink {
|
||||
const NAME: &'static str = "TsStandaloneAsyncMutexSink";
|
||||
type Type = super::AsyncMutexSink;
|
||||
type ParentType = gst::Element;
|
||||
|
||||
fn with_class(klass: &Self::Class) -> Self {
|
||||
let sink_pad_handler = AsyncPadSinkHandler::default();
|
||||
Self {
|
||||
sink_pad: PadSink::new(
|
||||
gst::Pad::from_template(&klass.pad_template("sink").unwrap(), Some("sink")),
|
||||
sink_pad_handler.clone(),
|
||||
),
|
||||
sink_pad_handler,
|
||||
settings: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for AsyncMutexSink {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(Settings::properties);
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
self.settings.lock().unwrap().set_property(id, value, pspec);
|
||||
}
|
||||
|
||||
fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
self.settings.lock().unwrap().property(id, pspec)
|
||||
}
|
||||
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
let obj = self.obj();
|
||||
obj.add_pad(self.sink_pad.gst_pad()).unwrap();
|
||||
obj.set_element_flags(gst::ElementFlags::SINK);
|
||||
}
|
||||
}
|
||||
|
||||
impl GstObjectImpl for AsyncMutexSink {}
|
||||
|
||||
impl ElementImpl for AsyncMutexSink {
|
||||
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||
gst::subclass::ElementMetadata::new(
|
||||
"Thread-sharing standalone test async mutex sink",
|
||||
"Sink/Test",
|
||||
"Thread-sharing standalone test async mutex sink",
|
||||
"François Laignel <fengalin@free.fr>",
|
||||
)
|
||||
});
|
||||
|
||||
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:?}");
|
||||
|
||||
match transition {
|
||||
gst::StateChange::NullToReady => {
|
||||
self.prepare().map_err(|err| {
|
||||
self.post_error_message(err);
|
||||
gst::StateChangeError
|
||||
})?;
|
||||
}
|
||||
gst::StateChange::ReadyToPaused => {
|
||||
self.start().map_err(|_| gst::StateChangeError)?;
|
||||
}
|
||||
gst::StateChange::PausedToReady => {
|
||||
self.stop().map_err(|_| gst::StateChangeError)?;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.parent_change_state(transition)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
use gst::glib;
|
||||
use gst::prelude::*;
|
||||
|
||||
mod imp;
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct AsyncMutexSink(ObjectSubclass<imp::AsyncMutexSink>) @extends gst::Element, gst::Object;
|
||||
}
|
||||
|
||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
gst::Element::register(
|
||||
Some(plugin),
|
||||
super::ASYNC_MUTEX_ELEMENT_NAME,
|
||||
gst::Rank::None,
|
||||
AsyncMutexSink::static_type(),
|
||||
)
|
||||
}
|
|
@ -1,864 +0,0 @@
|
|||
// Copyright (C) 2022 François Laignel <fengalin@free.fr>
|
||||
//
|
||||
// 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 futures::future::BoxFuture;
|
||||
use futures::prelude::*;
|
||||
use futures::stream::Peekable;
|
||||
|
||||
use gst::error_msg;
|
||||
use gst::glib;
|
||||
use gst::prelude::*;
|
||||
use gst::subclass::prelude::*;
|
||||
use gst::EventView;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use gstthreadshare::runtime::prelude::*;
|
||||
use gstthreadshare::runtime::{Context, PadSink, Task};
|
||||
|
||||
use std::sync::Mutex;
|
||||
use std::task::Poll;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||
gst::DebugCategory::new(
|
||||
"ts-standalone-test-sink",
|
||||
gst::DebugColorFlags::empty(),
|
||||
Some("Thread-sharing standalone test sink"),
|
||||
)
|
||||
});
|
||||
|
||||
const DEFAULT_CONTEXT: &str = "";
|
||||
const DEFAULT_CONTEXT_WAIT: Duration = Duration::from_millis(20);
|
||||
const DEFAULT_PUSH_PERIOD: Duration = Duration::from_millis(20);
|
||||
const DEFAULT_MAX_BUFFERS: i32 = 50 * (100 - 25);
|
||||
|
||||
const LOG_PERIOD: Duration = Duration::from_secs(20);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Settings {
|
||||
context: String,
|
||||
context_wait: Duration,
|
||||
raise_log_level: bool,
|
||||
logs_stats: bool,
|
||||
push_period: Duration,
|
||||
max_buffers: Option<u32>,
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Settings {
|
||||
context: DEFAULT_CONTEXT.into(),
|
||||
context_wait: DEFAULT_CONTEXT_WAIT,
|
||||
raise_log_level: false,
|
||||
logs_stats: false,
|
||||
push_period: DEFAULT_PUSH_PERIOD,
|
||||
max_buffers: Some(DEFAULT_MAX_BUFFERS as u32),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum StreamItem {
|
||||
Buffer(gst::Buffer),
|
||||
Event(gst::Event),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct TestSinkPadHandler;
|
||||
|
||||
impl PadSinkHandler for TestSinkPadHandler {
|
||||
type ElementImpl = TestSink;
|
||||
|
||||
fn sink_chain(
|
||||
self,
|
||||
_pad: gst::Pad,
|
||||
elem: super::TestSink,
|
||||
buffer: gst::Buffer,
|
||||
) -> BoxFuture<'static, Result<gst::FlowSuccess, gst::FlowError>> {
|
||||
let sender = elem.imp().clone_item_sender();
|
||||
async move {
|
||||
if sender.send_async(StreamItem::Buffer(buffer)).await.is_err() {
|
||||
gst::debug!(CAT, obj: elem, "Flushing");
|
||||
return Err(gst::FlowError::Flushing);
|
||||
}
|
||||
|
||||
Ok(gst::FlowSuccess::Ok)
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn sink_chain_list(
|
||||
self,
|
||||
_pad: gst::Pad,
|
||||
elem: super::TestSink,
|
||||
list: gst::BufferList,
|
||||
) -> BoxFuture<'static, Result<gst::FlowSuccess, gst::FlowError>> {
|
||||
let sender = elem.imp().clone_item_sender();
|
||||
async move {
|
||||
for buffer in list.iter_owned() {
|
||||
if sender.send_async(StreamItem::Buffer(buffer)).await.is_err() {
|
||||
gst::debug!(CAT, obj: elem, "Flushing");
|
||||
return Err(gst::FlowError::Flushing);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(gst::FlowSuccess::Ok)
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn sink_event_serialized(
|
||||
self,
|
||||
_pad: gst::Pad,
|
||||
elem: super::TestSink,
|
||||
event: gst::Event,
|
||||
) -> BoxFuture<'static, bool> {
|
||||
let sender = elem.imp().clone_item_sender();
|
||||
async move {
|
||||
if let EventView::FlushStop(_) = event.view() {
|
||||
let imp = elem.imp();
|
||||
return imp.task.flush_stop().await_maybe_on_context().is_ok();
|
||||
} else if sender.send_async(StreamItem::Event(event)).await.is_err() {
|
||||
gst::debug!(CAT, obj: elem, "Flushing");
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn sink_event(self, _pad: &gst::Pad, imp: &TestSink, event: gst::Event) -> bool {
|
||||
if let EventView::FlushStart(..) = event.view() {
|
||||
return imp.task.flush_start().await_maybe_on_context().is_ok();
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Stats {
|
||||
must_log: bool,
|
||||
ramp_up_instant: Option<Instant>,
|
||||
log_start_instant: Option<Instant>,
|
||||
last_delta_instant: Option<Instant>,
|
||||
max_buffers: Option<f32>,
|
||||
buffer_count: f32,
|
||||
buffer_count_delta: f32,
|
||||
latency_sum: f32,
|
||||
latency_square_sum: f32,
|
||||
latency_sum_delta: f32,
|
||||
latency_square_sum_delta: f32,
|
||||
latency_min: Duration,
|
||||
latency_min_delta: Duration,
|
||||
latency_max: Duration,
|
||||
latency_max_delta: Duration,
|
||||
interval_sum: f32,
|
||||
interval_square_sum: f32,
|
||||
interval_sum_delta: f32,
|
||||
interval_square_sum_delta: f32,
|
||||
interval_min: Duration,
|
||||
interval_min_delta: Duration,
|
||||
interval_max: Duration,
|
||||
interval_max_delta: Duration,
|
||||
interval_late_warn: Duration,
|
||||
interval_late_count: f32,
|
||||
interval_late_count_delta: f32,
|
||||
#[cfg(feature = "tuning")]
|
||||
parked_duration_init: Duration,
|
||||
}
|
||||
|
||||
impl Stats {
|
||||
fn start(&mut self) {
|
||||
if !self.must_log {
|
||||
return;
|
||||
}
|
||||
|
||||
self.buffer_count = 0.0;
|
||||
self.buffer_count_delta = 0.0;
|
||||
self.latency_sum = 0.0;
|
||||
self.latency_square_sum = 0.0;
|
||||
self.latency_sum_delta = 0.0;
|
||||
self.latency_square_sum_delta = 0.0;
|
||||
self.latency_min = Duration::MAX;
|
||||
self.latency_min_delta = Duration::MAX;
|
||||
self.latency_max = Duration::ZERO;
|
||||
self.latency_max_delta = Duration::ZERO;
|
||||
self.interval_sum = 0.0;
|
||||
self.interval_square_sum = 0.0;
|
||||
self.interval_sum_delta = 0.0;
|
||||
self.interval_square_sum_delta = 0.0;
|
||||
self.interval_min = Duration::MAX;
|
||||
self.interval_min_delta = Duration::MAX;
|
||||
self.interval_max = Duration::ZERO;
|
||||
self.interval_max_delta = Duration::ZERO;
|
||||
self.interval_late_count = 0.0;
|
||||
self.interval_late_count_delta = 0.0;
|
||||
self.last_delta_instant = None;
|
||||
self.log_start_instant = None;
|
||||
|
||||
self.ramp_up_instant = Some(Instant::now());
|
||||
gst::info!(CAT, "First stats logs in {:2?}", 2 * LOG_PERIOD);
|
||||
}
|
||||
|
||||
fn is_active(&mut self) -> bool {
|
||||
if !self.must_log {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(ramp_up_instant) = self.ramp_up_instant {
|
||||
if ramp_up_instant.elapsed() < LOG_PERIOD {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.ramp_up_instant = None;
|
||||
gst::info!(CAT, "Ramp up complete. Stats logs in {:2?}", LOG_PERIOD);
|
||||
self.log_start_instant = Some(Instant::now());
|
||||
self.last_delta_instant = self.log_start_instant;
|
||||
|
||||
#[cfg(feature = "tuning")]
|
||||
{
|
||||
self.parked_duration_init = Context::current().unwrap().parked_duration();
|
||||
}
|
||||
}
|
||||
|
||||
use std::cmp::Ordering::*;
|
||||
match self.max_buffers.opt_cmp(self.buffer_count) {
|
||||
Some(Equal) => {
|
||||
self.log_global();
|
||||
self.buffer_count += 1.0;
|
||||
false
|
||||
}
|
||||
Some(Less) => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn add_buffer(&mut self, latency: Duration, interval: Duration) {
|
||||
if !self.is_active() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.buffer_count += 1.0;
|
||||
self.buffer_count_delta += 1.0;
|
||||
|
||||
// Latency
|
||||
let latency_f32 = latency.as_nanos() as f32;
|
||||
let latency_square = latency_f32.powi(2);
|
||||
|
||||
self.latency_sum += latency_f32;
|
||||
self.latency_square_sum += latency_square;
|
||||
self.latency_min = self.latency_min.min(latency);
|
||||
self.latency_max = self.latency_max.max(latency);
|
||||
|
||||
self.latency_sum_delta += latency_f32;
|
||||
self.latency_square_sum_delta += latency_square;
|
||||
self.latency_min_delta = self.latency_min_delta.min(latency);
|
||||
self.latency_max_delta = self.latency_max_delta.max(latency);
|
||||
|
||||
// Interval
|
||||
let interval_f32 = interval.as_nanos() as f32;
|
||||
let interval_square = interval_f32.powi(2);
|
||||
|
||||
self.interval_sum += interval_f32;
|
||||
self.interval_square_sum += interval_square;
|
||||
self.interval_min = self.interval_min.min(interval);
|
||||
self.interval_max = self.interval_max.max(interval);
|
||||
|
||||
self.interval_sum_delta += interval_f32;
|
||||
self.interval_square_sum_delta += interval_square;
|
||||
self.interval_min_delta = self.interval_min_delta.min(interval);
|
||||
self.interval_max_delta = self.interval_max_delta.max(interval);
|
||||
|
||||
if interval > self.interval_late_warn {
|
||||
self.interval_late_count += 1.0;
|
||||
self.interval_late_count_delta += 1.0;
|
||||
}
|
||||
|
||||
let delta_duration = match self.last_delta_instant {
|
||||
Some(last_delta) => last_delta.elapsed(),
|
||||
None => return,
|
||||
};
|
||||
|
||||
if delta_duration < LOG_PERIOD {
|
||||
return;
|
||||
}
|
||||
|
||||
self.last_delta_instant = Some(Instant::now());
|
||||
|
||||
gst::info!(CAT, "Delta stats:");
|
||||
let interval_mean = self.interval_sum_delta / self.buffer_count_delta;
|
||||
let interval_std_dev = f32::sqrt(
|
||||
self.interval_square_sum_delta / self.buffer_count_delta - interval_mean.powi(2),
|
||||
);
|
||||
|
||||
gst::info!(
|
||||
CAT,
|
||||
"o interval: mean {:4.2?} σ {:4.1?} [{:4.1?}, {:4.1?}]",
|
||||
Duration::from_nanos(interval_mean as u64),
|
||||
Duration::from_nanos(interval_std_dev as u64),
|
||||
self.interval_min_delta,
|
||||
self.interval_max_delta,
|
||||
);
|
||||
|
||||
if self.interval_late_count_delta > f32::EPSILON {
|
||||
gst::warning!(
|
||||
CAT,
|
||||
"o {:5.2}% late buffers",
|
||||
100f32 * self.interval_late_count_delta / self.buffer_count_delta
|
||||
);
|
||||
}
|
||||
|
||||
self.interval_sum_delta = 0.0;
|
||||
self.interval_square_sum_delta = 0.0;
|
||||
self.interval_min_delta = Duration::MAX;
|
||||
self.interval_max_delta = Duration::ZERO;
|
||||
self.interval_late_count_delta = 0.0;
|
||||
|
||||
let latency_mean = self.latency_sum_delta / self.buffer_count_delta;
|
||||
let latency_std_dev = f32::sqrt(
|
||||
self.latency_square_sum_delta / self.buffer_count_delta - latency_mean.powi(2),
|
||||
);
|
||||
|
||||
gst::info!(
|
||||
CAT,
|
||||
"o latency: mean {:4.2?} σ {:4.1?} [{:4.1?}, {:4.1?}]",
|
||||
Duration::from_nanos(latency_mean as u64),
|
||||
Duration::from_nanos(latency_std_dev as u64),
|
||||
self.latency_min_delta,
|
||||
self.latency_max_delta,
|
||||
);
|
||||
|
||||
self.latency_sum_delta = 0.0;
|
||||
self.latency_square_sum_delta = 0.0;
|
||||
self.latency_min_delta = Duration::MAX;
|
||||
self.latency_max_delta = Duration::ZERO;
|
||||
|
||||
self.buffer_count_delta = 0.0;
|
||||
}
|
||||
|
||||
fn log_global(&mut self) {
|
||||
if self.buffer_count < 1.0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let _log_start = if let Some(log_start) = self.log_start_instant {
|
||||
log_start
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
gst::info!(CAT, "Global stats:");
|
||||
|
||||
#[cfg(feature = "tuning")]
|
||||
{
|
||||
let duration = _log_start.elapsed();
|
||||
let parked_duration =
|
||||
Context::current().unwrap().parked_duration() - self.parked_duration_init;
|
||||
gst::info!(
|
||||
CAT,
|
||||
"o parked: {parked_duration:4.2?} ({:5.2?}%)",
|
||||
(parked_duration.as_nanos() as f32 * 100.0 / duration.as_nanos() as f32)
|
||||
);
|
||||
}
|
||||
|
||||
let interval_mean = self.interval_sum / self.buffer_count;
|
||||
let interval_std_dev =
|
||||
f32::sqrt(self.interval_square_sum / self.buffer_count - interval_mean.powi(2));
|
||||
|
||||
gst::info!(
|
||||
CAT,
|
||||
"o interval: mean {:4.2?} σ {:4.1?} [{:4.1?}, {:4.1?}]",
|
||||
Duration::from_nanos(interval_mean as u64),
|
||||
Duration::from_nanos(interval_std_dev as u64),
|
||||
self.interval_min,
|
||||
self.interval_max,
|
||||
);
|
||||
|
||||
if self.interval_late_count > f32::EPSILON {
|
||||
gst::warning!(
|
||||
CAT,
|
||||
"o {:5.2}% late buffers",
|
||||
100f32 * self.interval_late_count / self.buffer_count
|
||||
);
|
||||
}
|
||||
|
||||
let latency_mean = self.latency_sum / self.buffer_count;
|
||||
let latency_std_dev =
|
||||
f32::sqrt(self.latency_square_sum / self.buffer_count - latency_mean.powi(2));
|
||||
|
||||
gst::info!(
|
||||
CAT,
|
||||
"o latency: mean {:4.2?} σ {:4.1?} [{:4.1?}, {:4.1?}]",
|
||||
Duration::from_nanos(latency_mean as u64),
|
||||
Duration::from_nanos(latency_std_dev as u64),
|
||||
self.latency_min,
|
||||
self.latency_max,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
struct TestSinkTask {
|
||||
element: super::TestSink,
|
||||
raise_log_level: bool,
|
||||
last_dts: Option<gst::ClockTime>,
|
||||
item_receiver: Peekable<flume::r#async::RecvStream<'static, StreamItem>>,
|
||||
stats: Stats,
|
||||
segment: Option<gst::Segment>,
|
||||
}
|
||||
|
||||
impl TestSinkTask {
|
||||
fn new(element: &super::TestSink, item_receiver: flume::Receiver<StreamItem>) -> Self {
|
||||
TestSinkTask {
|
||||
element: element.clone(),
|
||||
raise_log_level: false,
|
||||
last_dts: None,
|
||||
item_receiver: item_receiver.into_stream().peekable(),
|
||||
stats: Stats::default(),
|
||||
segment: None,
|
||||
}
|
||||
}
|
||||
|
||||
async fn flush(&mut self) {
|
||||
// Purge the channel
|
||||
while let Poll::Ready(Some(_item)) = futures::poll!(self.item_receiver.next()) {}
|
||||
}
|
||||
}
|
||||
|
||||
impl TaskImpl for TestSinkTask {
|
||||
type Item = StreamItem;
|
||||
|
||||
fn prepare(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
||||
async move {
|
||||
let sink = self.element.imp();
|
||||
let settings = sink.settings.lock().unwrap();
|
||||
self.raise_log_level = settings.raise_log_level;
|
||||
|
||||
if self.raise_log_level {
|
||||
gst::log!(CAT, obj: self.element, "Preparing Task");
|
||||
} else {
|
||||
gst::trace!(CAT, obj: self.element, "Preparing Task");
|
||||
}
|
||||
|
||||
self.stats.must_log = settings.logs_stats;
|
||||
self.stats.max_buffers = settings.max_buffers.map(|max_buffers| max_buffers as f32);
|
||||
self.stats.interval_late_warn = settings.push_period + settings.context_wait / 2;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn start(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
||||
async {
|
||||
if self.raise_log_level {
|
||||
gst::log!(CAT, obj: self.element, "Starting Task");
|
||||
} else {
|
||||
gst::trace!(CAT, obj: self.element, "Starting Task");
|
||||
}
|
||||
|
||||
self.last_dts = None;
|
||||
self.stats.start();
|
||||
Ok(())
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn stop(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
||||
async {
|
||||
if self.raise_log_level {
|
||||
gst::log!(CAT, obj: self.element, "Stopping Task");
|
||||
} else {
|
||||
gst::trace!(CAT, obj: self.element, "Stopping Task");
|
||||
}
|
||||
|
||||
self.flush().await;
|
||||
Ok(())
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn try_next(&mut self) -> BoxFuture<'_, Result<StreamItem, gst::FlowError>> {
|
||||
async move {
|
||||
let item = self.item_receiver.next().await.unwrap();
|
||||
|
||||
if self.raise_log_level {
|
||||
gst::log!(CAT, obj: self.element, "Popped item");
|
||||
} else {
|
||||
gst::trace!(CAT, obj: self.element, "Popped item");
|
||||
}
|
||||
|
||||
Ok(item)
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn handle_item(&mut self, item: StreamItem) -> BoxFuture<'_, Result<(), gst::FlowError>> {
|
||||
async move {
|
||||
if self.raise_log_level {
|
||||
gst::debug!(CAT, obj: self.element, "Received {:?}", item);
|
||||
} else {
|
||||
gst::trace!(CAT, obj: self.element, "Received {:?}", item);
|
||||
}
|
||||
|
||||
match item {
|
||||
StreamItem::Buffer(buffer) => {
|
||||
let dts = self
|
||||
.segment
|
||||
.as_ref()
|
||||
.and_then(|segment| {
|
||||
segment
|
||||
.downcast_ref::<gst::format::Time>()
|
||||
.and_then(|segment| segment.to_running_time(buffer.dts()))
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
if let Some(last_dts) = self.last_dts {
|
||||
let cur_ts = self.element.current_running_time().unwrap();
|
||||
let latency: Duration = (cur_ts - dts).into();
|
||||
let interval: Duration = (dts - last_dts).into();
|
||||
|
||||
self.stats.add_buffer(latency, interval);
|
||||
|
||||
if self.raise_log_level {
|
||||
gst::debug!(CAT, obj: self.element, "o latency {:.2?}", latency);
|
||||
gst::debug!(CAT, obj: self.element, "o interval {:.2?}", interval);
|
||||
} else {
|
||||
gst::trace!(CAT, obj: self.element, "o latency {:.2?}", latency);
|
||||
gst::trace!(CAT, obj: self.element, "o interval {:.2?}", interval);
|
||||
}
|
||||
}
|
||||
|
||||
self.last_dts = Some(dts);
|
||||
|
||||
if self.raise_log_level {
|
||||
gst::log!(CAT, obj: self.element, "Buffer processed");
|
||||
} else {
|
||||
gst::trace!(CAT, obj: self.element, "Buffer processed");
|
||||
}
|
||||
}
|
||||
StreamItem::Event(event) => match event.view() {
|
||||
EventView::Eos(_) => {
|
||||
if self.raise_log_level {
|
||||
gst::debug!(CAT, obj: self.element, "EOS");
|
||||
} else {
|
||||
gst::trace!(CAT, obj: self.element, "EOS");
|
||||
}
|
||||
|
||||
let elem = self.element.clone();
|
||||
self.element.call_async(move |_| {
|
||||
let _ =
|
||||
elem.post_message(gst::message::Eos::builder().src(&elem).build());
|
||||
});
|
||||
|
||||
return Err(gst::FlowError::Eos);
|
||||
}
|
||||
EventView::Segment(e) => {
|
||||
self.segment = Some(e.segment().clone());
|
||||
}
|
||||
EventView::SinkMessage(e) => {
|
||||
let _ = self.element.post_message(e.message());
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TestSink {
|
||||
sink_pad: PadSink,
|
||||
task: Task,
|
||||
item_sender: Mutex<Option<flume::Sender<StreamItem>>>,
|
||||
settings: Mutex<Settings>,
|
||||
}
|
||||
|
||||
impl TestSink {
|
||||
#[track_caller]
|
||||
fn clone_item_sender(&self) -> flume::Sender<StreamItem> {
|
||||
self.item_sender.lock().unwrap().as_ref().unwrap().clone()
|
||||
}
|
||||
|
||||
fn prepare(&self) -> Result<(), gst::ErrorMessage> {
|
||||
let raise_log_level = self.settings.lock().unwrap().raise_log_level;
|
||||
if raise_log_level {
|
||||
gst::debug!(CAT, imp: self, "Preparing");
|
||||
} else {
|
||||
gst::trace!(CAT, imp: self, "Preparing");
|
||||
}
|
||||
|
||||
let context = {
|
||||
let settings = self.settings.lock().unwrap();
|
||||
|
||||
Context::acquire(&settings.context, settings.context_wait).map_err(|err| {
|
||||
error_msg!(
|
||||
gst::ResourceError::OpenWrite,
|
||||
["Failed to acquire Context: {}", err]
|
||||
)
|
||||
})?
|
||||
};
|
||||
|
||||
// Enable backpressure for items
|
||||
let (item_sender, item_receiver) = flume::bounded(0);
|
||||
let task_impl = TestSinkTask::new(&*self.obj(), item_receiver);
|
||||
self.task.prepare(task_impl, context).block_on()?;
|
||||
|
||||
*self.item_sender.lock().unwrap() = Some(item_sender);
|
||||
|
||||
if raise_log_level {
|
||||
gst::debug!(CAT, imp: self, "Prepared");
|
||||
} else {
|
||||
gst::trace!(CAT, imp: self, "Prepared");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unprepare(&self) {
|
||||
let raise_log_level = self.settings.lock().unwrap().raise_log_level;
|
||||
if raise_log_level {
|
||||
gst::debug!(CAT, imp: self, "Unpreparing");
|
||||
} else {
|
||||
gst::trace!(CAT, imp: self, "Unpreparing");
|
||||
}
|
||||
|
||||
self.task.unprepare().block_on().unwrap();
|
||||
|
||||
if raise_log_level {
|
||||
gst::debug!(CAT, imp: self, "Unprepared");
|
||||
} else {
|
||||
gst::trace!(CAT, imp: self, "Unprepared");
|
||||
}
|
||||
}
|
||||
|
||||
fn stop(&self) -> Result<(), gst::ErrorMessage> {
|
||||
let raise_log_level = self.settings.lock().unwrap().raise_log_level;
|
||||
if raise_log_level {
|
||||
gst::debug!(CAT, imp: self, "Stopping");
|
||||
} else {
|
||||
gst::trace!(CAT, imp: self, "Stopping");
|
||||
}
|
||||
|
||||
self.task.stop().block_on()?;
|
||||
|
||||
if raise_log_level {
|
||||
gst::debug!(CAT, imp: self, "Stopped");
|
||||
} else {
|
||||
gst::trace!(CAT, imp: self, "Stopped");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn start(&self) -> Result<(), gst::ErrorMessage> {
|
||||
let raise_log_level = self.settings.lock().unwrap().raise_log_level;
|
||||
if raise_log_level {
|
||||
gst::debug!(CAT, imp: self, "Starting");
|
||||
} else {
|
||||
gst::trace!(CAT, imp: self, "Starting");
|
||||
}
|
||||
|
||||
self.task.start().block_on()?;
|
||||
|
||||
if raise_log_level {
|
||||
gst::debug!(CAT, imp: self, "Started");
|
||||
} else {
|
||||
gst::trace!(CAT, imp: self, "Started");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for TestSink {
|
||||
const NAME: &'static str = "StandaloneTestSink";
|
||||
type Type = super::TestSink;
|
||||
type ParentType = gst::Element;
|
||||
|
||||
fn with_class(klass: &Self::Class) -> Self {
|
||||
Self {
|
||||
sink_pad: PadSink::new(
|
||||
gst::Pad::from_template(&klass.pad_template("sink").unwrap(), Some("sink")),
|
||||
TestSinkPadHandler,
|
||||
),
|
||||
task: Task::default(),
|
||||
item_sender: Default::default(),
|
||||
settings: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for TestSink {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![
|
||||
glib::ParamSpecString::builder("context")
|
||||
.nick("Context")
|
||||
.blurb("Context name to share threads with")
|
||||
.default_value(Some(DEFAULT_CONTEXT))
|
||||
.build(),
|
||||
glib::ParamSpecUInt::builder("context-wait")
|
||||
.nick("Context Wait")
|
||||
.blurb("Throttle poll loop to run at most once every this many ms")
|
||||
.maximum(1000)
|
||||
.default_value(DEFAULT_CONTEXT_WAIT.as_millis() as u32)
|
||||
.build(),
|
||||
glib::ParamSpecBoolean::builder("raise-log-level")
|
||||
.nick("Raise log level")
|
||||
.blurb("Raises the log level so that this element stands out")
|
||||
.write_only()
|
||||
.build(),
|
||||
glib::ParamSpecBoolean::builder("logs-stats")
|
||||
.nick("Logs Stats")
|
||||
.blurb("Whether statistics should be logged")
|
||||
.write_only()
|
||||
.build(),
|
||||
glib::ParamSpecUInt::builder("push-period")
|
||||
.nick("Src buffer Push Period")
|
||||
.blurb("Push period used by `src` element (used for stats warnings)")
|
||||
.default_value(DEFAULT_PUSH_PERIOD.as_millis() as u32)
|
||||
.build(),
|
||||
glib::ParamSpecInt::builder("max-buffers")
|
||||
.nick("Max Buffers")
|
||||
.blurb("Number of buffers to count before stopping stats (-1 = unlimited)")
|
||||
.minimum(-1i32)
|
||||
.default_value(DEFAULT_MAX_BUFFERS)
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
let mut settings = self.settings.lock().unwrap();
|
||||
match pspec.name() {
|
||||
"context" => {
|
||||
settings.context = value
|
||||
.get::<Option<String>>()
|
||||
.expect("type checked upstream")
|
||||
.unwrap_or_else(|| DEFAULT_CONTEXT.into());
|
||||
}
|
||||
"context-wait" => {
|
||||
settings.context_wait = Duration::from_millis(
|
||||
value.get::<u32>().expect("type checked upstream").into(),
|
||||
);
|
||||
}
|
||||
"raise-log-level" => {
|
||||
settings.raise_log_level = value.get::<bool>().expect("type checked upstream");
|
||||
}
|
||||
"logs-stats" => {
|
||||
let logs_stats = value.get().expect("type checked upstream");
|
||||
settings.logs_stats = logs_stats;
|
||||
}
|
||||
"push-period" => {
|
||||
settings.push_period = Duration::from_millis(
|
||||
value.get::<u32>().expect("type checked upstream").into(),
|
||||
);
|
||||
}
|
||||
"max-buffers" => {
|
||||
let value = value.get::<i32>().expect("type checked upstream");
|
||||
settings.max_buffers = if value > 0 { Some(value as u32) } else { None };
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
let settings = self.settings.lock().unwrap();
|
||||
match pspec.name() {
|
||||
"context" => settings.context.to_value(),
|
||||
"context-wait" => (settings.context_wait.as_millis() as u32).to_value(),
|
||||
"raise-log-level" => settings.raise_log_level.to_value(),
|
||||
"push-period" => (settings.push_period.as_millis() as u32).to_value(),
|
||||
"max-buffers" => settings
|
||||
.max_buffers
|
||||
.and_then(|val| val.try_into().ok())
|
||||
.unwrap_or(-1i32)
|
||||
.to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
let obj = self.obj();
|
||||
obj.add_pad(self.sink_pad.gst_pad()).unwrap();
|
||||
obj.set_element_flags(gst::ElementFlags::SINK);
|
||||
}
|
||||
}
|
||||
|
||||
impl GstObjectImpl for TestSink {}
|
||||
|
||||
impl ElementImpl for TestSink {
|
||||
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||
gst::subclass::ElementMetadata::new(
|
||||
"Thread-sharing standalone test sink",
|
||||
"Sink/Test",
|
||||
"Thread-sharing standalone test sink",
|
||||
"François Laignel <fengalin@free.fr>",
|
||||
)
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
match transition {
|
||||
gst::StateChange::NullToReady => {
|
||||
self.prepare().map_err(|err| {
|
||||
self.post_error_message(err);
|
||||
gst::StateChangeError
|
||||
})?;
|
||||
}
|
||||
gst::StateChange::ReadyToPaused => {
|
||||
self.start().map_err(|_| gst::StateChangeError)?;
|
||||
}
|
||||
gst::StateChange::PausedToReady => {
|
||||
self.stop().map_err(|_| gst::StateChangeError)?;
|
||||
}
|
||||
gst::StateChange::ReadyToNull => {
|
||||
self.unprepare();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.parent_change_state(transition)
|
||||
}
|
||||
}
|
|
@ -1,17 +1,22 @@
|
|||
use gst::glib;
|
||||
use gst::prelude::*;
|
||||
pub mod async_mutex;
|
||||
pub mod sync_mutex;
|
||||
pub mod task;
|
||||
|
||||
mod imp;
|
||||
mod settings;
|
||||
pub use settings::Settings;
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct TestSink(ObjectSubclass<imp::TestSink>) @extends gst::Element, gst::Object;
|
||||
}
|
||||
mod stats;
|
||||
pub use stats::Stats;
|
||||
|
||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
gst::Element::register(
|
||||
Some(plugin),
|
||||
"ts-standalone-test-sink",
|
||||
gst::Rank::None,
|
||||
TestSink::static_type(),
|
||||
pub const ASYNC_MUTEX_ELEMENT_NAME: &str = "ts-standalone-async-mutex-sink";
|
||||
pub const SYNC_MUTEX_ELEMENT_NAME: &str = "ts-standalone-sync-mutex-sink";
|
||||
pub const TASK_ELEMENT_NAME: &str = "ts-standalone-task-sink";
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||
gst::DebugCategory::new(
|
||||
"ts-standalone-sink",
|
||||
gst::DebugColorFlags::empty(),
|
||||
Some("Thread-sharing standalone test sink"),
|
||||
)
|
||||
}
|
||||
});
|
||||
|
|
115
generic/threadshare/examples/standalone/sink/settings.rs
Normal file
115
generic/threadshare/examples/standalone/sink/settings.rs
Normal file
|
@ -0,0 +1,115 @@
|
|||
use gst::glib;
|
||||
use gst::prelude::*;
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
const DEFAULT_CONTEXT: &str = "";
|
||||
const DEFAULT_CONTEXT_WAIT: Duration = Duration::from_millis(20);
|
||||
const DEFAULT_PUSH_PERIOD: Duration = Duration::from_millis(20);
|
||||
const DEFAULT_MAX_BUFFERS: i32 = 50 * (100 - 25);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Settings {
|
||||
pub context: String,
|
||||
pub context_wait: Duration,
|
||||
pub is_main_elem: bool,
|
||||
pub logs_stats: bool,
|
||||
pub push_period: Duration,
|
||||
pub max_buffers: Option<u32>,
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Settings {
|
||||
context: DEFAULT_CONTEXT.into(),
|
||||
context_wait: DEFAULT_CONTEXT_WAIT,
|
||||
is_main_elem: false,
|
||||
logs_stats: false,
|
||||
push_period: DEFAULT_PUSH_PERIOD,
|
||||
max_buffers: Some(DEFAULT_MAX_BUFFERS as u32),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
pub fn properties() -> Vec<glib::ParamSpec> {
|
||||
vec![
|
||||
glib::ParamSpecString::builder("context")
|
||||
.nick("Context")
|
||||
.blurb("Context name to share threads with")
|
||||
.default_value(Some(DEFAULT_CONTEXT))
|
||||
.build(),
|
||||
glib::ParamSpecUInt::builder("context-wait")
|
||||
.nick("Context Wait")
|
||||
.blurb("Throttle poll loop to run at most once every this many ms")
|
||||
.maximum(1000)
|
||||
.default_value(DEFAULT_CONTEXT_WAIT.as_millis() as u32)
|
||||
.build(),
|
||||
glib::ParamSpecBoolean::builder("main-elem")
|
||||
.nick("Main Element")
|
||||
.blurb("Declare this element as the main one")
|
||||
.write_only()
|
||||
.build(),
|
||||
glib::ParamSpecBoolean::builder("logs-stats")
|
||||
.nick("Logs Stats")
|
||||
.blurb("Whether statistics should be logged")
|
||||
.write_only()
|
||||
.build(),
|
||||
glib::ParamSpecUInt::builder("push-period")
|
||||
.nick("Src buffer Push Period")
|
||||
.blurb("Push period used by `src` element (used for stats warnings)")
|
||||
.default_value(DEFAULT_PUSH_PERIOD.as_millis() as u32)
|
||||
.build(),
|
||||
glib::ParamSpecInt::builder("max-buffers")
|
||||
.nick("Max Buffers")
|
||||
.blurb("Number of buffers to count before stopping stats (-1 = unlimited)")
|
||||
.minimum(-1i32)
|
||||
.default_value(DEFAULT_MAX_BUFFERS)
|
||||
.build(),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn set_property(&mut self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"context" => {
|
||||
self.context = value
|
||||
.get::<Option<String>>()
|
||||
.unwrap()
|
||||
.unwrap_or_else(|| DEFAULT_CONTEXT.into());
|
||||
}
|
||||
"context-wait" => {
|
||||
self.context_wait = Duration::from_millis(value.get::<u32>().unwrap().into());
|
||||
}
|
||||
"main-elem" => {
|
||||
self.is_main_elem = value.get::<bool>().unwrap();
|
||||
}
|
||||
"logs-stats" => {
|
||||
let logs_stats = value.get().unwrap();
|
||||
self.logs_stats = logs_stats;
|
||||
}
|
||||
"push-period" => {
|
||||
self.push_period = Duration::from_millis(value.get::<u32>().unwrap().into());
|
||||
}
|
||||
"max-buffers" => {
|
||||
let value = value.get::<i32>().unwrap();
|
||||
self.max_buffers = if value > 0 { Some(value as u32) } else { None };
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
match pspec.name() {
|
||||
"context" => self.context.to_value(),
|
||||
"context-wait" => (self.context_wait.as_millis() as u32).to_value(),
|
||||
"main-elem" => self.is_main_elem.to_value(),
|
||||
"push-period" => (self.push_period.as_millis() as u32).to_value(),
|
||||
"max-buffers" => self
|
||||
.max_buffers
|
||||
.and_then(|val| val.try_into().ok())
|
||||
.unwrap_or(-1i32)
|
||||
.to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
270
generic/threadshare/examples/standalone/sink/stats.rs
Normal file
270
generic/threadshare/examples/standalone/sink/stats.rs
Normal file
|
@ -0,0 +1,270 @@
|
|||
use gst::prelude::*;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[cfg(feature = "tuning")]
|
||||
use gstthreadshare::runtime::Context;
|
||||
|
||||
use super::CAT;
|
||||
|
||||
const LOG_PERIOD: Duration = Duration::from_secs(20);
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Stats {
|
||||
ramp_up_instant: Option<Instant>,
|
||||
log_start_instant: Option<Instant>,
|
||||
last_delta_instant: Option<Instant>,
|
||||
max_buffers: Option<f32>,
|
||||
buffer_count: f32,
|
||||
buffer_count_delta: f32,
|
||||
latency_sum: f32,
|
||||
latency_square_sum: f32,
|
||||
latency_sum_delta: f32,
|
||||
latency_square_sum_delta: f32,
|
||||
latency_min: Duration,
|
||||
latency_min_delta: Duration,
|
||||
latency_max: Duration,
|
||||
latency_max_delta: Duration,
|
||||
interval_sum: f32,
|
||||
interval_square_sum: f32,
|
||||
interval_sum_delta: f32,
|
||||
interval_square_sum_delta: f32,
|
||||
interval_min: Duration,
|
||||
interval_min_delta: Duration,
|
||||
interval_max: Duration,
|
||||
interval_max_delta: Duration,
|
||||
interval_late_warn: Duration,
|
||||
interval_late_count: f32,
|
||||
interval_late_count_delta: f32,
|
||||
#[cfg(feature = "tuning")]
|
||||
parked_duration_init: Duration,
|
||||
}
|
||||
|
||||
impl Stats {
|
||||
pub fn new(max_buffers: Option<u32>, interval_late_warn: Duration) -> Self {
|
||||
Stats {
|
||||
max_buffers: max_buffers.map(|max_buffers| max_buffers as f32),
|
||||
interval_late_warn,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(&mut self) {
|
||||
self.buffer_count = 0.0;
|
||||
self.buffer_count_delta = 0.0;
|
||||
self.latency_sum = 0.0;
|
||||
self.latency_square_sum = 0.0;
|
||||
self.latency_sum_delta = 0.0;
|
||||
self.latency_square_sum_delta = 0.0;
|
||||
self.latency_min = Duration::MAX;
|
||||
self.latency_min_delta = Duration::MAX;
|
||||
self.latency_max = Duration::ZERO;
|
||||
self.latency_max_delta = Duration::ZERO;
|
||||
self.interval_sum = 0.0;
|
||||
self.interval_square_sum = 0.0;
|
||||
self.interval_sum_delta = 0.0;
|
||||
self.interval_square_sum_delta = 0.0;
|
||||
self.interval_min = Duration::MAX;
|
||||
self.interval_min_delta = Duration::MAX;
|
||||
self.interval_max = Duration::ZERO;
|
||||
self.interval_max_delta = Duration::ZERO;
|
||||
self.interval_late_count = 0.0;
|
||||
self.interval_late_count_delta = 0.0;
|
||||
self.last_delta_instant = None;
|
||||
self.log_start_instant = None;
|
||||
|
||||
self.ramp_up_instant = Some(Instant::now());
|
||||
gst::info!(CAT, "First stats logs in {:2?}", 2 * LOG_PERIOD);
|
||||
}
|
||||
|
||||
pub fn is_active(&mut self) -> bool {
|
||||
if let Some(ramp_up_instant) = self.ramp_up_instant {
|
||||
if ramp_up_instant.elapsed() < LOG_PERIOD {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.ramp_up_instant = None;
|
||||
gst::info!(CAT, "Ramp up complete. Stats logs in {:2?}", LOG_PERIOD);
|
||||
self.log_start_instant = Some(Instant::now());
|
||||
self.last_delta_instant = self.log_start_instant;
|
||||
|
||||
#[cfg(feature = "tuning")]
|
||||
{
|
||||
self.parked_duration_init = Context::current().unwrap().parked_duration();
|
||||
}
|
||||
}
|
||||
|
||||
use std::cmp::Ordering::*;
|
||||
match self.max_buffers.opt_cmp(self.buffer_count) {
|
||||
Some(Equal) => {
|
||||
self.log_global();
|
||||
self.buffer_count += 1.0;
|
||||
false
|
||||
}
|
||||
Some(Less) => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_buffer(&mut self, latency: Duration, interval: Duration) {
|
||||
if !self.is_active() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.buffer_count += 1.0;
|
||||
self.buffer_count_delta += 1.0;
|
||||
|
||||
// Latency
|
||||
let latency_f32 = latency.as_nanos() as f32;
|
||||
let latency_square = latency_f32.powi(2);
|
||||
|
||||
self.latency_sum += latency_f32;
|
||||
self.latency_square_sum += latency_square;
|
||||
self.latency_min = self.latency_min.min(latency);
|
||||
self.latency_max = self.latency_max.max(latency);
|
||||
|
||||
self.latency_sum_delta += latency_f32;
|
||||
self.latency_square_sum_delta += latency_square;
|
||||
self.latency_min_delta = self.latency_min_delta.min(latency);
|
||||
self.latency_max_delta = self.latency_max_delta.max(latency);
|
||||
|
||||
// Interval
|
||||
let interval_f32 = interval.as_nanos() as f32;
|
||||
let interval_square = interval_f32.powi(2);
|
||||
|
||||
self.interval_sum += interval_f32;
|
||||
self.interval_square_sum += interval_square;
|
||||
self.interval_min = self.interval_min.min(interval);
|
||||
self.interval_max = self.interval_max.max(interval);
|
||||
|
||||
self.interval_sum_delta += interval_f32;
|
||||
self.interval_square_sum_delta += interval_square;
|
||||
self.interval_min_delta = self.interval_min_delta.min(interval);
|
||||
self.interval_max_delta = self.interval_max_delta.max(interval);
|
||||
|
||||
if interval > self.interval_late_warn {
|
||||
self.interval_late_count += 1.0;
|
||||
self.interval_late_count_delta += 1.0;
|
||||
}
|
||||
|
||||
let delta_duration = match self.last_delta_instant {
|
||||
Some(last_delta) => last_delta.elapsed(),
|
||||
None => return,
|
||||
};
|
||||
|
||||
if delta_duration < LOG_PERIOD {
|
||||
return;
|
||||
}
|
||||
|
||||
self.last_delta_instant = Some(Instant::now());
|
||||
|
||||
gst::info!(CAT, "Delta stats:");
|
||||
let interval_mean = self.interval_sum_delta / self.buffer_count_delta;
|
||||
let interval_std_dev = f32::sqrt(
|
||||
self.interval_square_sum_delta / self.buffer_count_delta - interval_mean.powi(2),
|
||||
);
|
||||
|
||||
gst::info!(
|
||||
CAT,
|
||||
"o interval: mean {:4.2?} σ {:4.1?} [{:4.1?}, {:4.1?}]",
|
||||
Duration::from_nanos(interval_mean as u64),
|
||||
Duration::from_nanos(interval_std_dev as u64),
|
||||
self.interval_min_delta,
|
||||
self.interval_max_delta,
|
||||
);
|
||||
|
||||
if self.interval_late_count_delta > f32::EPSILON {
|
||||
gst::warning!(
|
||||
CAT,
|
||||
"o {:5.2}% late buffers",
|
||||
100f32 * self.interval_late_count_delta / self.buffer_count_delta
|
||||
);
|
||||
}
|
||||
|
||||
self.interval_sum_delta = 0.0;
|
||||
self.interval_square_sum_delta = 0.0;
|
||||
self.interval_min_delta = Duration::MAX;
|
||||
self.interval_max_delta = Duration::ZERO;
|
||||
self.interval_late_count_delta = 0.0;
|
||||
|
||||
let latency_mean = self.latency_sum_delta / self.buffer_count_delta;
|
||||
let latency_std_dev = f32::sqrt(
|
||||
self.latency_square_sum_delta / self.buffer_count_delta - latency_mean.powi(2),
|
||||
);
|
||||
|
||||
gst::info!(
|
||||
CAT,
|
||||
"o latency: mean {:4.2?} σ {:4.1?} [{:4.1?}, {:4.1?}]",
|
||||
Duration::from_nanos(latency_mean as u64),
|
||||
Duration::from_nanos(latency_std_dev as u64),
|
||||
self.latency_min_delta,
|
||||
self.latency_max_delta,
|
||||
);
|
||||
|
||||
self.latency_sum_delta = 0.0;
|
||||
self.latency_square_sum_delta = 0.0;
|
||||
self.latency_min_delta = Duration::MAX;
|
||||
self.latency_max_delta = Duration::ZERO;
|
||||
|
||||
self.buffer_count_delta = 0.0;
|
||||
}
|
||||
|
||||
pub fn log_global(&mut self) {
|
||||
if self.buffer_count < 1.0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let _log_start = if let Some(log_start) = self.log_start_instant {
|
||||
log_start
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
gst::info!(CAT, "Global stats:");
|
||||
|
||||
#[cfg(feature = "tuning")]
|
||||
{
|
||||
let duration = _log_start.elapsed();
|
||||
let parked_duration =
|
||||
Context::current().unwrap().parked_duration() - self.parked_duration_init;
|
||||
gst::info!(
|
||||
CAT,
|
||||
"o parked: {parked_duration:4.2?} ({:5.2?}%)",
|
||||
(parked_duration.as_nanos() as f32 * 100.0 / duration.as_nanos() as f32)
|
||||
);
|
||||
}
|
||||
|
||||
let interval_mean = self.interval_sum / self.buffer_count;
|
||||
let interval_std_dev =
|
||||
f32::sqrt(self.interval_square_sum / self.buffer_count - interval_mean.powi(2));
|
||||
|
||||
gst::info!(
|
||||
CAT,
|
||||
"o interval: mean {:4.2?} σ {:4.1?} [{:4.1?}, {:4.1?}]",
|
||||
Duration::from_nanos(interval_mean as u64),
|
||||
Duration::from_nanos(interval_std_dev as u64),
|
||||
self.interval_min,
|
||||
self.interval_max,
|
||||
);
|
||||
|
||||
if self.interval_late_count > f32::EPSILON {
|
||||
gst::warning!(
|
||||
CAT,
|
||||
"o {:5.2}% late buffers",
|
||||
100f32 * self.interval_late_count / self.buffer_count
|
||||
);
|
||||
}
|
||||
|
||||
let latency_mean = self.latency_sum / self.buffer_count;
|
||||
let latency_std_dev =
|
||||
f32::sqrt(self.latency_square_sum / self.buffer_count - latency_mean.powi(2));
|
||||
|
||||
gst::info!(
|
||||
CAT,
|
||||
"o latency: mean {:4.2?} σ {:4.1?} [{:4.1?}, {:4.1?}]",
|
||||
Duration::from_nanos(latency_mean as u64),
|
||||
Duration::from_nanos(latency_std_dev as u64),
|
||||
self.latency_min,
|
||||
self.latency_max,
|
||||
);
|
||||
}
|
||||
}
|
327
generic/threadshare/examples/standalone/sink/sync_mutex/imp.rs
Normal file
327
generic/threadshare/examples/standalone/sink/sync_mutex/imp.rs
Normal file
|
@ -0,0 +1,327 @@
|
|||
// Copyright (C) 2022 François Laignel <fengalin@free.fr>
|
||||
//
|
||||
// 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 futures::future::BoxFuture;
|
||||
use futures::prelude::*;
|
||||
|
||||
use gst::glib;
|
||||
use gst::prelude::*;
|
||||
use gst::subclass::prelude::*;
|
||||
use gst::EventView;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use gstthreadshare::runtime::{prelude::*, PadSink};
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
|
||||
use super::super::{Settings, Stats, CAT};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct PadSinkHandlerInner {
|
||||
is_flushing: bool,
|
||||
is_main_elem: bool,
|
||||
last_dts: Option<gst::ClockTime>,
|
||||
segment_start: Option<gst::ClockTime>,
|
||||
stats: Option<Box<Stats>>,
|
||||
}
|
||||
|
||||
impl PadSinkHandlerInner {
|
||||
fn handle_buffer(
|
||||
&mut self,
|
||||
elem: &super::DirectSink,
|
||||
buffer: gst::Buffer,
|
||||
) -> Result<(), gst::FlowError> {
|
||||
if self.is_flushing {
|
||||
log_or_trace!(
|
||||
CAT,
|
||||
self.is_main_elem,
|
||||
obj: elem,
|
||||
"Discarding {buffer:?} (flushing)"
|
||||
);
|
||||
|
||||
return Err(gst::FlowError::Flushing);
|
||||
}
|
||||
|
||||
debug_or_trace!(CAT, self.is_main_elem, obj: elem, "Received {buffer:?}");
|
||||
|
||||
let dts = buffer
|
||||
.dts()
|
||||
.expect("Buffer without dts")
|
||||
.checked_sub(self.segment_start.expect("Buffer without Time Segment"))
|
||||
.expect("dts before Segment start");
|
||||
|
||||
if let Some(last_dts) = self.last_dts {
|
||||
let cur_ts = elem.current_running_time().unwrap();
|
||||
let latency: Duration = (cur_ts - dts).into();
|
||||
let interval: Duration = (dts - last_dts).into();
|
||||
|
||||
if let Some(stats) = self.stats.as_mut() {
|
||||
stats.add_buffer(latency, interval);
|
||||
}
|
||||
|
||||
debug_or_trace!(CAT, self.is_main_elem, obj: elem, "o latency {latency:.2?}");
|
||||
debug_or_trace!(
|
||||
CAT,
|
||||
self.is_main_elem,
|
||||
obj: elem,
|
||||
"o interval {interval:.2?}",
|
||||
);
|
||||
}
|
||||
|
||||
self.last_dts = Some(dts);
|
||||
|
||||
log_or_trace!(CAT, self.is_main_elem, obj: elem, "Buffer processed");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct SyncPadSinkHandler(Arc<Mutex<PadSinkHandlerInner>>);
|
||||
|
||||
impl PadSinkHandler for SyncPadSinkHandler {
|
||||
type ElementImpl = DirectSink;
|
||||
|
||||
fn sink_chain(
|
||||
self,
|
||||
_pad: gst::Pad,
|
||||
elem: super::DirectSink,
|
||||
buffer: gst::Buffer,
|
||||
) -> BoxFuture<'static, Result<gst::FlowSuccess, gst::FlowError>> {
|
||||
async move {
|
||||
if self.0.lock().unwrap().handle_buffer(&elem, buffer).is_err() {
|
||||
return Err(gst::FlowError::Flushing);
|
||||
}
|
||||
|
||||
Ok(gst::FlowSuccess::Ok)
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn sink_event_serialized(
|
||||
self,
|
||||
_pad: gst::Pad,
|
||||
elem: super::DirectSink,
|
||||
event: gst::Event,
|
||||
) -> BoxFuture<'static, bool> {
|
||||
async move {
|
||||
match event.view() {
|
||||
EventView::Eos(_) => {
|
||||
{
|
||||
let mut inner = self.0.lock().unwrap();
|
||||
debug_or_trace!(CAT, inner.is_main_elem, obj: elem, "EOS");
|
||||
inner.is_flushing = true;
|
||||
}
|
||||
|
||||
// When each element sends its own EOS message,
|
||||
// it takes ages for the pipeline to process all of them.
|
||||
// Let's just post an error message and let main shuts down
|
||||
// after all streams have posted this message.
|
||||
let _ = elem
|
||||
.post_message(gst::message::Error::new(gst::LibraryError::Shutdown, "EOS"));
|
||||
}
|
||||
EventView::FlushStop(_) => {
|
||||
self.0.lock().unwrap().is_flushing = false;
|
||||
}
|
||||
EventView::Segment(evt) => {
|
||||
if let Some(time_seg) = evt.segment().downcast_ref::<gst::ClockTime>() {
|
||||
self.0.lock().unwrap().segment_start = time_seg.start();
|
||||
}
|
||||
}
|
||||
EventView::SinkMessage(evt) => {
|
||||
let _ = elem.post_message(evt.message());
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn sink_event(self, _pad: &gst::Pad, _imp: &DirectSink, event: gst::Event) -> bool {
|
||||
if let EventView::FlushStart(..) = event.view() {
|
||||
self.0.lock().unwrap().is_flushing = true;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl SyncPadSinkHandler {
|
||||
fn prepare(&self, is_main_elem: bool, stats: Option<Stats>) {
|
||||
let mut inner = self.0.lock().unwrap();
|
||||
inner.is_main_elem = is_main_elem;
|
||||
inner.stats = stats.map(Box::new);
|
||||
}
|
||||
|
||||
fn start(&self) {
|
||||
let mut inner = self.0.lock().unwrap();
|
||||
|
||||
inner.is_flushing = false;
|
||||
inner.last_dts = None;
|
||||
|
||||
if let Some(stats) = inner.stats.as_mut() {
|
||||
stats.start();
|
||||
}
|
||||
}
|
||||
|
||||
fn stop(&self) {
|
||||
let mut inner = self.0.lock().unwrap();
|
||||
inner.is_flushing = true;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DirectSink {
|
||||
sink_pad: PadSink,
|
||||
sink_pad_handler: SyncPadSinkHandler,
|
||||
settings: Mutex<Settings>,
|
||||
}
|
||||
|
||||
impl DirectSink {
|
||||
fn prepare(&self) -> Result<(), gst::ErrorMessage> {
|
||||
let settings = self.settings.lock().unwrap();
|
||||
debug_or_trace!(CAT, settings.is_main_elem, imp: self, "Preparing");
|
||||
let stats = if settings.logs_stats {
|
||||
Some(Stats::new(
|
||||
settings.max_buffers,
|
||||
settings.push_period + settings.context_wait / 2,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
self.sink_pad_handler.prepare(settings.is_main_elem, stats);
|
||||
debug_or_trace!(CAT, settings.is_main_elem, imp: self, "Prepared");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn stop(&self) -> Result<(), gst::ErrorMessage> {
|
||||
let is_main_elem = self.settings.lock().unwrap().is_main_elem;
|
||||
debug_or_trace!(CAT, is_main_elem, imp: self, "Stopping");
|
||||
self.sink_pad_handler.stop();
|
||||
debug_or_trace!(CAT, is_main_elem, imp: self, "Stopped");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn start(&self) -> Result<(), gst::ErrorMessage> {
|
||||
let is_main_elem = self.settings.lock().unwrap().is_main_elem;
|
||||
debug_or_trace!(CAT, is_main_elem, imp: self, "Starting");
|
||||
self.sink_pad_handler.start();
|
||||
debug_or_trace!(CAT, is_main_elem, imp: self, "Started");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for DirectSink {
|
||||
const NAME: &'static str = "TsStandaloneDirectSink";
|
||||
type Type = super::DirectSink;
|
||||
type ParentType = gst::Element;
|
||||
|
||||
fn with_class(klass: &Self::Class) -> Self {
|
||||
let sink_pad_handler = SyncPadSinkHandler::default();
|
||||
Self {
|
||||
sink_pad: PadSink::new(
|
||||
gst::Pad::from_template(&klass.pad_template("sink").unwrap(), Some("sink")),
|
||||
sink_pad_handler.clone(),
|
||||
),
|
||||
sink_pad_handler,
|
||||
settings: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for DirectSink {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(Settings::properties);
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
self.settings.lock().unwrap().set_property(id, value, pspec);
|
||||
}
|
||||
|
||||
fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
self.settings.lock().unwrap().property(id, pspec)
|
||||
}
|
||||
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
let obj = self.obj();
|
||||
obj.add_pad(self.sink_pad.gst_pad()).unwrap();
|
||||
obj.set_element_flags(gst::ElementFlags::SINK);
|
||||
}
|
||||
}
|
||||
|
||||
impl GstObjectImpl for DirectSink {}
|
||||
|
||||
impl ElementImpl for DirectSink {
|
||||
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||
gst::subclass::ElementMetadata::new(
|
||||
"Thread-sharing standalone test direct sink",
|
||||
"Sink/Test",
|
||||
"Thread-sharing standalone test direct sink",
|
||||
"François Laignel <fengalin@free.fr>",
|
||||
)
|
||||
});
|
||||
|
||||
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:?}");
|
||||
|
||||
match transition {
|
||||
gst::StateChange::NullToReady => {
|
||||
self.prepare().map_err(|err| {
|
||||
self.post_error_message(err);
|
||||
gst::StateChangeError
|
||||
})?;
|
||||
}
|
||||
gst::StateChange::ReadyToPaused => {
|
||||
self.start().map_err(|_| gst::StateChangeError)?;
|
||||
}
|
||||
gst::StateChange::PausedToReady => {
|
||||
self.stop().map_err(|_| gst::StateChangeError)?;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.parent_change_state(transition)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
use gst::glib;
|
||||
use gst::prelude::*;
|
||||
|
||||
mod imp;
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct DirectSink(ObjectSubclass<imp::DirectSink>) @extends gst::Element, gst::Object;
|
||||
}
|
||||
|
||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
gst::Element::register(
|
||||
Some(plugin),
|
||||
super::SYNC_MUTEX_ELEMENT_NAME,
|
||||
gst::Rank::None,
|
||||
DirectSink::static_type(),
|
||||
)
|
||||
}
|
402
generic/threadshare/examples/standalone/sink/task/imp.rs
Normal file
402
generic/threadshare/examples/standalone/sink/task/imp.rs
Normal file
|
@ -0,0 +1,402 @@
|
|||
// Copyright (C) 2022 François Laignel <fengalin@free.fr>
|
||||
//
|
||||
// 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 futures::future::BoxFuture;
|
||||
use futures::prelude::*;
|
||||
|
||||
use gst::error_msg;
|
||||
use gst::glib;
|
||||
use gst::prelude::*;
|
||||
use gst::subclass::prelude::*;
|
||||
use gst::EventView;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use gstthreadshare::runtime::prelude::*;
|
||||
use gstthreadshare::runtime::{Context, PadSink, Task};
|
||||
|
||||
use std::sync::Mutex;
|
||||
use std::time::Duration;
|
||||
|
||||
use super::super::{Settings, Stats, CAT};
|
||||
|
||||
#[derive(Debug)]
|
||||
enum StreamItem {
|
||||
Buffer(gst::Buffer),
|
||||
Event(gst::Event),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct TaskPadSinkHandler;
|
||||
|
||||
impl PadSinkHandler for TaskPadSinkHandler {
|
||||
type ElementImpl = TaskSink;
|
||||
|
||||
fn sink_chain(
|
||||
self,
|
||||
_pad: gst::Pad,
|
||||
elem: super::TaskSink,
|
||||
buffer: gst::Buffer,
|
||||
) -> BoxFuture<'static, Result<gst::FlowSuccess, gst::FlowError>> {
|
||||
let sender = elem.imp().clone_item_sender();
|
||||
async move {
|
||||
if sender.send_async(StreamItem::Buffer(buffer)).await.is_err() {
|
||||
return Err(gst::FlowError::Flushing);
|
||||
}
|
||||
|
||||
Ok(gst::FlowSuccess::Ok)
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn sink_event_serialized(
|
||||
self,
|
||||
_pad: gst::Pad,
|
||||
elem: super::TaskSink,
|
||||
event: gst::Event,
|
||||
) -> BoxFuture<'static, bool> {
|
||||
let sender = elem.imp().clone_item_sender();
|
||||
async move {
|
||||
match event.view() {
|
||||
EventView::Segment(_) => {
|
||||
let _ = sender.send_async(StreamItem::Event(event)).await;
|
||||
}
|
||||
EventView::Eos(_) => {
|
||||
let is_main_elem = elem.imp().settings.lock().unwrap().is_main_elem;
|
||||
debug_or_trace!(CAT, is_main_elem, obj: elem, "EOS");
|
||||
|
||||
// When each element sends its own EOS message,
|
||||
// it takes ages for the pipeline to process all of them.
|
||||
// Let's just post an error message and let main shuts down
|
||||
// after all streams have posted this message.
|
||||
let _ = elem
|
||||
.post_message(gst::message::Error::new(gst::LibraryError::Shutdown, "EOS"));
|
||||
}
|
||||
EventView::FlushStop(_) => {
|
||||
let imp = elem.imp();
|
||||
return imp.task.flush_stop().await_maybe_on_context().is_ok();
|
||||
}
|
||||
EventView::SinkMessage(evt) => {
|
||||
let _ = elem.post_message(evt.message());
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn sink_event(self, _pad: &gst::Pad, imp: &TaskSink, event: gst::Event) -> bool {
|
||||
if let EventView::FlushStart(..) = event.view() {
|
||||
return imp.task.flush_start().await_maybe_on_context().is_ok();
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
struct TaskSinkTask {
|
||||
elem: super::TaskSink,
|
||||
item_receiver: flume::Receiver<StreamItem>,
|
||||
is_main_elem: bool,
|
||||
last_dts: Option<gst::ClockTime>,
|
||||
segment_start: Option<gst::ClockTime>,
|
||||
stats: Option<Box<Stats>>,
|
||||
}
|
||||
|
||||
impl TaskSinkTask {
|
||||
fn new(
|
||||
elem: &super::TaskSink,
|
||||
item_receiver: flume::Receiver<StreamItem>,
|
||||
is_main_elem: bool,
|
||||
stats: Option<Box<Stats>>,
|
||||
) -> Self {
|
||||
TaskSinkTask {
|
||||
elem: elem.clone(),
|
||||
item_receiver,
|
||||
is_main_elem,
|
||||
last_dts: None,
|
||||
stats,
|
||||
segment_start: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) {
|
||||
// Purge the channel
|
||||
while !self.item_receiver.is_empty() {}
|
||||
}
|
||||
}
|
||||
|
||||
impl TaskImpl for TaskSinkTask {
|
||||
type Item = StreamItem;
|
||||
|
||||
fn prepare(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
||||
log_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Preparing Task");
|
||||
future::ok(()).boxed()
|
||||
}
|
||||
|
||||
fn start(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
||||
async {
|
||||
log_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Starting Task");
|
||||
self.last_dts = None;
|
||||
if let Some(stats) = self.stats.as_mut() {
|
||||
stats.start();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn stop(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
||||
async {
|
||||
log_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Stopping Task");
|
||||
self.flush();
|
||||
Ok(())
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn try_next(&mut self) -> BoxFuture<'_, Result<StreamItem, gst::FlowError>> {
|
||||
self.item_receiver
|
||||
.recv_async()
|
||||
.map(|opt_item| Ok(opt_item.unwrap()))
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn handle_item(&mut self, item: StreamItem) -> BoxFuture<'_, Result<(), gst::FlowError>> {
|
||||
async move {
|
||||
debug_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Received {item:?}");
|
||||
|
||||
match item {
|
||||
StreamItem::Buffer(buffer) => {
|
||||
let dts = buffer
|
||||
.dts()
|
||||
.expect("Buffer without dts")
|
||||
.checked_sub(self.segment_start.expect("Buffer without Time Segment"))
|
||||
.expect("dts before Segment start");
|
||||
|
||||
if let Some(last_dts) = self.last_dts {
|
||||
let cur_ts = self.elem.current_running_time().unwrap();
|
||||
let latency: Duration = (cur_ts - dts).into();
|
||||
let interval: Duration = (dts - last_dts).into();
|
||||
|
||||
if let Some(stats) = self.stats.as_mut() {
|
||||
stats.add_buffer(latency, interval);
|
||||
}
|
||||
|
||||
debug_or_trace!(
|
||||
CAT,
|
||||
self.is_main_elem,
|
||||
obj: self.elem,
|
||||
"o latency {latency:.2?}",
|
||||
);
|
||||
debug_or_trace!(
|
||||
CAT,
|
||||
self.is_main_elem,
|
||||
obj: self.elem,
|
||||
"o interval {interval:.2?}",
|
||||
);
|
||||
}
|
||||
|
||||
self.last_dts = Some(dts);
|
||||
|
||||
log_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Buffer processed");
|
||||
}
|
||||
StreamItem::Event(evt) => {
|
||||
if let EventView::Segment(evt) = evt.view() {
|
||||
if let Some(time_seg) = evt.segment().downcast_ref::<gst::ClockTime>() {
|
||||
self.segment_start = time_seg.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TaskSink {
|
||||
sink_pad: PadSink,
|
||||
task: Task,
|
||||
item_sender: Mutex<Option<flume::Sender<StreamItem>>>,
|
||||
settings: Mutex<Settings>,
|
||||
}
|
||||
|
||||
impl TaskSink {
|
||||
#[track_caller]
|
||||
fn clone_item_sender(&self) -> flume::Sender<StreamItem> {
|
||||
self.item_sender.lock().unwrap().as_ref().unwrap().clone()
|
||||
}
|
||||
|
||||
fn prepare(&self) -> Result<(), gst::ErrorMessage> {
|
||||
let settings = self.settings.lock().unwrap();
|
||||
let stats = if settings.logs_stats {
|
||||
Some(Box::new(Stats::new(
|
||||
settings.max_buffers,
|
||||
settings.push_period + settings.context_wait / 2,
|
||||
)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
debug_or_trace!(CAT, settings.is_main_elem, imp: self, "Preparing");
|
||||
|
||||
let ts_ctx = Context::acquire(&settings.context, settings.context_wait).map_err(|err| {
|
||||
error_msg!(
|
||||
gst::ResourceError::OpenWrite,
|
||||
["Failed to acquire Context: {}", err]
|
||||
)
|
||||
})?;
|
||||
|
||||
// Enable backpressure for items
|
||||
let (item_sender, item_receiver) = flume::bounded(0);
|
||||
let task_impl = TaskSinkTask::new(&self.obj(), item_receiver, settings.is_main_elem, stats);
|
||||
self.task.prepare(task_impl, ts_ctx).block_on()?;
|
||||
|
||||
*self.item_sender.lock().unwrap() = Some(item_sender);
|
||||
|
||||
debug_or_trace!(CAT, settings.is_main_elem, imp: self, "Prepared");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unprepare(&self) {
|
||||
let is_main_elem = self.settings.lock().unwrap().is_main_elem;
|
||||
debug_or_trace!(CAT, is_main_elem, imp: self, "Unpreparing");
|
||||
self.task.unprepare().block_on().unwrap();
|
||||
debug_or_trace!(CAT, is_main_elem, imp: self, "Unprepared");
|
||||
}
|
||||
|
||||
fn stop(&self) -> Result<(), gst::ErrorMessage> {
|
||||
let is_main_elem = self.settings.lock().unwrap().is_main_elem;
|
||||
debug_or_trace!(CAT, is_main_elem, imp: self, "Stopping");
|
||||
self.task.stop().block_on()?;
|
||||
debug_or_trace!(CAT, is_main_elem, imp: self, "Stopped");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn start(&self) -> Result<(), gst::ErrorMessage> {
|
||||
let is_main_elem = self.settings.lock().unwrap().is_main_elem;
|
||||
debug_or_trace!(CAT, is_main_elem, imp: self, "Starting");
|
||||
self.task.start().block_on()?;
|
||||
debug_or_trace!(CAT, is_main_elem, imp: self, "Started");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for TaskSink {
|
||||
const NAME: &'static str = "TsStandaloneTaskSink";
|
||||
type Type = super::TaskSink;
|
||||
type ParentType = gst::Element;
|
||||
|
||||
fn with_class(klass: &Self::Class) -> Self {
|
||||
Self {
|
||||
sink_pad: PadSink::new(
|
||||
gst::Pad::from_template(&klass.pad_template("sink").unwrap(), Some("sink")),
|
||||
TaskPadSinkHandler,
|
||||
),
|
||||
task: Task::default(),
|
||||
item_sender: Default::default(),
|
||||
settings: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for TaskSink {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(Settings::properties);
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
self.settings.lock().unwrap().set_property(id, value, pspec);
|
||||
}
|
||||
|
||||
fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
self.settings.lock().unwrap().property(id, pspec)
|
||||
}
|
||||
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
let obj = self.obj();
|
||||
obj.add_pad(self.sink_pad.gst_pad()).unwrap();
|
||||
obj.set_element_flags(gst::ElementFlags::SINK);
|
||||
}
|
||||
}
|
||||
|
||||
impl GstObjectImpl for TaskSink {}
|
||||
|
||||
impl ElementImpl for TaskSink {
|
||||
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||
gst::subclass::ElementMetadata::new(
|
||||
"Thread-sharing standalone test task sink",
|
||||
"Sink/Test",
|
||||
"Thread-sharing standalone test task sink",
|
||||
"François Laignel <fengalin@free.fr>",
|
||||
)
|
||||
});
|
||||
|
||||
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:?}");
|
||||
|
||||
match transition {
|
||||
gst::StateChange::NullToReady => {
|
||||
self.prepare().map_err(|err| {
|
||||
self.post_error_message(err);
|
||||
gst::StateChangeError
|
||||
})?;
|
||||
}
|
||||
gst::StateChange::ReadyToPaused => {
|
||||
self.start().map_err(|_| gst::StateChangeError)?;
|
||||
}
|
||||
gst::StateChange::PausedToReady => {
|
||||
self.stop().map_err(|_| gst::StateChangeError)?;
|
||||
}
|
||||
gst::StateChange::ReadyToNull => {
|
||||
self.unprepare();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.parent_change_state(transition)
|
||||
}
|
||||
}
|
17
generic/threadshare/examples/standalone/sink/task/mod.rs
Normal file
17
generic/threadshare/examples/standalone/sink/task/mod.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
use gst::glib;
|
||||
use gst::prelude::*;
|
||||
|
||||
mod imp;
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct TaskSink(ObjectSubclass<imp::TaskSink>) @extends gst::Element, gst::Object;
|
||||
}
|
||||
|
||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
gst::Element::register(
|
||||
Some(plugin),
|
||||
super::TASK_ELEMENT_NAME,
|
||||
gst::Rank::None,
|
||||
TaskSink::static_type(),
|
||||
)
|
||||
}
|
|
@ -19,11 +19,11 @@ use std::sync::Mutex;
|
|||
use std::time::Duration;
|
||||
|
||||
use gstthreadshare::runtime::prelude::*;
|
||||
use gstthreadshare::runtime::{timer, Context, PadSrc, Task};
|
||||
use gstthreadshare::runtime::{task, timer, Context, PadSrc, Task};
|
||||
|
||||
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||
gst::DebugCategory::new(
|
||||
"ts-standalone-test-src",
|
||||
super::ELEMENT_NAME,
|
||||
gst::DebugColorFlags::empty(),
|
||||
Some("Thread-sharing standalone test src"),
|
||||
)
|
||||
|
@ -39,7 +39,7 @@ struct Settings {
|
|||
context: String,
|
||||
context_wait: Duration,
|
||||
push_period: gst::ClockTime,
|
||||
raise_log_level: bool,
|
||||
is_main_elem: bool,
|
||||
num_buffers: Option<u32>,
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ impl Default for Settings {
|
|||
context: DEFAULT_CONTEXT.into(),
|
||||
context_wait: DEFAULT_CONTEXT_WAIT,
|
||||
push_period: DEFAULT_PUSH_PERIOD,
|
||||
raise_log_level: false,
|
||||
is_main_elem: false,
|
||||
num_buffers: Some(DEFAULT_NUM_BUFFERS as u32),
|
||||
}
|
||||
}
|
||||
|
@ -63,19 +63,18 @@ impl PadSrcHandler for TestSrcPadHandler {
|
|||
|
||||
#[derive(Debug)]
|
||||
struct SrcTask {
|
||||
element: super::TestSrc,
|
||||
elem: super::TestSrc,
|
||||
buffer_pool: gst::BufferPool,
|
||||
timer: Option<timer::Interval>,
|
||||
raise_log_level: bool,
|
||||
is_main_elem: bool,
|
||||
push_period: gst::ClockTime,
|
||||
need_initial_events: bool,
|
||||
need_segment: bool,
|
||||
num_buffers: Option<u32>,
|
||||
buffer_count: u32,
|
||||
}
|
||||
|
||||
impl SrcTask {
|
||||
fn new(element: super::TestSrc) -> Self {
|
||||
fn new(elem: super::TestSrc) -> Self {
|
||||
let buffer_pool = gst::BufferPool::new();
|
||||
let mut pool_config = buffer_pool.config();
|
||||
pool_config
|
||||
|
@ -84,13 +83,12 @@ impl SrcTask {
|
|||
buffer_pool.set_config(pool_config).unwrap();
|
||||
|
||||
SrcTask {
|
||||
element,
|
||||
elem,
|
||||
buffer_pool,
|
||||
timer: None,
|
||||
raise_log_level: false,
|
||||
is_main_elem: false,
|
||||
push_period: gst::ClockTime::ZERO,
|
||||
need_initial_events: true,
|
||||
need_segment: true,
|
||||
num_buffers: Some(DEFAULT_NUM_BUFFERS as u32),
|
||||
buffer_count: 0,
|
||||
}
|
||||
|
@ -98,34 +96,48 @@ impl SrcTask {
|
|||
}
|
||||
|
||||
impl TaskImpl for SrcTask {
|
||||
type Item = gst::Buffer;
|
||||
type Item = ();
|
||||
|
||||
fn prepare(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
||||
async move {
|
||||
let src = self.element.imp();
|
||||
let settings = src.settings.lock().unwrap();
|
||||
self.raise_log_level = settings.raise_log_level;
|
||||
let imp = self.elem.imp();
|
||||
let settings = imp.settings.lock().unwrap();
|
||||
self.is_main_elem = settings.is_main_elem;
|
||||
|
||||
if self.raise_log_level {
|
||||
gst::log!(CAT, obj: self.element, "Preparing Task");
|
||||
} else {
|
||||
gst::trace!(CAT, obj: self.element, "Preparing Task");
|
||||
}
|
||||
log_or_trace!(CAT, self.is_main_elem, imp: imp, "Preparing Task");
|
||||
|
||||
self.push_period = settings.push_period;
|
||||
self.num_buffers = settings.num_buffers;
|
||||
self.push_period = settings.push_period;
|
||||
self.num_buffers = settings.num_buffers;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
.boxed()
|
||||
future::ok(()).boxed()
|
||||
}
|
||||
|
||||
fn start(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
||||
async {
|
||||
if self.raise_log_level {
|
||||
gst::log!(CAT, obj: self.element, "Starting Task");
|
||||
} else {
|
||||
gst::trace!(CAT, obj: self.element, "Starting Task");
|
||||
async move {
|
||||
log_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Starting Task");
|
||||
|
||||
if self.need_initial_events {
|
||||
let imp = self.elem.imp();
|
||||
|
||||
debug_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Pushing initial events");
|
||||
|
||||
let stream_id =
|
||||
format!("{:08x}{:08x}", rand::random::<u32>(), rand::random::<u32>());
|
||||
let stream_start_evt = gst::event::StreamStart::builder(&stream_id)
|
||||
.group_id(gst::GroupId::next())
|
||||
.build();
|
||||
imp.src_pad.push_event(stream_start_evt).await;
|
||||
|
||||
imp.src_pad
|
||||
.push_event(gst::event::Caps::new(
|
||||
&gst::Caps::builder("foo/bar").build(),
|
||||
))
|
||||
.await;
|
||||
|
||||
let segment_evt =
|
||||
gst::event::Segment::new(&gst::FormattedSegment::<gst::format::Time>::new());
|
||||
imp.src_pad.push_event(segment_evt).await;
|
||||
|
||||
self.need_initial_events = false;
|
||||
}
|
||||
|
||||
self.timer = Some(
|
||||
|
@ -138,178 +150,100 @@ impl TaskImpl for SrcTask {
|
|||
);
|
||||
self.buffer_count = 0;
|
||||
self.buffer_pool.set_active(true).unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn stop(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
||||
async move {
|
||||
if self.raise_log_level {
|
||||
gst::log!(CAT, obj: self.element, "Stopping Task");
|
||||
} else {
|
||||
gst::trace!(CAT, obj: self.element, "Stopping Task");
|
||||
}
|
||||
log_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Stopping Task");
|
||||
self.buffer_pool.set_active(false).unwrap();
|
||||
self.timer = None;
|
||||
self.need_initial_events = true;
|
||||
|
||||
self.buffer_pool.set_active(false).unwrap();
|
||||
self.timer = None;
|
||||
self.need_initial_events = true;
|
||||
self.need_segment = true;
|
||||
future::ok(()).boxed()
|
||||
}
|
||||
|
||||
fn try_next(&mut self) -> BoxFuture<'_, Result<(), gst::FlowError>> {
|
||||
async move {
|
||||
log_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Awaiting timer");
|
||||
self.timer.as_mut().unwrap().next().await;
|
||||
log_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Timer ticked");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn try_next(&mut self) -> BoxFuture<'_, Result<gst::Buffer, gst::FlowError>> {
|
||||
fn handle_item(&mut self, _: ()) -> BoxFuture<'_, Result<(), gst::FlowError>> {
|
||||
async move {
|
||||
if self.raise_log_level {
|
||||
gst::log!(CAT, obj: self.element, "Awaiting timer");
|
||||
} else {
|
||||
gst::trace!(CAT, obj: self.element, "Awaiting timer");
|
||||
}
|
||||
|
||||
self.timer.as_mut().unwrap().next().await;
|
||||
|
||||
if self.raise_log_level {
|
||||
gst::log!(CAT, obj: self.element, "Timer ticked");
|
||||
} else {
|
||||
gst::trace!(CAT, obj: self.element, "Timer ticked");
|
||||
}
|
||||
|
||||
self.buffer_pool
|
||||
let buffer = self
|
||||
.buffer_pool
|
||||
.acquire_buffer(None)
|
||||
.map(|mut buffer| {
|
||||
{
|
||||
let buffer = buffer.get_mut().unwrap();
|
||||
let rtime = self.element.current_running_time().unwrap();
|
||||
let rtime = self.elem.current_running_time().unwrap();
|
||||
buffer.set_dts(rtime);
|
||||
}
|
||||
buffer
|
||||
})
|
||||
.map_err(|err| {
|
||||
gst::error!(CAT, obj: self.element, "Failed to acquire buffer {}", err);
|
||||
gst::error!(CAT, obj: self.elem, "Failed to acquire buffer {err}");
|
||||
err
|
||||
})
|
||||
})?;
|
||||
|
||||
debug_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Forwarding buffer");
|
||||
self.elem.imp().src_pad.push(buffer).await?;
|
||||
log_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Successfully pushed buffer");
|
||||
|
||||
self.buffer_count += 1;
|
||||
|
||||
if self.num_buffers.opt_eq(self.buffer_count) == Some(true) {
|
||||
return Err(gst::FlowError::Eos);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn handle_item(&mut self, buffer: gst::Buffer) -> BoxFuture<'_, Result<(), gst::FlowError>> {
|
||||
fn handle_loop_error(&mut self, err: gst::FlowError) -> BoxFuture<'_, task::Trigger> {
|
||||
async move {
|
||||
let res = self.push(buffer).await;
|
||||
match res {
|
||||
Ok(_) => {
|
||||
if self.raise_log_level {
|
||||
gst::log!(CAT, obj: self.element, "Successfully pushed buffer");
|
||||
} else {
|
||||
gst::trace!(CAT, obj: self.element, "Successfully pushed buffer");
|
||||
}
|
||||
}
|
||||
Err(gst::FlowError::Eos) => {
|
||||
if self.raise_log_level {
|
||||
gst::debug!(CAT, obj: self.element, "EOS");
|
||||
} else {
|
||||
gst::trace!(CAT, obj: self.element, "EOS");
|
||||
}
|
||||
let test_src = self.element.imp();
|
||||
test_src.src_pad.push_event(gst::event::Eos::new()).await;
|
||||
match err {
|
||||
gst::FlowError::Eos => {
|
||||
debug_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Pushing EOS");
|
||||
|
||||
return Err(gst::FlowError::Eos);
|
||||
}
|
||||
Err(gst::FlowError::Flushing) => {
|
||||
if self.raise_log_level {
|
||||
gst::debug!(CAT, obj: self.element, "Flushing");
|
||||
} else {
|
||||
gst::trace!(CAT, obj: self.element, "Flushing");
|
||||
let imp = self.elem.imp();
|
||||
if !imp.src_pad.push_event(gst::event::Eos::new()).await {
|
||||
gst::error!(CAT, imp: imp, "Error pushing EOS");
|
||||
}
|
||||
|
||||
task::Trigger::Stop
|
||||
}
|
||||
Err(err) => {
|
||||
gst::error!(CAT, obj: self.element, "Got error {}", err);
|
||||
gst::FlowError::Flushing => {
|
||||
debug_or_trace!(CAT, self.is_main_elem, obj: self.elem, "Flushing");
|
||||
|
||||
task::Trigger::FlushStart
|
||||
}
|
||||
err => {
|
||||
gst::error!(CAT, obj: self.elem, "Got error {err}");
|
||||
gst::element_error!(
|
||||
&self.element,
|
||||
&self.elem,
|
||||
gst::StreamError::Failed,
|
||||
("Internal data stream error"),
|
||||
["streaming stopped, reason {}", err]
|
||||
);
|
||||
|
||||
task::Trigger::Error
|
||||
}
|
||||
}
|
||||
|
||||
res.map(drop)
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
impl SrcTask {
|
||||
async fn push(&mut self, buffer: gst::Buffer) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
if self.raise_log_level {
|
||||
gst::debug!(CAT, obj: self.element, "Pushing {:?}", buffer);
|
||||
} else {
|
||||
gst::trace!(CAT, obj: self.element, "Pushing {:?}", buffer);
|
||||
}
|
||||
|
||||
let test_src = self.element.imp();
|
||||
|
||||
if self.need_initial_events {
|
||||
if self.raise_log_level {
|
||||
gst::debug!(CAT, obj: self.element, "Pushing initial events");
|
||||
} else {
|
||||
gst::trace!(CAT, obj: self.element, "Pushing initial events");
|
||||
}
|
||||
|
||||
let stream_id = format!("{:08x}{:08x}", rand::random::<u32>(), rand::random::<u32>());
|
||||
let stream_start_evt = gst::event::StreamStart::builder(&stream_id)
|
||||
.group_id(gst::GroupId::next())
|
||||
.build();
|
||||
test_src.src_pad.push_event(stream_start_evt).await;
|
||||
|
||||
test_src
|
||||
.src_pad
|
||||
.push_event(gst::event::Caps::new(
|
||||
&gst::Caps::builder("foo/bar").build(),
|
||||
))
|
||||
.await;
|
||||
|
||||
self.need_initial_events = false;
|
||||
}
|
||||
|
||||
if self.need_segment {
|
||||
let segment_evt =
|
||||
gst::event::Segment::new(&gst::FormattedSegment::<gst::format::Time>::new());
|
||||
test_src.src_pad.push_event(segment_evt).await;
|
||||
|
||||
self.need_segment = false;
|
||||
}
|
||||
|
||||
if self.raise_log_level {
|
||||
gst::debug!(CAT, obj: self.element, "Forwarding buffer");
|
||||
} else {
|
||||
gst::trace!(CAT, obj: self.element, "Forwarding buffer");
|
||||
}
|
||||
|
||||
let ok = test_src.src_pad.push(buffer).await?;
|
||||
|
||||
self.buffer_count += 1;
|
||||
|
||||
if self.num_buffers.opt_eq(self.buffer_count).unwrap_or(false) {
|
||||
if self.raise_log_level {
|
||||
gst::debug!(CAT, obj: self.element, "Pushing EOS");
|
||||
} else {
|
||||
gst::trace!(CAT, obj: self.element, "Pushing EOS");
|
||||
}
|
||||
|
||||
let test_src = self.element.imp();
|
||||
if !test_src.src_pad.push_event(gst::event::Eos::new()).await {
|
||||
gst::error!(CAT, obj: self.element, "Error pushing EOS");
|
||||
}
|
||||
return Err(gst::FlowError::Eos);
|
||||
}
|
||||
|
||||
Ok(ok)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TestSrc {
|
||||
src_pad: PadSrc,
|
||||
|
@ -319,106 +253,57 @@ pub struct TestSrc {
|
|||
|
||||
impl TestSrc {
|
||||
fn prepare(&self) -> Result<(), gst::ErrorMessage> {
|
||||
let raise_log_level = self.settings.lock().unwrap().raise_log_level;
|
||||
if raise_log_level {
|
||||
gst::debug!(CAT, imp: self, "Preparing");
|
||||
} else {
|
||||
gst::trace!(CAT, imp: self, "Preparing");
|
||||
}
|
||||
let is_main_elem = self.settings.lock().unwrap().is_main_elem;
|
||||
debug_or_trace!(CAT, is_main_elem, imp: self, "Preparing");
|
||||
|
||||
let settings = self.settings.lock().unwrap();
|
||||
let context =
|
||||
Context::acquire(&settings.context, settings.context_wait).map_err(|err| {
|
||||
gst::error_msg!(
|
||||
gst::ResourceError::OpenRead,
|
||||
["Failed to acquire Context: {}", err]
|
||||
)
|
||||
})?;
|
||||
let ts_ctx = Context::acquire(&settings.context, settings.context_wait).map_err(|err| {
|
||||
gst::error_msg!(
|
||||
gst::ResourceError::OpenRead,
|
||||
["Failed to acquire Context: {}", err]
|
||||
)
|
||||
})?;
|
||||
drop(settings);
|
||||
|
||||
self.task
|
||||
.prepare(SrcTask::new(self.obj().clone()), context)
|
||||
.prepare(SrcTask::new(self.instance().clone()), ts_ctx)
|
||||
.block_on()?;
|
||||
|
||||
if raise_log_level {
|
||||
gst::debug!(CAT, imp: self, "Prepared");
|
||||
} else {
|
||||
gst::trace!(CAT, imp: self, "Prepared");
|
||||
}
|
||||
debug_or_trace!(CAT, is_main_elem, imp: self, "Prepared");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unprepare(&self) {
|
||||
let raise_log_level = self.settings.lock().unwrap().raise_log_level;
|
||||
if raise_log_level {
|
||||
gst::debug!(CAT, imp: self, "Unpreparing");
|
||||
} else {
|
||||
gst::trace!(CAT, imp: self, "Unpreparing");
|
||||
}
|
||||
|
||||
let is_main_elem = self.settings.lock().unwrap().is_main_elem;
|
||||
debug_or_trace!(CAT, is_main_elem, imp: self, "Unpreparing");
|
||||
self.task.unprepare().block_on().unwrap();
|
||||
|
||||
if raise_log_level {
|
||||
gst::debug!(CAT, imp: self, "Unprepared");
|
||||
} else {
|
||||
gst::trace!(CAT, imp: self, "Unprepared");
|
||||
}
|
||||
debug_or_trace!(CAT, is_main_elem, imp: self, "Unprepared");
|
||||
}
|
||||
|
||||
fn stop(&self) -> Result<(), gst::ErrorMessage> {
|
||||
let raise_log_level = self.settings.lock().unwrap().raise_log_level;
|
||||
if raise_log_level {
|
||||
gst::debug!(CAT, imp: self, "Stopping");
|
||||
} else {
|
||||
gst::trace!(CAT, imp: self, "Stopping");
|
||||
}
|
||||
|
||||
let is_main_elem = self.settings.lock().unwrap().is_main_elem;
|
||||
debug_or_trace!(CAT, is_main_elem, imp: self, "Stopping");
|
||||
self.task.stop().block_on()?;
|
||||
|
||||
if raise_log_level {
|
||||
gst::debug!(CAT, imp: self, "Stopped");
|
||||
} else {
|
||||
gst::trace!(CAT, imp: self, "Stopped");
|
||||
}
|
||||
debug_or_trace!(CAT, is_main_elem, imp: self, "Stopped");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn start(&self) -> Result<(), gst::ErrorMessage> {
|
||||
let raise_log_level = self.settings.lock().unwrap().raise_log_level;
|
||||
if raise_log_level {
|
||||
gst::debug!(CAT, imp: self, "Starting");
|
||||
} else {
|
||||
gst::trace!(CAT, imp: self, "Starting");
|
||||
}
|
||||
|
||||
let is_main_elem = self.settings.lock().unwrap().is_main_elem;
|
||||
debug_or_trace!(CAT, is_main_elem, imp: self, "Starting");
|
||||
self.task.start().block_on()?;
|
||||
|
||||
if raise_log_level {
|
||||
gst::debug!(CAT, imp: self, "Started");
|
||||
} else {
|
||||
gst::trace!(CAT, imp: self, "Started");
|
||||
}
|
||||
debug_or_trace!(CAT, is_main_elem, imp: self, "Started");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pause(&self) -> Result<(), gst::ErrorMessage> {
|
||||
let raise_log_level = self.settings.lock().unwrap().raise_log_level;
|
||||
if raise_log_level {
|
||||
gst::debug!(CAT, imp: self, "Pausing");
|
||||
} else {
|
||||
gst::trace!(CAT, imp: self, "Pausing");
|
||||
}
|
||||
|
||||
let is_main_elem = self.settings.lock().unwrap().is_main_elem;
|
||||
debug_or_trace!(CAT, is_main_elem, imp: self, "Pausing");
|
||||
self.task.pause().block_on()?;
|
||||
|
||||
if raise_log_level {
|
||||
gst::debug!(CAT, imp: self, "Paused");
|
||||
} else {
|
||||
gst::trace!(CAT, imp: self, "Paused");
|
||||
}
|
||||
debug_or_trace!(CAT, is_main_elem, imp: self, "Paused");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -462,9 +347,9 @@ impl ObjectImpl for TestSrc {
|
|||
.blurb("Push a new buffer every this many ms")
|
||||
.default_value(DEFAULT_PUSH_PERIOD.mseconds() as u32)
|
||||
.build(),
|
||||
glib::ParamSpecBoolean::builder("raise-log-level")
|
||||
.nick("Raise log level")
|
||||
.blurb("Raises the log level so that this element stands out")
|
||||
glib::ParamSpecBoolean::builder("main-elem")
|
||||
.nick("Main Element")
|
||||
.blurb("Declare this element as the main one")
|
||||
.write_only()
|
||||
.build(),
|
||||
glib::ParamSpecInt::builder("num-buffers")
|
||||
|
@ -485,24 +370,21 @@ impl ObjectImpl for TestSrc {
|
|||
"context" => {
|
||||
settings.context = value
|
||||
.get::<Option<String>>()
|
||||
.expect("type checked upstream")
|
||||
.unwrap()
|
||||
.unwrap_or_else(|| DEFAULT_CONTEXT.into());
|
||||
}
|
||||
"context-wait" => {
|
||||
settings.context_wait = Duration::from_millis(
|
||||
value.get::<u32>().expect("type checked upstream").into(),
|
||||
);
|
||||
settings.context_wait = Duration::from_millis(value.get::<u32>().unwrap().into());
|
||||
}
|
||||
"push-period" => {
|
||||
settings.push_period = gst::ClockTime::from_mseconds(
|
||||
value.get::<u32>().expect("type checked upstream").into(),
|
||||
);
|
||||
let value: u64 = value.get::<u32>().unwrap().into();
|
||||
settings.push_period = value.mseconds();
|
||||
}
|
||||
"raise-log-level" => {
|
||||
settings.raise_log_level = value.get::<bool>().expect("type checked upstream");
|
||||
"main-elem" => {
|
||||
settings.is_main_elem = value.get::<bool>().unwrap();
|
||||
}
|
||||
"num-buffers" => {
|
||||
let value = value.get::<i32>().expect("type checked upstream");
|
||||
let value = value.get::<i32>().unwrap();
|
||||
settings.num_buffers = if value > 0 { Some(value as u32) } else { None };
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
|
@ -515,7 +397,7 @@ impl ObjectImpl for TestSrc {
|
|||
"context" => settings.context.to_value(),
|
||||
"context-wait" => (settings.context_wait.as_millis() as u32).to_value(),
|
||||
"push-period" => (settings.push_period.mseconds() as u32).to_value(),
|
||||
"raise-log-level" => settings.raise_log_level.to_value(),
|
||||
"main-elem" => settings.is_main_elem.to_value(),
|
||||
"num-buffers" => settings
|
||||
.num_buffers
|
||||
.and_then(|val| val.try_into().ok())
|
||||
|
@ -571,7 +453,7 @@ impl ElementImpl for TestSrc {
|
|||
&self,
|
||||
transition: gst::StateChange,
|
||||
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
||||
gst::trace!(CAT, imp: self, "Changing state {:?}", transition);
|
||||
gst::trace!(CAT, imp: self, "Changing state {transition:?}");
|
||||
|
||||
match transition {
|
||||
gst::StateChange::NullToReady => {
|
||||
|
|
|
@ -3,6 +3,8 @@ use gst::prelude::*;
|
|||
|
||||
mod imp;
|
||||
|
||||
pub const ELEMENT_NAME: &str = "ts-standalone-src";
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct TestSrc(ObjectSubclass<imp::TestSrc>) @extends gst::Element, gst::Object;
|
||||
}
|
||||
|
@ -10,7 +12,7 @@ glib::wrapper! {
|
|||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
gst::Element::register(
|
||||
Some(plugin),
|
||||
"ts-standalone-test-src",
|
||||
"ts-standalone-src",
|
||||
gst::Rank::None,
|
||||
TestSrc::static_type(),
|
||||
)
|
||||
|
|
|
@ -17,19 +17,45 @@
|
|||
//
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
use gst::glib;
|
||||
use gst::prelude::*;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use std::net;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
use std::{env, thread, time};
|
||||
|
||||
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||
gst::DebugCategory::new(
|
||||
"ts-udpsrc-benchmark-sender",
|
||||
gst::DebugColorFlags::empty(),
|
||||
Some("Thread-sharing UDP src benchmark sender"),
|
||||
)
|
||||
});
|
||||
|
||||
fn main() {
|
||||
gst::init().unwrap();
|
||||
gstthreadshare::plugin_register_static().unwrap();
|
||||
|
||||
let args = env::args().collect::<Vec<_>>();
|
||||
assert!(args.len() > 1);
|
||||
let n_streams: u16 = args[1].parse().unwrap();
|
||||
|
||||
if args.len() > 2 && args[2] == "rtp" {
|
||||
send_rtp_buffers(n_streams);
|
||||
let num_buffers: Option<i32> = if args.len() > 3 {
|
||||
args[3].parse().ok()
|
||||
} else {
|
||||
send_raw_buffers(n_streams);
|
||||
None
|
||||
};
|
||||
|
||||
if args.len() > 2 {
|
||||
match args[2].as_str() {
|
||||
"raw" => send_raw_buffers(n_streams),
|
||||
"rtp" => send_rtp_buffers(n_streams, num_buffers),
|
||||
_ => send_test_buffers(n_streams, num_buffers),
|
||||
}
|
||||
} else {
|
||||
send_test_buffers(n_streams, num_buffers);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,7 +64,7 @@ fn send_raw_buffers(n_streams: u16) {
|
|||
let socket = net::UdpSocket::bind("0.0.0.0:0").unwrap();
|
||||
|
||||
let ipaddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
|
||||
let destinations = (40000..(40000 + n_streams))
|
||||
let destinations = (5004..(5004 + n_streams))
|
||||
.map(|port| SocketAddr::new(ipaddr, port))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
|
@ -60,43 +86,60 @@ fn send_raw_buffers(n_streams: u16) {
|
|||
}
|
||||
}
|
||||
|
||||
fn send_rtp_buffers(n_streams: u16) {
|
||||
use gst::glib;
|
||||
use gst::prelude::*;
|
||||
|
||||
gst::init().unwrap();
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
use std::path::Path;
|
||||
|
||||
let mut path = Path::new("target/debug");
|
||||
if !path.exists() {
|
||||
path = Path::new("../../target/debug");
|
||||
}
|
||||
|
||||
gst::Registry::get().scan_path(path);
|
||||
}
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
use std::path::Path;
|
||||
|
||||
let mut path = Path::new("target/release");
|
||||
if !path.exists() {
|
||||
path = Path::new("../../target/release");
|
||||
}
|
||||
|
||||
gst::Registry::get().scan_path(path);
|
||||
}
|
||||
|
||||
let l = glib::MainLoop::new(None, false);
|
||||
fn send_test_buffers(n_streams: u16, num_buffers: Option<i32>) {
|
||||
let pipeline = gst::Pipeline::default();
|
||||
for i in 0..n_streams {
|
||||
let src = gst::ElementFactory::make("audiotestsrc")
|
||||
.name(format!("audiotestsrc-{}", i).as_str())
|
||||
let src = gst::ElementFactory::make("ts-audiotestsrc")
|
||||
.name(format!("ts-audiotestsrc-{}", i).as_str())
|
||||
.property("context-wait", 20u32)
|
||||
.property("is-live", true)
|
||||
.property("do-timestamp", true)
|
||||
.build()
|
||||
.unwrap();
|
||||
src.set_property("is-live", true);
|
||||
|
||||
if let Some(num_buffers) = num_buffers {
|
||||
src.set_property("num-buffers", num_buffers);
|
||||
}
|
||||
|
||||
#[cfg(feature = "tuning")]
|
||||
if i == 0 {
|
||||
src.set_property("main-elem", true);
|
||||
}
|
||||
|
||||
let sink = gst::ElementFactory::make("ts-udpsink")
|
||||
.name(format!("udpsink-{}", i).as_str())
|
||||
.property("clients", format!("127.0.0.1:{}", i + 5004))
|
||||
.property("context-wait", 20u32)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let elements = &[&src, &sink];
|
||||
pipeline.add_many(elements).unwrap();
|
||||
gst::Element::link_many(elements).unwrap();
|
||||
}
|
||||
|
||||
run(pipeline);
|
||||
}
|
||||
|
||||
fn send_rtp_buffers(n_streams: u16, num_buffers: Option<i32>) {
|
||||
let pipeline = gst::Pipeline::default();
|
||||
for i in 0..n_streams {
|
||||
let src = gst::ElementFactory::make("ts-audiotestsrc")
|
||||
.name(format!("ts-audiotestsrc-{}", i).as_str())
|
||||
.property("context-wait", 20u32)
|
||||
.property("is-live", true)
|
||||
.property("do-timestamp", true)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
if let Some(num_buffers) = num_buffers {
|
||||
src.set_property("num-buffers", num_buffers);
|
||||
}
|
||||
|
||||
#[cfg(feature = "tuning")]
|
||||
if i == 0 {
|
||||
src.set_property("main-elem", true);
|
||||
}
|
||||
|
||||
let enc = gst::ElementFactory::make("alawenc")
|
||||
.name(format!("alawenc-{}", i).as_str())
|
||||
|
@ -106,11 +149,11 @@ fn send_rtp_buffers(n_streams: u16) {
|
|||
.name(format!("rtppcmapay-{}", i).as_str())
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let sink = gst::ElementFactory::make("ts-udpsink")
|
||||
.name(format!("udpsink-{}", i).as_str())
|
||||
.property("clients", format!("127.0.0.1:{}", i + 40000))
|
||||
.property("context", "context-udpsink")
|
||||
.property("context-wait", 20u32)
|
||||
.property("clients", format!("127.0.0.1:{}", i + 5004))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
|
@ -119,6 +162,42 @@ fn send_rtp_buffers(n_streams: u16) {
|
|||
gst::Element::link_many(elements).unwrap();
|
||||
}
|
||||
|
||||
run(pipeline);
|
||||
}
|
||||
|
||||
fn run(pipeline: gst::Pipeline) {
|
||||
let l = glib::MainLoop::new(None, false);
|
||||
|
||||
let bus = pipeline.bus().unwrap();
|
||||
let l_clone = l.clone();
|
||||
bus.add_watch(move |_, msg| {
|
||||
use gst::MessageView;
|
||||
match msg.view() {
|
||||
MessageView::Eos(_) => {
|
||||
gst::info!(CAT, "Received eos");
|
||||
l_clone.quit();
|
||||
|
||||
glib::Continue(false)
|
||||
}
|
||||
MessageView::Error(msg) => {
|
||||
gst::error!(
|
||||
CAT,
|
||||
"Error from {:?}: {} ({:?})",
|
||||
msg.src().map(|s| s.path_string()),
|
||||
msg.error(),
|
||||
msg.debug()
|
||||
);
|
||||
l_clone.quit();
|
||||
|
||||
glib::Continue(false)
|
||||
}
|
||||
_ => glib::Continue(true),
|
||||
}
|
||||
})
|
||||
.expect("Failed to add bus watch");
|
||||
|
||||
pipeline.set_state(gst::State::Playing).unwrap();
|
||||
l.run();
|
||||
|
||||
pipeline.set_state(gst::State::Null).unwrap();
|
||||
}
|
||||
|
|
735
generic/threadshare/src/audiotestsrc/imp.rs
Normal file
735
generic/threadshare/src/audiotestsrc/imp.rs
Normal file
|
@ -0,0 +1,735 @@
|
|||
// Copyright (C) 2022 François Laignel <fengalin@free.fr>
|
||||
//
|
||||
// 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 futures::future::BoxFuture;
|
||||
use futures::prelude::*;
|
||||
|
||||
use gst::glib;
|
||||
use gst::prelude::*;
|
||||
use gst::subclass::prelude::*;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use std::mem::size_of;
|
||||
use std::sync::Mutex;
|
||||
use std::time::Duration;
|
||||
#[cfg(feature = "tuning")]
|
||||
use std::time::Instant;
|
||||
|
||||
use crate::runtime::prelude::*;
|
||||
use crate::runtime::{self, task, timer, PadSrc, Task};
|
||||
|
||||
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||
gst::DebugCategory::new(
|
||||
"ts-audiotestsrc",
|
||||
gst::DebugColorFlags::empty(),
|
||||
Some("Thread-sharing audio test src"),
|
||||
)
|
||||
});
|
||||
|
||||
const DEFAULT_CONTEXT: &str = "";
|
||||
const DEFAULT_CONTEXT_WAIT: Duration = Duration::ZERO;
|
||||
const DEFAULT_BUFFER_DURATION: gst::ClockTime = gst::ClockTime::from_mseconds(10);
|
||||
const DEFAULT_DO_TIMESTAMP: bool = false;
|
||||
const DEFAULT_IS_LIVE: bool = false;
|
||||
const DEFAULT_NUM_BUFFERS: i32 = -1;
|
||||
|
||||
const DEFAULT_CHANNELS: usize = 1;
|
||||
const DEFAULT_FREQ: f32 = 440.0;
|
||||
const DEFAULT_VOLUME: f32 = 0.8;
|
||||
const DEFAULT_RATE: u32 = 44_100;
|
||||
|
||||
#[cfg(feature = "tuning")]
|
||||
const RAMPUP_BUFFER_COUNT: u32 = 500;
|
||||
#[cfg(feature = "tuning")]
|
||||
const LOG_BUFFER_INTERVAL: u32 = 2000;
|
||||
|
||||
static DEFAULT_CAPS: Lazy<gst::Caps> = Lazy::new(|| {
|
||||
gst_audio::AudioCapsBuilder::new_interleaved()
|
||||
.format(gst_audio::AUDIO_FORMAT_S16)
|
||||
.rate_range(8_000..i32::MAX)
|
||||
.channels_range(1..i32::MAX)
|
||||
.build()
|
||||
});
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Settings {
|
||||
context: String,
|
||||
context_wait: Duration,
|
||||
do_timestamp: bool,
|
||||
is_live: bool,
|
||||
buffer_duration: gst::ClockTime,
|
||||
num_buffers: Option<u32>,
|
||||
#[cfg(feature = "tuning")]
|
||||
is_main_elem: bool,
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Settings {
|
||||
context: DEFAULT_CONTEXT.into(),
|
||||
context_wait: DEFAULT_CONTEXT_WAIT,
|
||||
do_timestamp: DEFAULT_DO_TIMESTAMP,
|
||||
is_live: DEFAULT_IS_LIVE,
|
||||
buffer_duration: DEFAULT_BUFFER_DURATION,
|
||||
num_buffers: None,
|
||||
#[cfg(feature = "tuning")]
|
||||
is_main_elem: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct AudioTestSrcPadHandler;
|
||||
impl PadSrcHandler for AudioTestSrcPadHandler {
|
||||
type ElementImpl = AudioTestSrc;
|
||||
|
||||
fn src_query(self, pad: &gst::Pad, imp: &Self::ElementImpl, query: &mut gst::QueryRef) -> bool {
|
||||
gst::debug!(CAT, obj: pad, "Received {query:?}");
|
||||
|
||||
if let gst::QueryViewMut::Latency(q) = query.view_mut() {
|
||||
let settings = imp.settings.lock().unwrap();
|
||||
let min_latency = if settings.is_live {
|
||||
settings.buffer_duration
|
||||
} else {
|
||||
gst::ClockTime::ZERO
|
||||
};
|
||||
|
||||
q.set(
|
||||
settings.is_live,
|
||||
min_latency,
|
||||
min_latency
|
||||
+ runtime::Context::current().map_or(gst::ClockTime::ZERO, |ctx| {
|
||||
gst::ClockTime::try_from(ctx.wait_duration()).unwrap()
|
||||
}),
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
gst::Pad::query_default(pad, Some(&*imp.obj()), query)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
enum Negotiation {
|
||||
Unchanged,
|
||||
Changed,
|
||||
}
|
||||
|
||||
impl Negotiation {
|
||||
fn has_changed(self) -> bool {
|
||||
matches!(self, Negotiation::Changed)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct AudioTestSrcTask {
|
||||
elem: super::AudioTestSrc,
|
||||
buffer_pool: gst::BufferPool,
|
||||
rate: u32,
|
||||
channels: usize,
|
||||
do_timestamp: bool,
|
||||
is_live: bool,
|
||||
buffer_duration: gst::ClockTime,
|
||||
need_initial_events: bool,
|
||||
step: f32,
|
||||
accumulator: f32,
|
||||
last_buffer_end: Option<gst::ClockTime>,
|
||||
caps: gst::Caps,
|
||||
buffer_count: u32,
|
||||
num_buffers: Option<u32>,
|
||||
#[cfg(feature = "tuning")]
|
||||
is_main_elem: bool,
|
||||
#[cfg(feature = "tuning")]
|
||||
parked_duration_init: Option<Duration>,
|
||||
#[cfg(feature = "tuning")]
|
||||
log_start: Instant,
|
||||
}
|
||||
|
||||
impl AudioTestSrcTask {
|
||||
fn new(elem: super::AudioTestSrc) -> Self {
|
||||
AudioTestSrcTask {
|
||||
elem,
|
||||
buffer_pool: gst::BufferPool::new(),
|
||||
rate: DEFAULT_RATE,
|
||||
channels: DEFAULT_CHANNELS,
|
||||
do_timestamp: DEFAULT_DO_TIMESTAMP,
|
||||
is_live: DEFAULT_IS_LIVE,
|
||||
buffer_duration: DEFAULT_BUFFER_DURATION,
|
||||
need_initial_events: true,
|
||||
step: 0.0,
|
||||
accumulator: 0.0,
|
||||
last_buffer_end: None,
|
||||
caps: gst::Caps::new_empty(),
|
||||
buffer_count: 0,
|
||||
num_buffers: None,
|
||||
#[cfg(feature = "tuning")]
|
||||
is_main_elem: false,
|
||||
#[cfg(feature = "tuning")]
|
||||
parked_duration_init: None,
|
||||
#[cfg(feature = "tuning")]
|
||||
log_start: Instant::now(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn negotiate(&mut self) -> Result<Negotiation, gst::ErrorMessage> {
|
||||
let imp = self.elem.imp();
|
||||
let pad = imp.src_pad.gst_pad();
|
||||
|
||||
if !pad.check_reconfigure() {
|
||||
return Ok(Negotiation::Unchanged);
|
||||
}
|
||||
|
||||
let mut caps = pad.peer_query_caps(Some(&DEFAULT_CAPS));
|
||||
gst::debug!(CAT, imp: imp, "Peer returned {caps:?}");
|
||||
|
||||
if caps.is_empty() {
|
||||
pad.mark_reconfigure();
|
||||
let err = gst::error_msg!(gst::CoreError::Pad, ["No common Caps"]);
|
||||
gst::error!(CAT, imp: imp, "{err}");
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
if caps.is_any() {
|
||||
gst::debug!(CAT, imp: imp, "Using our own Caps");
|
||||
caps = DEFAULT_CAPS.clone();
|
||||
}
|
||||
|
||||
{
|
||||
let caps = caps.make_mut();
|
||||
let s = caps.structure_mut(0).ok_or_else(|| {
|
||||
let err = gst::error_msg!(gst::CoreError::Pad, ["Invalid peer Caps structure"]);
|
||||
gst::error!(CAT, imp: imp, "{err}");
|
||||
err
|
||||
})?;
|
||||
|
||||
s.fixate_field_nearest_int("rate", DEFAULT_RATE as i32);
|
||||
self.rate = s.get::<i32>("rate").unwrap() as u32;
|
||||
self.step = 2.0 * std::f32::consts::PI * DEFAULT_FREQ / (self.rate as f32);
|
||||
|
||||
s.fixate_field_nearest_int("channels", DEFAULT_CHANNELS as i32);
|
||||
self.channels = s.get::<i32>("channels").unwrap() as usize;
|
||||
|
||||
if self.channels > 2 {
|
||||
s.set::<gst::Bitmask>(
|
||||
"channel-mask",
|
||||
gst_audio::AudioChannelPosition::fallback_mask(self.channels as u32).into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
caps.fixate();
|
||||
gst::debug!(CAT, imp: imp, "fixated to {caps:?}");
|
||||
|
||||
imp.src_pad.push_event(gst::event::Caps::new(&caps)).await;
|
||||
|
||||
self.caps = caps;
|
||||
|
||||
Ok(Negotiation::Changed)
|
||||
}
|
||||
}
|
||||
|
||||
impl TaskImpl for AudioTestSrcTask {
|
||||
type Item = gst::Buffer;
|
||||
|
||||
fn prepare(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
||||
gst::log!(CAT, obj: self.elem, "Preparing Task");
|
||||
|
||||
let imp = self.elem.imp();
|
||||
let settings = imp.settings.lock().unwrap();
|
||||
self.do_timestamp = settings.do_timestamp;
|
||||
self.is_live = settings.is_live;
|
||||
self.buffer_duration = settings.buffer_duration;
|
||||
self.num_buffers = settings.num_buffers;
|
||||
|
||||
#[cfg(feature = "tuning")]
|
||||
{
|
||||
self.is_main_elem = settings.is_main_elem;
|
||||
}
|
||||
|
||||
future::ok(()).boxed()
|
||||
}
|
||||
|
||||
fn start(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
||||
async move {
|
||||
gst::log!(CAT, obj: self.elem, "Starting Task");
|
||||
|
||||
if self.need_initial_events {
|
||||
gst::debug!(CAT, obj: self.elem, "Pushing initial events");
|
||||
|
||||
let stream_id =
|
||||
format!("{:08x}{:08x}", rand::random::<u32>(), rand::random::<u32>());
|
||||
let stream_start_evt = gst::event::StreamStart::builder(&stream_id)
|
||||
.group_id(gst::GroupId::next())
|
||||
.build();
|
||||
self.elem.imp().src_pad.push_event(stream_start_evt).await;
|
||||
}
|
||||
|
||||
if self.negotiate().await?.has_changed() {
|
||||
let bytes_per_buffer = (self.rate as u64)
|
||||
* self.buffer_duration.mseconds()
|
||||
* self.channels as u64
|
||||
* size_of::<i16>() as u64
|
||||
/ 1_000;
|
||||
|
||||
let mut pool_config = self.buffer_pool.config();
|
||||
pool_config
|
||||
.as_mut()
|
||||
.set_params(Some(&self.caps), bytes_per_buffer as u32, 2, 6);
|
||||
self.buffer_pool.set_config(pool_config).unwrap();
|
||||
}
|
||||
|
||||
assert!(!self.caps.is_empty());
|
||||
self.buffer_pool.set_active(true).unwrap();
|
||||
|
||||
if self.need_initial_events {
|
||||
let segment_evt =
|
||||
gst::event::Segment::new(&gst::FormattedSegment::<gst::format::Time>::new());
|
||||
self.elem.imp().src_pad.push_event(segment_evt).await;
|
||||
|
||||
self.need_initial_events = false;
|
||||
}
|
||||
|
||||
self.buffer_count = 0;
|
||||
|
||||
#[cfg(feature = "tuning")]
|
||||
if self.is_main_elem {
|
||||
self.parked_duration_init = None;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn pause(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
||||
gst::log!(CAT, obj: self.elem, "Pausing Task");
|
||||
self.buffer_pool.set_active(false).unwrap();
|
||||
|
||||
future::ok(()).boxed()
|
||||
}
|
||||
|
||||
fn stop(&mut self) -> BoxFuture<'_, Result<(), gst::ErrorMessage>> {
|
||||
gst::log!(CAT, obj: self.elem, "Stopping Task");
|
||||
|
||||
self.need_initial_events = true;
|
||||
self.accumulator = 0.0;
|
||||
self.last_buffer_end = None;
|
||||
|
||||
future::ok(()).boxed()
|
||||
}
|
||||
|
||||
fn try_next(&mut self) -> BoxFuture<'_, Result<gst::Buffer, gst::FlowError>> {
|
||||
let mut buffer = match self.buffer_pool.acquire_buffer(None) {
|
||||
Ok(buffer) => buffer,
|
||||
Err(err) => {
|
||||
gst::error!(CAT, obj: self.elem, "Failed to acquire buffer {}", err);
|
||||
return future::err(err).boxed();
|
||||
}
|
||||
};
|
||||
|
||||
let buffer_mut = buffer.get_mut().unwrap();
|
||||
|
||||
let start = if self.is_live | self.do_timestamp {
|
||||
self.last_buffer_end
|
||||
.or_else(|| self.elem.current_running_time())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
{
|
||||
use std::io::Write;
|
||||
|
||||
let mut mapped = buffer_mut.map_writable().unwrap();
|
||||
let slice = mapped.as_mut_slice();
|
||||
slice
|
||||
.chunks_mut(self.channels * size_of::<i16>())
|
||||
.for_each(|frame| {
|
||||
let sample = ((self.accumulator.sin() * DEFAULT_VOLUME * (i16::MAX as f32))
|
||||
as i16)
|
||||
.to_ne_bytes();
|
||||
|
||||
frame.chunks_mut(size_of::<i16>()).for_each(|mut channel| {
|
||||
let _ = channel.write(&sample).unwrap();
|
||||
});
|
||||
|
||||
self.accumulator += self.step;
|
||||
if self.accumulator >= 2.0 * std::f32::consts::PI {
|
||||
self.accumulator = -2.0 * std::f32::consts::PI;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if self.do_timestamp {
|
||||
buffer_mut.set_dts(start);
|
||||
buffer_mut.set_duration(self.buffer_duration);
|
||||
}
|
||||
|
||||
self.last_buffer_end = start.opt_add(self.buffer_duration);
|
||||
|
||||
async move {
|
||||
if self.is_live {
|
||||
if let Some(delay) = self
|
||||
.last_buffer_end
|
||||
.unwrap()
|
||||
.checked_sub(self.elem.current_running_time().unwrap())
|
||||
{
|
||||
// Wait for all samples to fit in last time slice
|
||||
timer::delay_for_at_least(delay.into()).await;
|
||||
}
|
||||
} else {
|
||||
// Let the scheduler share time with other tasks
|
||||
runtime::executor::yield_now().await;
|
||||
}
|
||||
|
||||
Ok(buffer)
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn handle_item(&mut self, buffer: gst::Buffer) -> BoxFuture<'_, Result<(), gst::FlowError>> {
|
||||
async move {
|
||||
let imp = self.elem.imp();
|
||||
|
||||
gst::debug!(CAT, imp: imp, "Pushing {buffer:?}");
|
||||
imp.src_pad.push(buffer).await?;
|
||||
gst::log!(CAT, imp: imp, "Successfully pushed buffer");
|
||||
|
||||
self.buffer_count += 1;
|
||||
|
||||
#[cfg(feature = "tuning")]
|
||||
if self.is_main_elem {
|
||||
if let Some(parked_duration_init) = self.parked_duration_init {
|
||||
if self.buffer_count % LOG_BUFFER_INTERVAL == 0 {
|
||||
let parked_duration =
|
||||
runtime::Context::current().unwrap().parked_duration()
|
||||
- parked_duration_init;
|
||||
|
||||
gst::info!(
|
||||
CAT,
|
||||
"Parked: {:5.2?}%",
|
||||
parked_duration.as_nanos() as f32 * 100.0
|
||||
/ self.log_start.elapsed().as_nanos() as f32,
|
||||
);
|
||||
}
|
||||
} else if self.buffer_count == RAMPUP_BUFFER_COUNT {
|
||||
self.parked_duration_init =
|
||||
Some(runtime::Context::current().unwrap().parked_duration());
|
||||
self.log_start = Instant::now();
|
||||
|
||||
gst::info!(CAT, "Ramp up complete");
|
||||
}
|
||||
}
|
||||
|
||||
if self.num_buffers.opt_eq(self.buffer_count) == Some(true) {
|
||||
return Err(gst::FlowError::Eos);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn handle_loop_error(&mut self, err: gst::FlowError) -> BoxFuture<'_, task::Trigger> {
|
||||
async move {
|
||||
match err {
|
||||
gst::FlowError::Flushing => {
|
||||
gst::debug!(CAT, obj: self.elem, "Flushing");
|
||||
|
||||
task::Trigger::FlushStart
|
||||
}
|
||||
gst::FlowError::Eos => {
|
||||
gst::debug!(CAT, obj: self.elem, "EOS");
|
||||
self.elem
|
||||
.imp()
|
||||
.src_pad
|
||||
.push_event(gst::event::Eos::new())
|
||||
.await;
|
||||
|
||||
task::Trigger::Stop
|
||||
}
|
||||
err => {
|
||||
gst::error!(CAT, obj: self.elem, "Got error {err}");
|
||||
gst::element_error!(
|
||||
&self.elem,
|
||||
gst::StreamError::Failed,
|
||||
("Internal data stream error"),
|
||||
["streaming stopped, reason {}", err]
|
||||
);
|
||||
|
||||
task::Trigger::Error
|
||||
}
|
||||
}
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AudioTestSrc {
|
||||
src_pad: PadSrc,
|
||||
task: Task,
|
||||
settings: Mutex<Settings>,
|
||||
}
|
||||
|
||||
impl AudioTestSrc {
|
||||
fn prepare(&self) -> Result<(), gst::ErrorMessage> {
|
||||
gst::debug!(CAT, imp: self, "Preparing");
|
||||
|
||||
let settings = self.settings.lock().unwrap();
|
||||
let context =
|
||||
runtime::Context::acquire(&settings.context, settings.context_wait).map_err(|err| {
|
||||
gst::error_msg!(
|
||||
gst::ResourceError::OpenRead,
|
||||
["Failed to acquire Context: {}", err]
|
||||
)
|
||||
})?;
|
||||
drop(settings);
|
||||
|
||||
self.task
|
||||
.prepare(AudioTestSrcTask::new(self.obj().clone()), context)
|
||||
.block_on()?;
|
||||
|
||||
gst::debug!(CAT, imp: self, "Prepared");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unprepare(&self) {
|
||||
gst::debug!(CAT, imp: self, "Unpreparing");
|
||||
self.task.unprepare().block_on().unwrap();
|
||||
gst::debug!(CAT, imp: self, "Unprepared");
|
||||
}
|
||||
|
||||
fn stop(&self) -> Result<(), gst::ErrorMessage> {
|
||||
gst::debug!(CAT, imp: self, "Stopping");
|
||||
self.task.stop().block_on()?;
|
||||
gst::debug!(CAT, imp: self, "Stopped");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn start(&self) -> Result<(), gst::ErrorMessage> {
|
||||
gst::debug!(CAT, imp: self, "Starting");
|
||||
self.task.start().block_on()?;
|
||||
gst::debug!(CAT, imp: self, "Started");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pause(&self) -> Result<(), gst::ErrorMessage> {
|
||||
gst::debug!(CAT, imp: self, "Pausing");
|
||||
self.task.pause().block_on()?;
|
||||
gst::debug!(CAT, imp: self, "Paused");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for AudioTestSrc {
|
||||
const NAME: &'static str = "TsAudioTestSrc";
|
||||
type Type = super::AudioTestSrc;
|
||||
type ParentType = gst::Element;
|
||||
|
||||
fn with_class(klass: &Self::Class) -> Self {
|
||||
Self {
|
||||
src_pad: PadSrc::new(
|
||||
gst::Pad::from_template(&klass.pad_template("src").unwrap(), Some("src")),
|
||||
AudioTestSrcPadHandler,
|
||||
),
|
||||
task: Task::default(),
|
||||
settings: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for AudioTestSrc {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![
|
||||
glib::ParamSpecString::builder("context")
|
||||
.nick("Context")
|
||||
.blurb("Context name to share threads with")
|
||||
.default_value(Some(DEFAULT_CONTEXT))
|
||||
.build(),
|
||||
glib::ParamSpecUInt::builder("context-wait")
|
||||
.nick("Context Wait")
|
||||
.blurb("Throttle poll loop to run at most once every this many ms")
|
||||
.maximum(1000)
|
||||
.default_value(DEFAULT_CONTEXT_WAIT.as_millis() as u32)
|
||||
.build(),
|
||||
glib::ParamSpecBoolean::builder("do-timestamp")
|
||||
.nick("Do timestamp")
|
||||
.blurb("Apply current stream time to buffers")
|
||||
.build(),
|
||||
glib::ParamSpecBoolean::builder("is-live")
|
||||
.nick("Is live")
|
||||
.blurb("Whether to act as a live source")
|
||||
.build(),
|
||||
glib::ParamSpecUInt::builder("buffer-duration")
|
||||
.nick("Buffer duration")
|
||||
.blurb("Buffer duration in ms")
|
||||
.default_value(DEFAULT_BUFFER_DURATION.mseconds() as u32)
|
||||
.build(),
|
||||
glib::ParamSpecInt::builder("num-buffers")
|
||||
.nick("Num Buffers")
|
||||
.blurb("Number of buffers to output before sending EOS (-1 = unlimited)")
|
||||
.minimum(-1i32)
|
||||
.default_value(DEFAULT_NUM_BUFFERS)
|
||||
.build(),
|
||||
#[cfg(feature = "tuning")]
|
||||
glib::ParamSpecBoolean::builder("main-elem")
|
||||
.nick("Main Element")
|
||||
.blurb("Declare this element as the main one")
|
||||
.write_only()
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
let mut settings = self.settings.lock().unwrap();
|
||||
match pspec.name() {
|
||||
"context" => {
|
||||
settings.context = value
|
||||
.get::<Option<String>>()
|
||||
.unwrap()
|
||||
.unwrap_or_else(|| DEFAULT_CONTEXT.into());
|
||||
}
|
||||
"context-wait" => {
|
||||
settings.context_wait = Duration::from_millis(value.get::<u32>().unwrap().into());
|
||||
}
|
||||
"do-timestamp" => {
|
||||
settings.do_timestamp = value.get::<bool>().unwrap();
|
||||
}
|
||||
"is-live" => {
|
||||
settings.is_live = value.get::<bool>().unwrap();
|
||||
}
|
||||
"buffer-duration" => {
|
||||
settings.buffer_duration = (value.get::<u32>().unwrap() as u64).mseconds();
|
||||
}
|
||||
"num-buffers" => {
|
||||
let value = value.get::<i32>().unwrap();
|
||||
settings.num_buffers = if value > 0 { Some(value as u32) } else { None };
|
||||
}
|
||||
#[cfg(feature = "tuning")]
|
||||
"main-elem" => {
|
||||
settings.is_main_elem = value.get::<bool>().unwrap();
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
let settings = self.settings.lock().unwrap();
|
||||
match pspec.name() {
|
||||
"context" => settings.context.to_value(),
|
||||
"context-wait" => (settings.context_wait.as_millis() as u32).to_value(),
|
||||
"do-timestamp" => settings.do_timestamp.to_value(),
|
||||
"is-live" => settings.is_live.to_value(),
|
||||
"buffer-duration" => (settings.buffer_duration.mseconds() as u32).to_value(),
|
||||
"num-buffers" => settings
|
||||
.num_buffers
|
||||
.and_then(|val| val.try_into().ok())
|
||||
.unwrap_or(-1i32)
|
||||
.to_value(),
|
||||
#[cfg(feature = "tuning")]
|
||||
"main-elem" => settings.is_main_elem.to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
let obj = self.obj();
|
||||
obj.add_pad(self.src_pad.gst_pad()).unwrap();
|
||||
obj.set_element_flags(gst::ElementFlags::SOURCE);
|
||||
}
|
||||
}
|
||||
|
||||
impl GstObjectImpl for AudioTestSrc {}
|
||||
|
||||
impl ElementImpl for AudioTestSrc {
|
||||
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||
gst::subclass::ElementMetadata::new(
|
||||
"Thread-sharing audio test source",
|
||||
"Source/Test",
|
||||
"Thread-sharing audio test source",
|
||||
"François Laignel <fengalin@free.fr>",
|
||||
)
|
||||
});
|
||||
|
||||
Some(&*ELEMENT_METADATA)
|
||||
}
|
||||
|
||||
fn pad_templates() -> &'static [gst::PadTemplate] {
|
||||
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
||||
let src_pad_template = gst::PadTemplate::new(
|
||||
"src",
|
||||
gst::PadDirection::Src,
|
||||
gst::PadPresence::Always,
|
||||
&DEFAULT_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:?}");
|
||||
|
||||
match transition {
|
||||
gst::StateChange::NullToReady => {
|
||||
self.prepare().map_err(|err| {
|
||||
self.post_error_message(err);
|
||||
gst::StateChangeError
|
||||
})?;
|
||||
}
|
||||
gst::StateChange::PlayingToPaused => {
|
||||
self.pause().map_err(|_| gst::StateChangeError)?;
|
||||
}
|
||||
gst::StateChange::ReadyToNull => {
|
||||
self.unprepare();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let mut success = self.parent_change_state(transition)?;
|
||||
|
||||
match transition {
|
||||
gst::StateChange::ReadyToPaused => {
|
||||
self.pause().map_err(|_| gst::StateChangeError)?;
|
||||
success = gst::StateChangeSuccess::NoPreroll;
|
||||
}
|
||||
gst::StateChange::PausedToPlaying => {
|
||||
self.start().map_err(|_| gst::StateChangeError)?;
|
||||
}
|
||||
gst::StateChange::PlayingToPaused => {
|
||||
success = gst::StateChangeSuccess::NoPreroll;
|
||||
}
|
||||
gst::StateChange::PausedToReady => {
|
||||
self.stop().map_err(|_| gst::StateChangeError)?;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
Ok(success)
|
||||
}
|
||||
}
|
17
generic/threadshare/src/audiotestsrc/mod.rs
Normal file
17
generic/threadshare/src/audiotestsrc/mod.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
use gst::glib;
|
||||
use gst::prelude::*;
|
||||
|
||||
mod imp;
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct AudioTestSrc(ObjectSubclass<imp::AudioTestSrc>) @extends gst::Element, gst::Object;
|
||||
}
|
||||
|
||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
gst::Element::register(
|
||||
Some(plugin),
|
||||
"ts-audiotestsrc",
|
||||
gst::Rank::None,
|
||||
AudioTestSrc::static_type(),
|
||||
)
|
||||
}
|
|
@ -57,27 +57,27 @@ pub const RTP_JITTER_BUFFER_MODE_BUFFER: RTPJitterBufferMode = 2;
|
|||
pub const RTP_JITTER_BUFFER_MODE_SYNCED: RTPJitterBufferMode = 4;
|
||||
|
||||
extern "C" {
|
||||
pub fn rtp_jitter_buffer_new() -> *mut RTPJitterBuffer;
|
||||
pub fn rtp_jitter_buffer_get_type() -> GType;
|
||||
pub fn ts_rtp_jitter_buffer_new() -> *mut RTPJitterBuffer;
|
||||
pub fn ts_rtp_jitter_buffer_get_type() -> GType;
|
||||
#[allow(dead_code)]
|
||||
pub fn rtp_jitter_buffer_get_mode(jbuf: *mut RTPJitterBuffer) -> RTPJitterBufferMode;
|
||||
pub fn ts_rtp_jitter_buffer_get_mode(jbuf: *mut RTPJitterBuffer) -> RTPJitterBufferMode;
|
||||
#[allow(dead_code)]
|
||||
pub fn rtp_jitter_buffer_set_mode(jbuf: *mut RTPJitterBuffer, mode: RTPJitterBufferMode);
|
||||
pub fn ts_rtp_jitter_buffer_set_mode(jbuf: *mut RTPJitterBuffer, mode: RTPJitterBufferMode);
|
||||
#[allow(dead_code)]
|
||||
pub fn rtp_jitter_buffer_get_delay(jbuf: *mut RTPJitterBuffer) -> GstClockTime;
|
||||
pub fn rtp_jitter_buffer_set_delay(jbuf: *mut RTPJitterBuffer, delay: GstClockTime);
|
||||
pub fn rtp_jitter_buffer_set_clock_rate(jbuf: *mut RTPJitterBuffer, clock_rate: c_uint);
|
||||
pub fn ts_rtp_jitter_buffer_get_delay(jbuf: *mut RTPJitterBuffer) -> GstClockTime;
|
||||
pub fn ts_rtp_jitter_buffer_set_delay(jbuf: *mut RTPJitterBuffer, delay: GstClockTime);
|
||||
pub fn ts_rtp_jitter_buffer_set_clock_rate(jbuf: *mut RTPJitterBuffer, clock_rate: c_uint);
|
||||
#[allow(dead_code)]
|
||||
pub fn rtp_jitter_buffer_get_clock_rate(jbuf: *mut RTPJitterBuffer) -> c_uint;
|
||||
pub fn rtp_jitter_buffer_reset_skew(jbuf: *mut RTPJitterBuffer);
|
||||
pub fn ts_rtp_jitter_buffer_get_clock_rate(jbuf: *mut RTPJitterBuffer) -> c_uint;
|
||||
pub fn ts_rtp_jitter_buffer_reset_skew(jbuf: *mut RTPJitterBuffer);
|
||||
|
||||
pub fn rtp_jitter_buffer_flush(jbuf: *mut RTPJitterBuffer, free_func: glib::ffi::GFunc);
|
||||
pub fn rtp_jitter_buffer_find_earliest(
|
||||
pub fn ts_rtp_jitter_buffer_flush(jbuf: *mut RTPJitterBuffer, free_func: glib::ffi::GFunc);
|
||||
pub fn ts_rtp_jitter_buffer_find_earliest(
|
||||
jbuf: *mut RTPJitterBuffer,
|
||||
pts: *mut GstClockTime,
|
||||
seqnum: *mut c_uint,
|
||||
);
|
||||
pub fn rtp_jitter_buffer_calculate_pts(
|
||||
pub fn ts_rtp_jitter_buffer_calculate_pts(
|
||||
jbuf: *mut RTPJitterBuffer,
|
||||
dts: GstClockTime,
|
||||
estimated_dts: gboolean,
|
||||
|
@ -86,30 +86,30 @@ extern "C" {
|
|||
gap: c_int,
|
||||
is_rtx: gboolean,
|
||||
) -> GstClockTime;
|
||||
pub fn rtp_jitter_buffer_insert(
|
||||
pub fn ts_rtp_jitter_buffer_insert(
|
||||
jbuf: *mut RTPJitterBuffer,
|
||||
item: *mut RTPJitterBufferItem,
|
||||
head: *mut gboolean,
|
||||
percent: *mut c_int,
|
||||
) -> gboolean;
|
||||
pub fn rtp_jitter_buffer_pop(
|
||||
pub fn ts_rtp_jitter_buffer_pop(
|
||||
jbuf: *mut RTPJitterBuffer,
|
||||
percent: *mut c_int,
|
||||
) -> *mut RTPJitterBufferItem;
|
||||
pub fn rtp_jitter_buffer_peek(jbuf: *mut RTPJitterBuffer) -> *mut RTPJitterBufferItem;
|
||||
pub fn ts_rtp_jitter_buffer_peek(jbuf: *mut RTPJitterBuffer) -> *mut RTPJitterBufferItem;
|
||||
|
||||
pub fn gst_rtp_packet_rate_ctx_reset(ctx: *mut RTPPacketRateCtx, clock_rate: c_int);
|
||||
pub fn gst_rtp_packet_rate_ctx_update(
|
||||
pub fn ts_gst_rtp_packet_rate_ctx_reset(ctx: *mut RTPPacketRateCtx, clock_rate: c_int);
|
||||
pub fn ts_gst_rtp_packet_rate_ctx_update(
|
||||
ctx: *mut RTPPacketRateCtx,
|
||||
seqnum: c_ushort,
|
||||
ts: c_uint,
|
||||
) -> c_uint;
|
||||
pub fn gst_rtp_packet_rate_ctx_get_max_dropout(
|
||||
pub fn ts_gst_rtp_packet_rate_ctx_get_max_dropout(
|
||||
ctx: *mut RTPPacketRateCtx,
|
||||
time_ms: c_int,
|
||||
) -> c_uint;
|
||||
#[allow(dead_code)]
|
||||
pub fn gst_rtp_packet_rate_ctx_get_max_misorder(
|
||||
pub fn ts_gst_rtp_packet_rate_ctx_get_max_misorder(
|
||||
ctx: *mut RTPPacketRateCtx,
|
||||
time_ms: c_int,
|
||||
) -> c_uint;
|
||||
|
|
|
@ -28,7 +28,7 @@ use gst_rtp::RTPBuffer;
|
|||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use std::cmp::{max, min, Ordering};
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::{BTreeSet, VecDeque};
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
|
@ -381,7 +381,7 @@ impl SinkHandler {
|
|||
let caps = element
|
||||
.emit_by_name::<Option<gst::Caps>>("request-pt-map", &[&(pt as u32)])
|
||||
.ok_or_else(|| {
|
||||
gst::error!(CAT, obj: pad, "Signal 'request-pt-map' retuned None");
|
||||
gst::error!(CAT, obj: pad, "Signal 'request-pt-map' returned None");
|
||||
gst::FlowError::Error
|
||||
})?;
|
||||
let mut state = jb.state.lock().unwrap();
|
||||
|
@ -412,7 +412,7 @@ impl SinkHandler {
|
|||
}
|
||||
|
||||
if let Some(last_in_seqnum) = inner.last_in_seqnum {
|
||||
let gap = gst_rtp::compare_seqnum(last_in_seqnum as u16, seq);
|
||||
let gap = gst_rtp::compare_seqnum(last_in_seqnum, seq);
|
||||
if gap == 1 {
|
||||
self.calculate_packet_spacing(inner, &mut state, rtptime, pts);
|
||||
} else {
|
||||
|
@ -463,7 +463,7 @@ impl SinkHandler {
|
|||
state.equidistant += 1;
|
||||
}
|
||||
|
||||
state.equidistant = min(max(state.equidistant, -7), 7);
|
||||
state.equidistant = state.equidistant.clamp(-7, 7);
|
||||
|
||||
inner.last_rtptime = Some(rtptime);
|
||||
|
||||
|
@ -679,7 +679,7 @@ impl SrcHandler {
|
|||
// FIXME reason why we can expect Some for the 2 lines below
|
||||
let mut last_popped_pts = state.last_popped_pts.unwrap();
|
||||
let interval = pts.into().unwrap().saturating_sub(last_popped_pts);
|
||||
let spacing = interval / (gap as u64 + 1);
|
||||
let spacing = interval / (gap + 1);
|
||||
|
||||
*discont = true;
|
||||
|
||||
|
@ -1259,7 +1259,7 @@ impl JitterBuffer {
|
|||
|
||||
self.task
|
||||
.prepare(
|
||||
JitterBufferTask::new(&*self.obj(), &self.src_pad_handler, &self.sink_pad_handler),
|
||||
JitterBufferTask::new(&self.obj(), &self.src_pad_handler, &self.sink_pad_handler),
|
||||
context,
|
||||
)
|
||||
.block_on()?;
|
||||
|
|
|
@ -32,7 +32,7 @@ glib::wrapper! {
|
|||
pub struct RTPJitterBuffer(Object<ffi::RTPJitterBuffer>);
|
||||
|
||||
match fn {
|
||||
type_ => || ffi::rtp_jitter_buffer_get_type(),
|
||||
type_ => || ffi::ts_rtp_jitter_buffer_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,25 +180,25 @@ impl RTPPacketRateCtx {
|
|||
pub fn new() -> RTPPacketRateCtx {
|
||||
unsafe {
|
||||
let mut ptr = std::mem::MaybeUninit::uninit();
|
||||
ffi::gst_rtp_packet_rate_ctx_reset(ptr.as_mut_ptr(), -1);
|
||||
ffi::ts_gst_rtp_packet_rate_ctx_reset(ptr.as_mut_ptr(), -1);
|
||||
RTPPacketRateCtx(Box::new(ptr.assume_init()))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self, clock_rate: i32) {
|
||||
unsafe { ffi::gst_rtp_packet_rate_ctx_reset(&mut *self.0, clock_rate) }
|
||||
unsafe { ffi::ts_gst_rtp_packet_rate_ctx_reset(&mut *self.0, clock_rate) }
|
||||
}
|
||||
|
||||
pub fn update(&mut self, seqnum: u16, ts: u32) -> u32 {
|
||||
unsafe { ffi::gst_rtp_packet_rate_ctx_update(&mut *self.0, seqnum, ts) }
|
||||
unsafe { ffi::ts_gst_rtp_packet_rate_ctx_update(&mut *self.0, seqnum, ts) }
|
||||
}
|
||||
|
||||
pub fn max_dropout(&mut self, time_ms: i32) -> u32 {
|
||||
unsafe { ffi::gst_rtp_packet_rate_ctx_get_max_dropout(&mut *self.0, time_ms) }
|
||||
unsafe { ffi::ts_gst_rtp_packet_rate_ctx_get_max_dropout(&mut *self.0, time_ms) }
|
||||
}
|
||||
|
||||
pub fn max_misorder(&mut self, time_ms: i32) -> u32 {
|
||||
unsafe { ffi::gst_rtp_packet_rate_ctx_get_max_misorder(&mut *self.0, time_ms) }
|
||||
unsafe { ffi::ts_gst_rtp_packet_rate_ctx_get_max_misorder(&mut *self.0, time_ms) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,38 +219,38 @@ pub enum RTPJitterBufferMode {
|
|||
|
||||
impl RTPJitterBuffer {
|
||||
pub fn new() -> RTPJitterBuffer {
|
||||
unsafe { from_glib_full(ffi::rtp_jitter_buffer_new()) }
|
||||
unsafe { from_glib_full(ffi::ts_rtp_jitter_buffer_new()) }
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn mode(&self) -> RTPJitterBufferMode {
|
||||
unsafe { from_glib(ffi::rtp_jitter_buffer_get_mode(self.to_glib_none().0)) }
|
||||
unsafe { from_glib(ffi::ts_rtp_jitter_buffer_get_mode(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn set_mode(&self, mode: RTPJitterBufferMode) {
|
||||
unsafe { ffi::rtp_jitter_buffer_set_mode(self.to_glib_none().0, mode.into_glib()) }
|
||||
unsafe { ffi::ts_rtp_jitter_buffer_set_mode(self.to_glib_none().0, mode.into_glib()) }
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn delay(&self) -> gst::ClockTime {
|
||||
unsafe {
|
||||
try_from_glib(ffi::rtp_jitter_buffer_get_delay(self.to_glib_none().0))
|
||||
try_from_glib(ffi::ts_rtp_jitter_buffer_get_delay(self.to_glib_none().0))
|
||||
.expect("undefined delay")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_delay(&self, delay: gst::ClockTime) {
|
||||
unsafe { ffi::rtp_jitter_buffer_set_delay(self.to_glib_none().0, delay.into_glib()) }
|
||||
unsafe { ffi::ts_rtp_jitter_buffer_set_delay(self.to_glib_none().0, delay.into_glib()) }
|
||||
}
|
||||
|
||||
pub fn set_clock_rate(&self, clock_rate: u32) {
|
||||
unsafe { ffi::rtp_jitter_buffer_set_clock_rate(self.to_glib_none().0, clock_rate) }
|
||||
unsafe { ffi::ts_rtp_jitter_buffer_set_clock_rate(self.to_glib_none().0, clock_rate) }
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn clock_rate(&self) -> u32 {
|
||||
unsafe { ffi::rtp_jitter_buffer_get_clock_rate(self.to_glib_none().0) }
|
||||
unsafe { ffi::ts_rtp_jitter_buffer_get_clock_rate(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
pub fn calculate_pts(
|
||||
|
@ -263,7 +263,7 @@ impl RTPJitterBuffer {
|
|||
is_rtx: bool,
|
||||
) -> Option<gst::ClockTime> {
|
||||
unsafe {
|
||||
from_glib(ffi::rtp_jitter_buffer_calculate_pts(
|
||||
from_glib(ffi::ts_rtp_jitter_buffer_calculate_pts(
|
||||
self.to_glib_none().0,
|
||||
dts.into().into_glib(),
|
||||
estimated_dts.into_glib(),
|
||||
|
@ -280,7 +280,7 @@ impl RTPJitterBuffer {
|
|||
let mut head = mem::MaybeUninit::uninit();
|
||||
let mut percent = mem::MaybeUninit::uninit();
|
||||
let ptr = item.0.take().expect("Invalid wrapper");
|
||||
let ret: bool = from_glib(ffi::rtp_jitter_buffer_insert(
|
||||
let ret: bool = from_glib(ffi::ts_rtp_jitter_buffer_insert(
|
||||
self.to_glib_none().0,
|
||||
ptr.as_ptr(),
|
||||
head.as_mut_ptr(),
|
||||
|
@ -298,7 +298,7 @@ impl RTPJitterBuffer {
|
|||
let mut pts = mem::MaybeUninit::uninit();
|
||||
let mut seqnum = mem::MaybeUninit::uninit();
|
||||
|
||||
ffi::rtp_jitter_buffer_find_earliest(
|
||||
ffi::ts_rtp_jitter_buffer_find_earliest(
|
||||
self.to_glib_none().0,
|
||||
pts.as_mut_ptr(),
|
||||
seqnum.as_mut_ptr(),
|
||||
|
@ -319,7 +319,7 @@ impl RTPJitterBuffer {
|
|||
pub fn pop(&self) -> (Option<RTPJitterBufferItem>, i32) {
|
||||
unsafe {
|
||||
let mut percent = mem::MaybeUninit::uninit();
|
||||
let item = ffi::rtp_jitter_buffer_pop(self.to_glib_none().0, percent.as_mut_ptr());
|
||||
let item = ffi::ts_rtp_jitter_buffer_pop(self.to_glib_none().0, percent.as_mut_ptr());
|
||||
|
||||
(
|
||||
if item.is_null() {
|
||||
|
@ -334,7 +334,7 @@ impl RTPJitterBuffer {
|
|||
|
||||
pub fn peek(&self) -> (Option<gst::ClockTime>, Option<u16>) {
|
||||
unsafe {
|
||||
let item = ffi::rtp_jitter_buffer_peek(self.to_glib_none().0);
|
||||
let item = ffi::ts_rtp_jitter_buffer_peek(self.to_glib_none().0);
|
||||
if item.is_null() {
|
||||
(None, None)
|
||||
} else {
|
||||
|
@ -356,12 +356,12 @@ impl RTPJitterBuffer {
|
|||
}
|
||||
|
||||
unsafe {
|
||||
ffi::rtp_jitter_buffer_flush(self.to_glib_none().0, Some(free_item));
|
||||
ffi::ts_rtp_jitter_buffer_flush(self.to_glib_none().0, Some(free_item));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset_skew(&self) {
|
||||
unsafe { ffi::rtp_jitter_buffer_reset_skew(self.to_glib_none().0) }
|
||||
unsafe { ffi::ts_rtp_jitter_buffer_reset_skew(self.to_glib_none().0) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,10 +42,10 @@ enum
|
|||
};
|
||||
|
||||
/* GObject vmethods */
|
||||
static void rtp_jitter_buffer_finalize (GObject * object);
|
||||
static void ts_rtp_jitter_buffer_finalize (GObject * object);
|
||||
|
||||
GType
|
||||
rtp_jitter_buffer_mode_get_type (void)
|
||||
ts_rtp_jitter_buffer_mode_get_type (void)
|
||||
{
|
||||
static GType jitter_buffer_mode_type = 0;
|
||||
static const GEnumValue jitter_buffer_modes[] = {
|
||||
|
@ -67,36 +67,36 @@ rtp_jitter_buffer_mode_get_type (void)
|
|||
|
||||
/* static guint rtp_jitter_buffer_signals[LAST_SIGNAL] = { 0 }; */
|
||||
|
||||
G_DEFINE_TYPE (RTPJitterBuffer, rtp_jitter_buffer, G_TYPE_OBJECT);
|
||||
G_DEFINE_TYPE (TsRTPJitterBuffer, ts_rtp_jitter_buffer, G_TYPE_OBJECT);
|
||||
|
||||
static void
|
||||
rtp_jitter_buffer_class_init (RTPJitterBufferClass * klass)
|
||||
ts_rtp_jitter_buffer_class_init (TsRTPJitterBufferClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
|
||||
gobject_class = (GObjectClass *) klass;
|
||||
|
||||
gobject_class->finalize = rtp_jitter_buffer_finalize;
|
||||
gobject_class->finalize = ts_rtp_jitter_buffer_finalize;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (rtp_jitter_buffer_debug, "rtpjitterbuffer", 0,
|
||||
"RTP Jitter Buffer");
|
||||
}
|
||||
|
||||
static void
|
||||
rtp_jitter_buffer_init (RTPJitterBuffer * jbuf)
|
||||
ts_rtp_jitter_buffer_init (TsRTPJitterBuffer * jbuf)
|
||||
{
|
||||
g_mutex_init (&jbuf->clock_lock);
|
||||
|
||||
jbuf->packets = g_queue_new ();
|
||||
jbuf->mode = RTP_JITTER_BUFFER_MODE_SLAVE;
|
||||
|
||||
rtp_jitter_buffer_reset_skew (jbuf);
|
||||
ts_rtp_jitter_buffer_reset_skew (jbuf);
|
||||
}
|
||||
|
||||
static void
|
||||
rtp_jitter_buffer_finalize (GObject * object)
|
||||
ts_rtp_jitter_buffer_finalize (GObject * object)
|
||||
{
|
||||
RTPJitterBuffer *jbuf;
|
||||
TsRTPJitterBuffer *jbuf;
|
||||
RTPJitterBufferItem *item;
|
||||
|
||||
jbuf = RTP_JITTER_BUFFER_CAST (object);
|
||||
|
@ -127,7 +127,7 @@ rtp_jitter_buffer_finalize (GObject * object)
|
|||
|
||||
g_mutex_clear (&jbuf->clock_lock);
|
||||
|
||||
G_OBJECT_CLASS (rtp_jitter_buffer_parent_class)->finalize (object);
|
||||
G_OBJECT_CLASS (ts_rtp_jitter_buffer_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -137,10 +137,10 @@ rtp_jitter_buffer_finalize (GObject * object)
|
|||
*
|
||||
* Returns: a new #RTPJitterBuffer. Use g_object_unref() after usage.
|
||||
*/
|
||||
RTPJitterBuffer *
|
||||
rtp_jitter_buffer_new (void)
|
||||
TsRTPJitterBuffer *
|
||||
ts_rtp_jitter_buffer_new (void)
|
||||
{
|
||||
RTPJitterBuffer *jbuf;
|
||||
TsRTPJitterBuffer *jbuf;
|
||||
|
||||
jbuf = g_object_new (RTP_TYPE_JITTER_BUFFER, NULL);
|
||||
|
||||
|
@ -156,7 +156,7 @@ rtp_jitter_buffer_new (void)
|
|||
* Returns: the current jitterbuffer mode.
|
||||
*/
|
||||
RTPJitterBufferMode
|
||||
rtp_jitter_buffer_get_mode (RTPJitterBuffer * jbuf)
|
||||
ts_rtp_jitter_buffer_get_mode (TsRTPJitterBuffer * jbuf)
|
||||
{
|
||||
return jbuf->mode;
|
||||
}
|
||||
|
@ -169,20 +169,20 @@ rtp_jitter_buffer_get_mode (RTPJitterBuffer * jbuf)
|
|||
* Set the buffering and clock slaving algorithm used in the @jbuf.
|
||||
*/
|
||||
void
|
||||
rtp_jitter_buffer_set_mode (RTPJitterBuffer * jbuf, RTPJitterBufferMode mode)
|
||||
ts_rtp_jitter_buffer_set_mode (TsRTPJitterBuffer * jbuf, RTPJitterBufferMode mode)
|
||||
{
|
||||
jbuf->mode = mode;
|
||||
}
|
||||
|
||||
GstClockTime
|
||||
rtp_jitter_buffer_get_delay (RTPJitterBuffer * jbuf)
|
||||
ts_rtp_jitter_buffer_get_delay (RTPJitterBuffer * jbuf)
|
||||
{
|
||||
g_print ("%p getting delay (%" G_GUINT64_FORMAT ")\n", jbuf, jbuf->delay);
|
||||
return jbuf->delay;
|
||||
}
|
||||
|
||||
void
|
||||
rtp_jitter_buffer_set_delay (RTPJitterBuffer * jbuf, GstClockTime delay)
|
||||
ts_rtp_jitter_buffer_set_delay (TsRTPJitterBuffer * jbuf, GstClockTime delay)
|
||||
{
|
||||
jbuf->delay = delay;
|
||||
jbuf->low_level = (delay * 15) / 100;
|
||||
|
@ -203,13 +203,13 @@ rtp_jitter_buffer_set_delay (RTPJitterBuffer * jbuf, GstClockTime delay)
|
|||
* Set the clock rate in the jitterbuffer.
|
||||
*/
|
||||
void
|
||||
rtp_jitter_buffer_set_clock_rate (RTPJitterBuffer * jbuf, guint32 clock_rate)
|
||||
ts_rtp_jitter_buffer_set_clock_rate (TsRTPJitterBuffer * jbuf, guint32 clock_rate)
|
||||
{
|
||||
if (jbuf->clock_rate != clock_rate) {
|
||||
GST_DEBUG ("Clock rate changed from %" G_GUINT32_FORMAT " to %"
|
||||
G_GUINT32_FORMAT, jbuf->clock_rate, clock_rate);
|
||||
jbuf->clock_rate = clock_rate;
|
||||
rtp_jitter_buffer_reset_skew (jbuf);
|
||||
ts_rtp_jitter_buffer_reset_skew (jbuf);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,7 +222,7 @@ rtp_jitter_buffer_set_clock_rate (RTPJitterBuffer * jbuf, guint32 clock_rate)
|
|||
* Returns: the current clock-rate
|
||||
*/
|
||||
guint32
|
||||
rtp_jitter_buffer_get_clock_rate (RTPJitterBuffer * jbuf)
|
||||
ts_rtp_jitter_buffer_get_clock_rate (RTPJitterBuffer * jbuf)
|
||||
{
|
||||
return jbuf->clock_rate;
|
||||
}
|
||||
|
@ -253,7 +253,7 @@ media_clock_synced_cb (GstClock * clock G_GNUC_UNUSED,
|
|||
*
|
||||
*/
|
||||
void
|
||||
rtp_jitter_buffer_set_media_clock (RTPJitterBuffer * jbuf, GstClock * clock,
|
||||
ts_rtp_jitter_buffer_set_media_clock (TsRTPJitterBuffer * jbuf, GstClock * clock,
|
||||
guint64 clock_offset)
|
||||
{
|
||||
g_mutex_lock (&jbuf->clock_lock);
|
||||
|
@ -295,7 +295,7 @@ rtp_jitter_buffer_set_media_clock (RTPJitterBuffer * jbuf, GstClock * clock,
|
|||
*
|
||||
*/
|
||||
void
|
||||
rtp_jitter_buffer_set_pipeline_clock (RTPJitterBuffer * jbuf, GstClock * clock)
|
||||
ts_rtp_jitter_buffer_set_pipeline_clock (TsRTPJitterBuffer * jbuf, GstClock * clock)
|
||||
{
|
||||
g_mutex_lock (&jbuf->clock_lock);
|
||||
if (jbuf->pipeline_clock)
|
||||
|
@ -319,13 +319,13 @@ rtp_jitter_buffer_set_pipeline_clock (RTPJitterBuffer * jbuf, GstClock * clock)
|
|||
}
|
||||
|
||||
gboolean
|
||||
rtp_jitter_buffer_get_rfc7273_sync (RTPJitterBuffer * jbuf)
|
||||
ts_rtp_jitter_buffer_get_rfc7273_sync (RTPJitterBuffer * jbuf)
|
||||
{
|
||||
return jbuf->rfc7273_sync;
|
||||
}
|
||||
|
||||
void
|
||||
rtp_jitter_buffer_set_rfc7273_sync (RTPJitterBuffer * jbuf,
|
||||
ts_rtp_jitter_buffer_set_rfc7273_sync (TsRTPJitterBuffer * jbuf,
|
||||
gboolean rfc7273_sync)
|
||||
{
|
||||
jbuf->rfc7273_sync = rfc7273_sync;
|
||||
|
@ -338,7 +338,7 @@ rtp_jitter_buffer_set_rfc7273_sync (RTPJitterBuffer * jbuf,
|
|||
* Reset the skew calculations in @jbuf.
|
||||
*/
|
||||
void
|
||||
rtp_jitter_buffer_reset_skew (RTPJitterBuffer * jbuf)
|
||||
ts_rtp_jitter_buffer_reset_skew (TsRTPJitterBuffer * jbuf)
|
||||
{
|
||||
jbuf->base_time = -1;
|
||||
jbuf->base_rtptime = -1;
|
||||
|
@ -365,13 +365,13 @@ rtp_jitter_buffer_reset_skew (RTPJitterBuffer * jbuf)
|
|||
* Enable or disable buffering on @jbuf.
|
||||
*/
|
||||
void
|
||||
rtp_jitter_buffer_disable_buffering (RTPJitterBuffer * jbuf, gboolean disabled)
|
||||
ts_rtp_jitter_buffer_disable_buffering (TsRTPJitterBuffer * jbuf, gboolean disabled)
|
||||
{
|
||||
jbuf->buffering_disabled = disabled;
|
||||
}
|
||||
|
||||
static void
|
||||
rtp_jitter_buffer_resync (RTPJitterBuffer * jbuf, GstClockTime time,
|
||||
ts_rtp_jitter_buffer_resync (TsRTPJitterBuffer * jbuf, GstClockTime time,
|
||||
GstClockTime gstrtptime, guint64 ext_rtptime, gboolean reset_skew)
|
||||
{
|
||||
jbuf->base_time = time;
|
||||
|
@ -438,7 +438,7 @@ get_buffer_level (RTPJitterBuffer * jbuf)
|
|||
}
|
||||
|
||||
static void
|
||||
update_buffer_level (RTPJitterBuffer * jbuf, gint * percent)
|
||||
update_buffer_level (TsRTPJitterBuffer * jbuf, gint * percent)
|
||||
{
|
||||
gboolean post = FALSE;
|
||||
guint64 level;
|
||||
|
@ -587,7 +587,7 @@ calculate_skew (RTPJitterBuffer * jbuf, guint64 ext_rtptime,
|
|||
if (ABS (delta - jbuf->skew) > GST_SECOND) {
|
||||
GST_WARNING ("delta - skew: %" GST_TIME_FORMAT " too big, reset skew",
|
||||
GST_TIME_ARGS (ABS (delta - jbuf->skew)));
|
||||
rtp_jitter_buffer_resync (jbuf, time, gstrtptime, ext_rtptime, TRUE);
|
||||
ts_rtp_jitter_buffer_resync (jbuf, time, gstrtptime, ext_rtptime, TRUE);
|
||||
send_diff = 0;
|
||||
delta = 0;
|
||||
gap = 0;
|
||||
|
@ -692,7 +692,7 @@ no_skew:
|
|||
}
|
||||
|
||||
static void
|
||||
queue_do_insert (RTPJitterBuffer * jbuf, GList * list, GList * item)
|
||||
queue_do_insert (TsRTPJitterBuffer * jbuf, GList * list, GList * item)
|
||||
{
|
||||
GQueue *queue = jbuf->packets;
|
||||
|
||||
|
@ -714,7 +714,7 @@ queue_do_insert (RTPJitterBuffer * jbuf, GList * list, GList * item)
|
|||
}
|
||||
|
||||
GstClockTime
|
||||
rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts,
|
||||
ts_rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts,
|
||||
gboolean estimated_dts, guint32 rtptime, GstClockTime base_time,
|
||||
gint gap, gboolean is_rtx)
|
||||
{
|
||||
|
@ -742,7 +742,7 @@ rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts,
|
|||
/* reset even if we don't have valid incoming time;
|
||||
* still better than producing possibly very bogus output timestamp */
|
||||
GST_WARNING ("rtp delta too big, reset skew");
|
||||
rtp_jitter_buffer_reset_skew (jbuf);
|
||||
ts_rtp_jitter_buffer_reset_skew (jbuf);
|
||||
} else {
|
||||
GST_WARNING ("rtp delta too big: ignore rtx packet");
|
||||
media_clock = NULL;
|
||||
|
@ -823,7 +823,7 @@ rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts,
|
|||
}
|
||||
GST_INFO ("resync to time %" GST_TIME_FORMAT ", rtptime %"
|
||||
GST_TIME_FORMAT, GST_TIME_ARGS (dts), GST_TIME_ARGS (gstrtptime));
|
||||
rtp_jitter_buffer_resync (jbuf, dts, gstrtptime, ext_rtptime, FALSE);
|
||||
ts_rtp_jitter_buffer_resync (jbuf, dts, gstrtptime, ext_rtptime, FALSE);
|
||||
}
|
||||
|
||||
GST_DEBUG ("extrtp %" G_GUINT64_FORMAT ", gstrtp %" GST_TIME_FORMAT ", base %"
|
||||
|
@ -973,7 +973,7 @@ rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts,
|
|||
GST_DEBUG ("out %" GST_TIME_FORMAT " + %" G_GUINT64_FORMAT " < time %"
|
||||
GST_TIME_FORMAT ", reset jitterbuffer and discard", GST_TIME_ARGS (pts),
|
||||
jbuf->delay, GST_TIME_ARGS (dts));
|
||||
rtp_jitter_buffer_reset_skew (jbuf);
|
||||
ts_rtp_jitter_buffer_reset_skew (jbuf);
|
||||
pts = GST_CLOCK_TIME_NONE;
|
||||
goto done;
|
||||
}
|
||||
|
@ -1009,7 +1009,7 @@ done:
|
|||
* Returns: %FALSE if a packet with the same number already existed.
|
||||
*/
|
||||
gboolean
|
||||
rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, RTPJitterBufferItem * item,
|
||||
ts_rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, RTPJitterBufferItem * item,
|
||||
gboolean * head, gint * percent)
|
||||
{
|
||||
GList *list, *event = NULL;
|
||||
|
@ -1102,7 +1102,7 @@ duplicate:
|
|||
* Returns: a #GstBuffer or %NULL when there was no packet in the queue.
|
||||
*/
|
||||
RTPJitterBufferItem *
|
||||
rtp_jitter_buffer_pop (RTPJitterBuffer * jbuf, gint * percent)
|
||||
ts_rtp_jitter_buffer_pop (TsRTPJitterBuffer * jbuf, gint * percent)
|
||||
{
|
||||
GList *item = NULL;
|
||||
GQueue *queue;
|
||||
|
@ -1142,7 +1142,7 @@ rtp_jitter_buffer_pop (RTPJitterBuffer * jbuf, gint * percent)
|
|||
* Returns: a #GstBuffer or %NULL when there was no packet in the queue.
|
||||
*/
|
||||
RTPJitterBufferItem *
|
||||
rtp_jitter_buffer_peek (RTPJitterBuffer * jbuf)
|
||||
ts_rtp_jitter_buffer_peek (TsRTPJitterBuffer * jbuf)
|
||||
{
|
||||
g_return_val_if_fail (jbuf != NULL, NULL);
|
||||
|
||||
|
@ -1158,7 +1158,7 @@ rtp_jitter_buffer_peek (RTPJitterBuffer * jbuf)
|
|||
* Flush all packets from the jitterbuffer.
|
||||
*/
|
||||
void
|
||||
rtp_jitter_buffer_flush (RTPJitterBuffer * jbuf, GFunc free_func,
|
||||
ts_rtp_jitter_buffer_flush (TsRTPJitterBuffer * jbuf, GFunc free_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
GList *item;
|
||||
|
@ -1180,7 +1180,7 @@ rtp_jitter_buffer_flush (RTPJitterBuffer * jbuf, GFunc free_func,
|
|||
* Returns: the buffering state of @jbuf
|
||||
*/
|
||||
gboolean
|
||||
rtp_jitter_buffer_is_buffering (RTPJitterBuffer * jbuf)
|
||||
ts_rtp_jitter_buffer_is_buffering (RTPJitterBuffer * jbuf)
|
||||
{
|
||||
return jbuf->buffering && !jbuf->buffering_disabled;
|
||||
}
|
||||
|
@ -1193,7 +1193,7 @@ rtp_jitter_buffer_is_buffering (RTPJitterBuffer * jbuf)
|
|||
* Forces @jbuf to go into the buffering state.
|
||||
*/
|
||||
void
|
||||
rtp_jitter_buffer_set_buffering (RTPJitterBuffer * jbuf, gboolean buffering)
|
||||
ts_rtp_jitter_buffer_set_buffering (TsRTPJitterBuffer * jbuf, gboolean buffering)
|
||||
{
|
||||
jbuf->buffering = buffering;
|
||||
}
|
||||
|
@ -1207,7 +1207,7 @@ rtp_jitter_buffer_set_buffering (RTPJitterBuffer * jbuf, gboolean buffering)
|
|||
* Returns: the buffering percent
|
||||
*/
|
||||
gint
|
||||
rtp_jitter_buffer_get_percent (RTPJitterBuffer * jbuf)
|
||||
ts_rtp_jitter_buffer_get_percent (RTPJitterBuffer * jbuf)
|
||||
{
|
||||
gint percent;
|
||||
guint64 level;
|
||||
|
@ -1234,7 +1234,7 @@ rtp_jitter_buffer_get_percent (RTPJitterBuffer * jbuf)
|
|||
* Returns: The number of packets in @jbuf.
|
||||
*/
|
||||
guint
|
||||
rtp_jitter_buffer_num_packets (RTPJitterBuffer * jbuf)
|
||||
ts_rtp_jitter_buffer_num_packets (RTPJitterBuffer * jbuf)
|
||||
{
|
||||
g_return_val_if_fail (jbuf != NULL, 0);
|
||||
|
||||
|
@ -1251,7 +1251,7 @@ rtp_jitter_buffer_num_packets (RTPJitterBuffer * jbuf)
|
|||
* Returns: The difference expressed in the timestamp units of the packets.
|
||||
*/
|
||||
guint32
|
||||
rtp_jitter_buffer_get_ts_diff (RTPJitterBuffer * jbuf)
|
||||
ts_rtp_jitter_buffer_get_ts_diff (RTPJitterBuffer * jbuf)
|
||||
{
|
||||
guint64 high_ts, low_ts;
|
||||
RTPJitterBufferItem *high_buf, *low_buf;
|
||||
|
@ -1288,7 +1288,7 @@ rtp_jitter_buffer_get_ts_diff (RTPJitterBuffer * jbuf)
|
|||
* Returns: The difference expressed in seqnum.
|
||||
*/
|
||||
static guint16
|
||||
rtp_jitter_buffer_get_seqnum_diff (RTPJitterBuffer * jbuf)
|
||||
ts_rtp_jitter_buffer_get_seqnum_diff (RTPJitterBuffer * jbuf)
|
||||
{
|
||||
guint32 high_seqnum, low_seqnum;
|
||||
RTPJitterBufferItem *high_buf, *low_buf;
|
||||
|
@ -1338,7 +1338,7 @@ rtp_jitter_buffer_get_seqnum_diff (RTPJitterBuffer * jbuf)
|
|||
* @last_rtptime.
|
||||
*/
|
||||
void
|
||||
rtp_jitter_buffer_get_sync (RTPJitterBuffer * jbuf, guint64 * rtptime,
|
||||
ts_rtp_jitter_buffer_get_sync (TsRTPJitterBuffer * jbuf, guint64 * rtptime,
|
||||
guint64 * timestamp, guint32 * clock_rate, guint64 * last_rtptime)
|
||||
{
|
||||
if (rtptime)
|
||||
|
@ -1362,16 +1362,16 @@ rtp_jitter_buffer_get_sync (RTPJitterBuffer * jbuf, guint64 * rtptime,
|
|||
* Returns: %TRUE if the required number of consecutive packets was found.
|
||||
*/
|
||||
gboolean
|
||||
rtp_jitter_buffer_can_fast_start (RTPJitterBuffer * jbuf, gint num_packet)
|
||||
ts_rtp_jitter_buffer_can_fast_start (RTPJitterBuffer * jbuf, gint num_packet)
|
||||
{
|
||||
gboolean ret = TRUE;
|
||||
RTPJitterBufferItem *last_item = NULL, *item;
|
||||
gint i;
|
||||
|
||||
if (rtp_jitter_buffer_num_packets (jbuf) < (guint) num_packet)
|
||||
if (ts_rtp_jitter_buffer_num_packets (jbuf) < (guint) num_packet)
|
||||
return FALSE;
|
||||
|
||||
item = rtp_jitter_buffer_peek (jbuf);
|
||||
item = ts_rtp_jitter_buffer_peek (jbuf);
|
||||
for (i = 0; i < num_packet; i++) {
|
||||
if (G_LIKELY (last_item)) {
|
||||
guint16 expected_seqnum = last_item->seqnum + 1;
|
||||
|
@ -1390,14 +1390,14 @@ rtp_jitter_buffer_can_fast_start (RTPJitterBuffer * jbuf, gint num_packet)
|
|||
}
|
||||
|
||||
gboolean
|
||||
rtp_jitter_buffer_is_full (RTPJitterBuffer * jbuf)
|
||||
ts_rtp_jitter_buffer_is_full (RTPJitterBuffer * jbuf)
|
||||
{
|
||||
return rtp_jitter_buffer_get_seqnum_diff (jbuf) >= 32765 &&
|
||||
rtp_jitter_buffer_num_packets (jbuf) > 10000;
|
||||
return ts_rtp_jitter_buffer_get_seqnum_diff (jbuf) >= 32765 &&
|
||||
ts_rtp_jitter_buffer_num_packets (jbuf) > 10000;
|
||||
}
|
||||
|
||||
void
|
||||
rtp_jitter_buffer_find_earliest (RTPJitterBuffer * jbuf, GstClockTime * pts,
|
||||
ts_rtp_jitter_buffer_find_earliest (TsRTPJitterBuffer * jbuf, GstClockTime * pts,
|
||||
guint * seqnum)
|
||||
{
|
||||
GList *tmp;
|
||||
|
|
|
@ -23,16 +23,16 @@
|
|||
#include <gst/gst.h>
|
||||
#include <gst/rtp/gstrtcpbuffer.h>
|
||||
|
||||
typedef struct _RTPJitterBuffer RTPJitterBuffer;
|
||||
typedef struct _RTPJitterBufferClass RTPJitterBufferClass;
|
||||
typedef struct _RTPJitterBuffer TsRTPJitterBuffer;
|
||||
typedef struct _RTPJitterBufferClass TsRTPJitterBufferClass;
|
||||
typedef struct _RTPJitterBufferItem RTPJitterBufferItem;
|
||||
|
||||
#define RTP_TYPE_JITTER_BUFFER (rtp_jitter_buffer_get_type())
|
||||
#define RTP_JITTER_BUFFER(src) (G_TYPE_CHECK_INSTANCE_CAST((src),RTP_TYPE_JITTER_BUFFER,RTPJitterBuffer))
|
||||
#define RTP_JITTER_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),RTP_TYPE_JITTER_BUFFER,RTPJitterBufferClass))
|
||||
#define RTP_TYPE_JITTER_BUFFER (ts_rtp_jitter_buffer_get_type())
|
||||
#define RTP_JITTER_BUFFER(src) (G_TYPE_CHECK_INSTANCE_CAST((src),RTP_TYPE_JITTER_BUFFER,TsRTPJitterBuffer))
|
||||
#define RTP_JITTER_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),RTP_TYPE_JITTER_BUFFER,TsRTPJitterBufferClass))
|
||||
#define RTP_IS_JITTER_BUFFER(src) (G_TYPE_CHECK_INSTANCE_TYPE((src),RTP_TYPE_JITTER_BUFFER))
|
||||
#define RTP_IS_JITTER_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),RTP_TYPE_JITTER_BUFFER))
|
||||
#define RTP_JITTER_BUFFER_CAST(src) ((RTPJitterBuffer *)(src))
|
||||
#define RTP_JITTER_BUFFER_CAST(src) ((TsRTPJitterBuffer *)(src))
|
||||
|
||||
/**
|
||||
* RTPJitterBufferMode:
|
||||
|
@ -61,8 +61,8 @@ typedef enum {
|
|||
RTP_JITTER_BUFFER_MODE_LAST
|
||||
} RTPJitterBufferMode;
|
||||
|
||||
#define RTP_TYPE_JITTER_BUFFER_MODE (rtp_jitter_buffer_mode_get_type())
|
||||
GType rtp_jitter_buffer_mode_get_type (void);
|
||||
#define RTP_TYPE_JITTER_BUFFER_MODE (ts_rtp_jitter_buffer_mode_get_type())
|
||||
GType ts_rtp_jitter_buffer_mode_get_type (void);
|
||||
|
||||
#define RTP_JITTER_BUFFER_MAX_WINDOW 512
|
||||
/**
|
||||
|
@ -144,58 +144,58 @@ struct _RTPJitterBufferItem {
|
|||
guint rtptime;
|
||||
};
|
||||
|
||||
GType rtp_jitter_buffer_get_type (void);
|
||||
GType ts_rtp_jitter_buffer_get_type (void);
|
||||
|
||||
/* managing lifetime */
|
||||
RTPJitterBuffer* rtp_jitter_buffer_new (void);
|
||||
TsRTPJitterBuffer* ts_rtp_jitter_buffer_new (void);
|
||||
|
||||
RTPJitterBufferMode rtp_jitter_buffer_get_mode (RTPJitterBuffer *jbuf);
|
||||
void rtp_jitter_buffer_set_mode (RTPJitterBuffer *jbuf, RTPJitterBufferMode mode);
|
||||
RTPJitterBufferMode ts_rtp_jitter_buffer_get_mode (TsRTPJitterBuffer *jbuf);
|
||||
void ts_rtp_jitter_buffer_set_mode (TsRTPJitterBuffer *jbuf, RTPJitterBufferMode mode);
|
||||
|
||||
GstClockTime rtp_jitter_buffer_get_delay (RTPJitterBuffer *jbuf);
|
||||
void rtp_jitter_buffer_set_delay (RTPJitterBuffer *jbuf, GstClockTime delay);
|
||||
GstClockTime ts_rtp_jitter_buffer_get_delay (RTPJitterBuffer *jbuf);
|
||||
void ts_rtp_jitter_buffer_set_delay (TsRTPJitterBuffer *jbuf, GstClockTime delay);
|
||||
|
||||
void rtp_jitter_buffer_set_clock_rate (RTPJitterBuffer *jbuf, guint32 clock_rate);
|
||||
guint32 rtp_jitter_buffer_get_clock_rate (RTPJitterBuffer *jbuf);
|
||||
void ts_rtp_jitter_buffer_set_clock_rate (TsRTPJitterBuffer *jbuf, guint32 clock_rate);
|
||||
guint32 ts_rtp_jitter_buffer_get_clock_rate (RTPJitterBuffer *jbuf);
|
||||
|
||||
void rtp_jitter_buffer_set_media_clock (RTPJitterBuffer *jbuf, GstClock * clock, guint64 clock_offset);
|
||||
void rtp_jitter_buffer_set_pipeline_clock (RTPJitterBuffer *jbuf, GstClock * clock);
|
||||
void ts_rtp_jitter_buffer_set_media_clock (TsRTPJitterBuffer *jbuf, GstClock * clock, guint64 clock_offset);
|
||||
void ts_rtp_jitter_buffer_set_pipeline_clock (TsRTPJitterBuffer *jbuf, GstClock * clock);
|
||||
|
||||
gboolean rtp_jitter_buffer_get_rfc7273_sync (RTPJitterBuffer *jbuf);
|
||||
void rtp_jitter_buffer_set_rfc7273_sync (RTPJitterBuffer *jbuf, gboolean rfc7273_sync);
|
||||
gboolean ts_rtp_jitter_buffer_get_rfc7273_sync (RTPJitterBuffer *jbuf);
|
||||
void ts_rtp_jitter_buffer_set_rfc7273_sync (TsRTPJitterBuffer *jbuf, gboolean rfc7273_sync);
|
||||
|
||||
void rtp_jitter_buffer_reset_skew (RTPJitterBuffer *jbuf);
|
||||
void ts_rtp_jitter_buffer_reset_skew (TsRTPJitterBuffer *jbuf);
|
||||
|
||||
gboolean rtp_jitter_buffer_insert (RTPJitterBuffer *jbuf,
|
||||
RTPJitterBufferItem *item,
|
||||
gboolean *head, gint *percent);
|
||||
gboolean ts_rtp_jitter_buffer_insert (RTPJitterBuffer *jbuf,
|
||||
RTPJitterBufferItem *item,
|
||||
gboolean *head, gint *percent);
|
||||
|
||||
void rtp_jitter_buffer_disable_buffering (RTPJitterBuffer *jbuf, gboolean disabled);
|
||||
void ts_rtp_jitter_buffer_disable_buffering (TsRTPJitterBuffer *jbuf, gboolean disabled);
|
||||
|
||||
RTPJitterBufferItem * rtp_jitter_buffer_peek (RTPJitterBuffer *jbuf);
|
||||
RTPJitterBufferItem * rtp_jitter_buffer_pop (RTPJitterBuffer *jbuf, gint *percent);
|
||||
RTPJitterBufferItem * ts_rtp_jitter_buffer_peek (TsRTPJitterBuffer *jbuf);
|
||||
RTPJitterBufferItem * ts_rtp_jitter_buffer_pop (TsRTPJitterBuffer *jbuf, gint *percent);
|
||||
|
||||
void rtp_jitter_buffer_flush (RTPJitterBuffer *jbuf,
|
||||
GFunc free_func, gpointer user_data);
|
||||
void ts_rtp_jitter_buffer_flush (TsRTPJitterBuffer *jbuf,
|
||||
GFunc free_func, gpointer user_data);
|
||||
|
||||
gboolean rtp_jitter_buffer_is_buffering (RTPJitterBuffer * jbuf);
|
||||
void rtp_jitter_buffer_set_buffering (RTPJitterBuffer * jbuf, gboolean buffering);
|
||||
gint rtp_jitter_buffer_get_percent (RTPJitterBuffer * jbuf);
|
||||
gboolean ts_rtp_jitter_buffer_is_buffering (RTPJitterBuffer * jbuf);
|
||||
void ts_rtp_jitter_buffer_set_buffering (TsRTPJitterBuffer * jbuf, gboolean buffering);
|
||||
gint ts_rtp_jitter_buffer_get_percent (RTPJitterBuffer * jbuf);
|
||||
|
||||
guint rtp_jitter_buffer_num_packets (RTPJitterBuffer *jbuf);
|
||||
guint32 rtp_jitter_buffer_get_ts_diff (RTPJitterBuffer *jbuf);
|
||||
guint ts_rtp_jitter_buffer_num_packets (RTPJitterBuffer *jbuf);
|
||||
guint32 ts_rtp_jitter_buffer_get_ts_diff (RTPJitterBuffer *jbuf);
|
||||
|
||||
void rtp_jitter_buffer_get_sync (RTPJitterBuffer *jbuf, guint64 *rtptime,
|
||||
guint64 *timestamp, guint32 *clock_rate,
|
||||
guint64 *last_rtptime);
|
||||
void ts_rtp_jitter_buffer_get_sync (TsRTPJitterBuffer *jbuf, guint64 *rtptime,
|
||||
guint64 *timestamp, guint32 *clock_rate,
|
||||
guint64 *last_rtptime);
|
||||
|
||||
GstClockTime rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts, gboolean estimated_dts,
|
||||
guint32 rtptime, GstClockTime base_time, gint gap,
|
||||
gboolean is_rtx);
|
||||
GstClockTime ts_rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts, gboolean estimated_dts,
|
||||
guint32 rtptime, GstClockTime base_time, gint gap,
|
||||
gboolean is_rtx);
|
||||
|
||||
gboolean rtp_jitter_buffer_can_fast_start (RTPJitterBuffer * jbuf, gint num_packet);
|
||||
gboolean ts_rtp_jitter_buffer_can_fast_start (RTPJitterBuffer * jbuf, gint num_packet);
|
||||
|
||||
gboolean rtp_jitter_buffer_is_full (RTPJitterBuffer * jbuf);
|
||||
void rtp_jitter_buffer_find_earliest (RTPJitterBuffer * jbuf, GstClockTime *pts, guint * seqnum);
|
||||
gboolean ts_rtp_jitter_buffer_is_full (RTPJitterBuffer * jbuf);
|
||||
void ts_rtp_jitter_buffer_find_earliest (TsRTPJitterBuffer * jbuf, GstClockTime *pts, guint * seqnum);
|
||||
|
||||
#endif /* __RTP_JITTER_BUFFER_H__ */
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include "rtpstats.h"
|
||||
|
||||
void
|
||||
gst_rtp_packet_rate_ctx_reset (RTPPacketRateCtx * ctx, gint32 clock_rate)
|
||||
ts_gst_rtp_packet_rate_ctx_reset (RTPPacketRateCtx * ctx, gint32 clock_rate)
|
||||
{
|
||||
ctx->clock_rate = clock_rate;
|
||||
ctx->probed = FALSE;
|
||||
|
@ -31,7 +31,7 @@ gst_rtp_packet_rate_ctx_reset (RTPPacketRateCtx * ctx, gint32 clock_rate)
|
|||
}
|
||||
|
||||
guint32
|
||||
gst_rtp_packet_rate_ctx_update (RTPPacketRateCtx * ctx, guint16 seqnum,
|
||||
ts_gst_rtp_packet_rate_ctx_update (RTPPacketRateCtx * ctx, guint16 seqnum,
|
||||
guint32 ts)
|
||||
{
|
||||
guint64 new_ts, diff_ts;
|
||||
|
@ -80,13 +80,13 @@ done:
|
|||
}
|
||||
|
||||
guint32
|
||||
gst_rtp_packet_rate_ctx_get (RTPPacketRateCtx * ctx)
|
||||
ts_gst_rtp_packet_rate_ctx_get (RTPPacketRateCtx * ctx)
|
||||
{
|
||||
return ctx->avg_packet_rate;
|
||||
}
|
||||
|
||||
guint32
|
||||
gst_rtp_packet_rate_ctx_get_max_dropout (RTPPacketRateCtx * ctx, gint32 time_ms)
|
||||
ts_gst_rtp_packet_rate_ctx_get_max_dropout (RTPPacketRateCtx * ctx, gint32 time_ms)
|
||||
{
|
||||
if (time_ms <= 0 || !ctx->probed || ctx->avg_packet_rate == G_MAXUINT32) {
|
||||
return RTP_DEF_DROPOUT;
|
||||
|
@ -96,7 +96,7 @@ gst_rtp_packet_rate_ctx_get_max_dropout (RTPPacketRateCtx * ctx, gint32 time_ms)
|
|||
}
|
||||
|
||||
guint32
|
||||
gst_rtp_packet_rate_ctx_get_max_misorder (RTPPacketRateCtx * ctx,
|
||||
ts_gst_rtp_packet_rate_ctx_get_max_misorder (RTPPacketRateCtx * ctx,
|
||||
gint32 time_ms)
|
||||
{
|
||||
if (time_ms <= 0 || !ctx->probed || ctx->avg_packet_rate == G_MAXUINT32) {
|
||||
|
@ -113,9 +113,9 @@ gst_rtp_packet_rate_ctx_get_max_misorder (RTPPacketRateCtx * ctx,
|
|||
* Initialize @stats with its default values.
|
||||
*/
|
||||
void
|
||||
rtp_stats_init_defaults (RTPSessionStats * stats)
|
||||
ts_rtp_stats_init_defaults (RTPSessionStats * stats)
|
||||
{
|
||||
rtp_stats_set_bandwidths (stats, -1, -1, -1, -1);
|
||||
ts_rtp_stats_set_bandwidths (stats, -1, -1, -1, -1);
|
||||
stats->min_interval = RTP_STATS_MIN_INTERVAL;
|
||||
stats->bye_timeout = RTP_STATS_BYE_TIMEOUT;
|
||||
stats->nacks_dropped = 0;
|
||||
|
@ -136,7 +136,7 @@ rtp_stats_init_defaults (RTPSessionStats * stats)
|
|||
* defaults.
|
||||
*/
|
||||
void
|
||||
rtp_stats_set_bandwidths (RTPSessionStats * stats, guint rtp_bw,
|
||||
ts_rtp_stats_set_bandwidths (RTPSessionStats * stats, guint rtp_bw,
|
||||
gdouble rtcp_bw, guint rs, guint rr)
|
||||
{
|
||||
GST_DEBUG ("recalc bandwidths: RTP %u, RTCP %f, RS %u, RR %u", rtp_bw,
|
||||
|
@ -217,7 +217,7 @@ rtp_stats_set_bandwidths (RTPSessionStats * stats, guint rtp_bw,
|
|||
* Returns: the RTCP interval.
|
||||
*/
|
||||
GstClockTime
|
||||
rtp_stats_calculate_rtcp_interval (RTPSessionStats * stats, gboolean we_send,
|
||||
ts_rtp_stats_calculate_rtcp_interval (RTPSessionStats * stats, gboolean we_send,
|
||||
GstRTPProfile profile, gboolean ptp, gboolean first)
|
||||
{
|
||||
gdouble members, senders, n;
|
||||
|
@ -296,7 +296,7 @@ rtp_stats_calculate_rtcp_interval (RTPSessionStats * stats, gboolean we_send,
|
|||
* Returns: the new RTCP interval.
|
||||
*/
|
||||
GstClockTime
|
||||
rtp_stats_add_rtcp_jitter (RTPSessionStats * stats G_GNUC_UNUSED,
|
||||
ts_rtp_stats_add_rtcp_jitter (RTPSessionStats * stats G_GNUC_UNUSED,
|
||||
GstClockTime interval)
|
||||
{
|
||||
gdouble temp;
|
||||
|
@ -323,7 +323,7 @@ rtp_stats_add_rtcp_jitter (RTPSessionStats * stats G_GNUC_UNUSED,
|
|||
* Returns: the BYE interval.
|
||||
*/
|
||||
GstClockTime
|
||||
rtp_stats_calculate_bye_interval (RTPSessionStats * stats)
|
||||
ts_rtp_stats_calculate_bye_interval (RTPSessionStats * stats)
|
||||
{
|
||||
gdouble members;
|
||||
gdouble avg_rtcp_size, rtcp_bw;
|
||||
|
@ -377,7 +377,7 @@ rtp_stats_calculate_bye_interval (RTPSessionStats * stats)
|
|||
* Returns: total RTP packets lost.
|
||||
*/
|
||||
gint64
|
||||
rtp_stats_get_packets_lost (const RTPSourceStats * stats)
|
||||
ts_rtp_stats_get_packets_lost (const RTPSourceStats * stats)
|
||||
{
|
||||
gint64 lost;
|
||||
guint64 extended_max, expected;
|
||||
|
@ -390,41 +390,7 @@ rtp_stats_get_packets_lost (const RTPSourceStats * stats)
|
|||
}
|
||||
|
||||
void
|
||||
rtp_stats_set_min_interval (RTPSessionStats * stats, gdouble min_interval)
|
||||
ts_rtp_stats_set_min_interval (RTPSessionStats * stats, gdouble min_interval)
|
||||
{
|
||||
stats->min_interval = min_interval;
|
||||
}
|
||||
|
||||
gboolean
|
||||
__g_socket_address_equal (GSocketAddress * a, GSocketAddress * b)
|
||||
{
|
||||
GInetSocketAddress *ia, *ib;
|
||||
GInetAddress *iaa, *iab;
|
||||
|
||||
ia = G_INET_SOCKET_ADDRESS (a);
|
||||
ib = G_INET_SOCKET_ADDRESS (b);
|
||||
|
||||
if (g_inet_socket_address_get_port (ia) !=
|
||||
g_inet_socket_address_get_port (ib))
|
||||
return FALSE;
|
||||
|
||||
iaa = g_inet_socket_address_get_address (ia);
|
||||
iab = g_inet_socket_address_get_address (ib);
|
||||
|
||||
return g_inet_address_equal (iaa, iab);
|
||||
}
|
||||
|
||||
gchar *
|
||||
__g_socket_address_to_string (GSocketAddress * addr)
|
||||
{
|
||||
GInetSocketAddress *ia;
|
||||
gchar *ret, *tmp;
|
||||
|
||||
ia = G_INET_SOCKET_ADDRESS (addr);
|
||||
|
||||
tmp = g_inet_address_to_string (g_inet_socket_address_get_address (ia));
|
||||
ret = g_strdup_printf ("%s:%u", tmp, g_inet_socket_address_get_port (ia));
|
||||
g_free (tmp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -216,11 +216,11 @@ typedef struct {
|
|||
guint32 avg_packet_rate;
|
||||
} RTPPacketRateCtx;
|
||||
|
||||
void gst_rtp_packet_rate_ctx_reset (RTPPacketRateCtx * ctx, gint32 clock_rate);
|
||||
guint32 gst_rtp_packet_rate_ctx_update (RTPPacketRateCtx *ctx, guint16 seqnum, guint32 ts);
|
||||
guint32 gst_rtp_packet_rate_ctx_get (RTPPacketRateCtx *ctx);
|
||||
guint32 gst_rtp_packet_rate_ctx_get_max_dropout (RTPPacketRateCtx *ctx, gint32 time_ms);
|
||||
guint32 gst_rtp_packet_rate_ctx_get_max_misorder (RTPPacketRateCtx *ctx, gint32 time_ms);
|
||||
void ts_gst_rtp_packet_rate_ctx_reset (RTPPacketRateCtx * ctx, gint32 clock_rate);
|
||||
guint32 ts_gst_rtp_packet_rate_ctx_update (RTPPacketRateCtx *ctx, guint16 seqnum, guint32 ts);
|
||||
guint32 ts_gst_rtp_packet_rate_ctx_get (RTPPacketRateCtx *ctx);
|
||||
guint32 ts_gst_rtp_packet_rate_ctx_get_max_dropout (RTPPacketRateCtx *ctx, gint32 time_ms);
|
||||
guint32 ts_gst_rtp_packet_rate_ctx_get_max_misorder (RTPPacketRateCtx *ctx, gint32 time_ms);
|
||||
|
||||
/**
|
||||
* RTPSessionStats:
|
||||
|
@ -245,23 +245,19 @@ typedef struct {
|
|||
guint nacks_received;
|
||||
} RTPSessionStats;
|
||||
|
||||
void rtp_stats_init_defaults (RTPSessionStats *stats);
|
||||
void ts_rtp_stats_init_defaults (RTPSessionStats *stats);
|
||||
|
||||
void rtp_stats_set_bandwidths (RTPSessionStats *stats,
|
||||
guint rtp_bw,
|
||||
gdouble rtcp_bw,
|
||||
guint rs, guint rr);
|
||||
void ts_rtp_stats_set_bandwidths (RTPSessionStats *stats,
|
||||
guint rtp_bw,
|
||||
gdouble rtcp_bw,
|
||||
guint rs, guint rr);
|
||||
|
||||
GstClockTime rtp_stats_calculate_rtcp_interval (RTPSessionStats *stats, gboolean sender, GstRTPProfile profile, gboolean ptp, gboolean first);
|
||||
GstClockTime rtp_stats_add_rtcp_jitter (RTPSessionStats *stats, GstClockTime interval);
|
||||
GstClockTime rtp_stats_calculate_bye_interval (RTPSessionStats *stats);
|
||||
gint64 rtp_stats_get_packets_lost (const RTPSourceStats *stats);
|
||||
GstClockTime ts_rtp_stats_calculate_rtcp_interval (RTPSessionStats *stats, gboolean sender, GstRTPProfile profile, gboolean ptp, gboolean first);
|
||||
GstClockTime ts_rtp_stats_add_rtcp_jitter (RTPSessionStats *stats, GstClockTime interval);
|
||||
GstClockTime ts_rtp_stats_calculate_bye_interval (RTPSessionStats *stats);
|
||||
gint64 ts_rtp_stats_get_packets_lost (const RTPSourceStats *stats);
|
||||
|
||||
void rtp_stats_set_min_interval (RTPSessionStats *stats,
|
||||
gdouble min_interval);
|
||||
|
||||
|
||||
gboolean __g_socket_address_equal (GSocketAddress *a, GSocketAddress *b);
|
||||
gchar * __g_socket_address_to_string (GSocketAddress * addr);
|
||||
void ts_rtp_stats_set_min_interval (RTPSessionStats *stats,
|
||||
gdouble min_interval);
|
||||
|
||||
#endif /* __RTP_STATS_H__ */
|
||||
|
|
|
@ -16,29 +16,30 @@
|
|||
#[macro_use]
|
||||
pub mod runtime;
|
||||
|
||||
pub mod socket;
|
||||
mod tcpclientsrc;
|
||||
mod udpsink;
|
||||
mod udpsrc;
|
||||
|
||||
mod appsrc;
|
||||
mod audiotestsrc;
|
||||
pub mod dataqueue;
|
||||
mod inputselector;
|
||||
mod jitterbuffer;
|
||||
mod proxy;
|
||||
mod queue;
|
||||
pub mod socket;
|
||||
mod tcpclientsrc;
|
||||
mod udpsink;
|
||||
mod udpsrc;
|
||||
|
||||
use gst::glib;
|
||||
|
||||
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
udpsrc::register(plugin)?;
|
||||
udpsink::register(plugin)?;
|
||||
tcpclientsrc::register(plugin)?;
|
||||
queue::register(plugin)?;
|
||||
proxy::register(plugin)?;
|
||||
appsrc::register(plugin)?;
|
||||
jitterbuffer::register(plugin)?;
|
||||
audiotestsrc::register(plugin)?;
|
||||
inputselector::register(plugin)?;
|
||||
jitterbuffer::register(plugin)?;
|
||||
proxy::register(plugin)?;
|
||||
queue::register(plugin)?;
|
||||
tcpclientsrc::register(plugin)?;
|
||||
udpsink::register(plugin)?;
|
||||
udpsrc::register(plugin)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -375,6 +375,8 @@ impl TaskImpl for UdpSrcTask {
|
|||
)
|
||||
})?;
|
||||
|
||||
drop(settings);
|
||||
|
||||
self.socket = Some(
|
||||
Socket::try_new(
|
||||
self.element.clone().upcast(),
|
||||
|
|
|
@ -336,13 +336,13 @@ fn eos() {
|
|||
.name("src-eos")
|
||||
.property("caps", &caps)
|
||||
.property("do-timestamp", true)
|
||||
.property("context", &CONTEXT)
|
||||
.property("context", CONTEXT)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let queue = gst::ElementFactory::make("ts-queue")
|
||||
.name("queue-eos")
|
||||
.property("context", &CONTEXT)
|
||||
.property("context", CONTEXT)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
|
@ -636,7 +636,7 @@ fn socket_play_null_play() {
|
|||
let sink = gst::ElementFactory::make("ts-udpsink")
|
||||
.name(format!("sink-{}", TEST).as_str())
|
||||
.property("socket", &socket)
|
||||
.property("context", &TEST)
|
||||
.property("context", TEST)
|
||||
.property("context-wait", 20u32)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
|
|
@ -100,6 +100,6 @@ fn test_chain() {
|
|||
assert!(buf == [42, 43, 44, 45, 0]);
|
||||
});
|
||||
|
||||
let buf = gst::Buffer::from_slice(&[42, 43, 44, 45]);
|
||||
let buf = gst::Buffer::from_slice([42, 43, 44, 45]);
|
||||
assert!(h.push(buf) == Ok(gst::FlowSuccess::Ok));
|
||||
}
|
||||
|
|
713
meson.build
713
meson.build
|
@ -1,11 +1,16 @@
|
|||
project('gst-plugins-rs',
|
||||
'rust',
|
||||
'c',
|
||||
version: '0.9.0-alpha.1',
|
||||
version: '0.9.13',
|
||||
meson_version : '>= 0.60')
|
||||
|
||||
python = import('python').find_installation()
|
||||
# dependencies.py needs a toml parsing module
|
||||
python = import('python').find_installation(modules: ['tomllib'], required: false)
|
||||
if not python.found()
|
||||
python = import('python').find_installation(modules: ['tomli'])
|
||||
endif
|
||||
fs = import('fs')
|
||||
host_system = host_machine.system()
|
||||
|
||||
if get_option('debug')
|
||||
target = 'debug'
|
||||
|
@ -16,16 +21,17 @@ endif
|
|||
cargo = find_program('cargo', version:'>=1.40')
|
||||
cargo_wrapper = find_program('cargo_wrapper.py')
|
||||
cargo_c = find_program('cargo-cbuild', version:'>=0.9.3', required: false)
|
||||
rustc = find_program('rustc', version:'>=1.52')
|
||||
|
||||
rustc = meson.get_compiler('rust')
|
||||
|
||||
if not cargo_c.found()
|
||||
error('cargo-c missing, install it with: \'cargo install cargo-c\'')
|
||||
endif
|
||||
|
||||
system = build_machine.system()
|
||||
ext_exe = ''
|
||||
system = host_machine.system()
|
||||
exe_suffix = ''
|
||||
if system == 'windows'
|
||||
ext_exe = 'exe'
|
||||
exe_suffix = '.exe'
|
||||
ext_dynamic = 'dll'
|
||||
ext_static = 'lib'
|
||||
elif system == 'darwin'
|
||||
|
@ -36,135 +42,18 @@ else
|
|||
ext_static = 'a'
|
||||
endif
|
||||
|
||||
# workspace name -> lib name
|
||||
# kept in the same order as the `members` list in Cargo.toml
|
||||
plugins = {
|
||||
'gst-plugin-audiofx': 'libgstrsaudiofx',
|
||||
'gst-plugin-claxon': 'libgstclaxon',
|
||||
# csound has an external dependency, see below
|
||||
'gst-plugin-lewton': 'libgstlewton',
|
||||
'gst-plugin-spotify': 'libgstspotify',
|
||||
|
||||
'gst-plugin-file': 'libgstrsfile',
|
||||
# sodium has an external dependency, see below
|
||||
'gst-plugin-threadshare': 'libgstthreadshare',
|
||||
|
||||
'gst-plugin-fmp4': 'libgstfmp4',
|
||||
|
||||
'gst-plugin-aws': 'libgstaws',
|
||||
'gst-plugin-hlssink3': 'libgsthlssink3',
|
||||
'gst-plugin-ndi': 'libgstndi',
|
||||
'gst-plugin-onvif': 'libgstrsonvif',
|
||||
'gst-plugin-raptorq': 'libgstraptorq',
|
||||
'gst-plugin-reqwest': 'libgstreqwest',
|
||||
'gst-plugin-rtp': 'libgstrsrtp',
|
||||
'gst-plugin-webrtchttp': 'libgstwebrtchttp',
|
||||
'gst-plugin-webrtc': 'libgstrswebrtc',
|
||||
|
||||
'gst-plugin-textahead': 'libgsttextahead',
|
||||
'gst-plugin-json': 'libgstjson',
|
||||
'gst-plugin-regex': 'libgstregex',
|
||||
'gst-plugin-textwrap': 'libgsttextwrap',
|
||||
|
||||
'gst-plugin-fallbackswitch': 'libgstfallbackswitch',
|
||||
'gst-plugin-togglerecord': 'libgsttogglerecord',
|
||||
'gst-plugin-tracers': 'libgstrstracers',
|
||||
'gst-plugin-uriplaylistbin': 'libgsturiplaylistbin',
|
||||
|
||||
'gst-plugin-cdg': 'libgstcdg',
|
||||
# closedcaption has an external dependency, see below
|
||||
# dav1d has an external dependency, see below
|
||||
'gst-plugin-ffv1': 'libgstffv1',
|
||||
'gst-plugin-flavors': 'libgstrsflv',
|
||||
'gst-plugin-gif': 'libgstgif',
|
||||
# gtk4 has an external dependency, see below
|
||||
'gst-plugin-hsv': 'libgsthsv',
|
||||
'gst-plugin-png': 'libgstrspng',
|
||||
'gst-plugin-rav1e': 'libgstrav1e',
|
||||
# videofx has an external dependency, see below
|
||||
|
||||
# FIXME: libwebp-sys2 will build its bundled version on msvc and apple platforms
|
||||
# https://github.com/qnighy/libwebp-sys2-rs/issues/4
|
||||
'gst-plugin-webp': 'libgstrswebp',
|
||||
}
|
||||
|
||||
# Extra env to pass to cargo
|
||||
extra_env = {}
|
||||
|
||||
if dependency('cairo-gobject', required : get_option('videofx')).found()
|
||||
plugins += {'gst-plugin-videofx': 'libgstrsvideofx',}
|
||||
endif
|
||||
|
||||
if dependency('pangocairo', required : get_option('closedcaption')).found()
|
||||
plugins += {'gst-plugin-closedcaption' : 'libgstrsclosedcaption',}
|
||||
endif
|
||||
|
||||
if dependency('dav1d', version : '>= 1.0.0', required : get_option('dav1d')).found()
|
||||
plugins += {'gst-plugin-dav1d' : 'libgstdav1d'}
|
||||
endif
|
||||
|
||||
sodium = get_option ('sodium')
|
||||
if sodium == 'system'
|
||||
dependency('libsodium')
|
||||
plugins += {'gst-plugin-sodium': 'libgstsodium'}
|
||||
extra_env += {'SODIUM_USE_PKG_CONFIG': '1'}
|
||||
elif sodium == 'built-in'
|
||||
plugins += {'gst-plugin-sodium': 'libgstsodium'}
|
||||
endif
|
||||
|
||||
cc = meson.get_compiler('c')
|
||||
csound_option = get_option('csound')
|
||||
# try first to find csound using pkg-config
|
||||
csound_dep = dependency('', required: false)
|
||||
if not csound_dep.found() and not csound_option.disabled()
|
||||
# if csound isn't distributed with pkg-config then user needs to define CSOUND_LIB_DIR with its location
|
||||
|
||||
res = run_command(python, '-c', 'import os; print(os.environ["CSOUND_LIB_DIR"])', check: false)
|
||||
if res.returncode() == 0
|
||||
csound_libdir = res.stdout().strip()
|
||||
csound_dep = cc.find_library('csound64', dirs: csound_libdir, required: false)
|
||||
if csound_dep.found()
|
||||
extra_env += {'CSOUND_LIB_DIR': csound_libdir}
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
if csound_dep.found()
|
||||
plugins += {'gst-plugin-csound' : 'libgstcsound'}
|
||||
elif csound_option.enabled()
|
||||
error('csound option is enabled, but csound64 library could not be found and CSOUND_LIB_DIR was not set')
|
||||
else
|
||||
message('csound not found, disabling its plugin')
|
||||
endif
|
||||
|
||||
if dependency('gtk4', required : get_option('gtk4')).found()
|
||||
plugins += {'gst-plugin-gtk4' : 'libgstgtk4',}
|
||||
endif
|
||||
|
||||
output = []
|
||||
|
||||
extensions = []
|
||||
|
||||
# Add the plugin file as output
|
||||
if get_option('default_library') == 'shared' or get_option('default_library') == 'both'
|
||||
extensions += [ext_dynamic]
|
||||
foreach p, lib : plugins
|
||||
output += [lib + '.' + ext_dynamic]
|
||||
endforeach
|
||||
endif
|
||||
|
||||
static_build = get_option('default_library') == 'static'
|
||||
if static_build or get_option('default_library') == 'both'
|
||||
extensions += [ext_static]
|
||||
foreach p, lib : plugins
|
||||
output += [lib + '.' + ext_static]
|
||||
endforeach
|
||||
endif
|
||||
# Used to not lookup the same dependency multiple times which clutters logs
|
||||
deps_cache = {}
|
||||
|
||||
# Need to depends on all gstreamer-rs deps to ensure they are built
|
||||
# before gstreamer-rs when building with gst-build.
|
||||
# Custom targets can't depend on dependency() objects so we have to depend
|
||||
# on the library variable from the subproject instead.
|
||||
gst_req = '>= 1.18.0'
|
||||
glib_req = '>=2.62'
|
||||
gst_req = '>=1.20.0'
|
||||
depends = []
|
||||
|
||||
deps = [
|
||||
|
@ -173,19 +62,36 @@ deps = [
|
|||
['gstreamer-app-1.0', 'gst-plugins-base', 'app_dep', 'gstapp'],
|
||||
['gstreamer-audio-1.0', 'gst-plugins-base', 'audio_dep', 'gstaudio'],
|
||||
['gstreamer-base-1.0', 'gstreamer', 'gst_base_dep', 'gst_base'],
|
||||
['gstreamer-check-1.0', 'gstreamer', 'gst_check_dep', 'gst_check'],
|
||||
['gstreamer-net-1.0', 'gstreamer', 'gst_net_dep', 'gst_net'],
|
||||
['gstreamer-rtp-1.0', 'gst-plugins-base', 'rtp_dep', 'gst_rtp'],
|
||||
['gstreamer-video-1.0', 'gst-plugins-base', 'video_dep', 'gstvideo'],
|
||||
['gstreamer-sdp-1.0', 'gst-plugins-base', 'sdp_dep', 'gstsdp'],
|
||||
['gstreamer-webrtc-1.0', 'gst-plugins-bad', 'gstwebrtc_dep', 'gstwebrtc'],
|
||||
]
|
||||
|
||||
# Used to not lookup the same dependency multiple times which clutters logs
|
||||
deps_cache = {}
|
||||
if get_option('threadshare').allowed() \
|
||||
or get_option('onvif').allowed() \
|
||||
or get_option('raptorq').allowed() \
|
||||
or get_option('rtp').allowed() \
|
||||
or get_option('webrtc').allowed()
|
||||
deps += [['gstreamer-rtp-1.0', 'gst-plugins-base', 'rtp_dep', 'gst_rtp']]
|
||||
endif
|
||||
if get_option('webrtc').allowed() \
|
||||
or get_option('webrtchttp').allowed()
|
||||
deps += [['gstreamer-webrtc-1.0', 'gst-plugins-bad', 'gstwebrtc_dep', 'gstwebrtc']]
|
||||
deps += [['gstreamer-sdp-1.0', 'gst-plugins-base', 'sdp_dep', 'gstsdp']]
|
||||
endif
|
||||
if get_option('tests').allowed()
|
||||
deps += [['gstreamer-check-1.0', 'gstreamer', 'gst_check_dep', 'gst_check']]
|
||||
endif
|
||||
if get_option('gtk4').allowed()
|
||||
deps += [['gstreamer-gl-1.0', 'gst-plugins-base', 'gst_gl_dep', 'gstgl']]
|
||||
endif
|
||||
if get_option('threadshare').allowed()
|
||||
deps += [['gstreamer-net-1.0', 'gstreamer', 'gst_net_dep', 'gst_net']]
|
||||
endif
|
||||
|
||||
glib_dep = dependency('glib-2.0', version: glib_req)
|
||||
deps_cache += {'glib-2.0': glib_dep}
|
||||
|
||||
foreach d: deps
|
||||
dep = dependency(d[0], version : gst_req,
|
||||
dep = dependency(d[0], version: gst_req,
|
||||
fallback : [d[1], d[2]])
|
||||
set_variable(d[2], dep)
|
||||
deps_cache += {d[0]: dep}
|
||||
|
@ -195,55 +101,393 @@ foreach d: deps
|
|||
endif
|
||||
endforeach
|
||||
|
||||
include = ','.join(plugins.keys())
|
||||
# kept in the same order as the `members` list in Cargo.toml
|
||||
plugins = {
|
||||
'audiofx': {
|
||||
'library': 'libgstrsaudiofx',
|
||||
'examples': ['hrtfrender'],
|
||||
},
|
||||
'claxon': {'library': 'libgstclaxon'},
|
||||
# csound has a non-trivial external dependency, see below
|
||||
'lewton': {'library': 'libgstlewton'},
|
||||
'spotify': {'library': 'libgstspotify'},
|
||||
|
||||
# serialize extra_env
|
||||
extra_env_list = []
|
||||
foreach key, value : extra_env
|
||||
extra_env_list += key + ':' + value
|
||||
'file': {'library': 'libgstrsfile'},
|
||||
# sodium can have an external dependency, see below
|
||||
'threadshare': {
|
||||
'library': 'libgstthreadshare',
|
||||
'examples': [
|
||||
'ts-benchmark',
|
||||
'udpsrc-benchmark-sender',
|
||||
'tcpclientsrc-benchmark-sender',
|
||||
'ts-standalone',
|
||||
],
|
||||
},
|
||||
|
||||
'mp4': {'library': 'libgstmp4'},
|
||||
'fmp4': {
|
||||
'library': 'libgstfmp4',
|
||||
'examples': [
|
||||
'dash_vod',
|
||||
'hls_live',
|
||||
'hls_vod',
|
||||
],
|
||||
},
|
||||
|
||||
'aws': {
|
||||
'library': 'libgstaws',
|
||||
'extra-deps': {'openssl': '>=1.1'},
|
||||
},
|
||||
'hlssink3': {'library': 'libgsthlssink3'},
|
||||
'ndi': {'library': 'libgstndi'},
|
||||
'onvif': {
|
||||
'library': 'libgstrsonvif',
|
||||
'extra-deps': {'pangocairo': ''},
|
||||
},
|
||||
'raptorq': {'library': 'libgstraptorq'},
|
||||
'reqwest': {'library': 'libgstreqwest'},
|
||||
'rtp': {'library': 'libgstrsrtp'},
|
||||
'webrtchttp': {'library': 'libgstwebrtchttp'},
|
||||
'webrtc': {
|
||||
'library': 'libgstrswebrtc',
|
||||
'examples': ['webrtcsink-stats-server'],
|
||||
},
|
||||
|
||||
'textahead': {'library': 'libgsttextahead'},
|
||||
'json': {'library': 'libgstjson'},
|
||||
'regex': {'library': 'libgstregex'},
|
||||
'textwrap': {'library': 'libgsttextwrap'},
|
||||
|
||||
'tracers': {'library': 'libgstrstracers'},
|
||||
'uriplaylistbin': {
|
||||
'library': 'libgsturiplaylistbin',
|
||||
'examples': ['playlist'],
|
||||
'features': ['clap'],
|
||||
},
|
||||
|
||||
'cdg': {'library': 'libgstcdg'},
|
||||
'closedcaption': {
|
||||
'library': 'libgstrsclosedcaption',
|
||||
'extra-deps': {
|
||||
'pango': '',
|
||||
'pangocairo': '',
|
||||
'cairo-gobject': '',
|
||||
}
|
||||
},
|
||||
'dav1d': {
|
||||
'library': 'libgstdav1d',
|
||||
'extra-deps': {'dav1d': '>=1.0'},
|
||||
},
|
||||
'ffv1': {'library': 'libgstffv1'},
|
||||
'flavors': {'library': 'libgstrsflv'},
|
||||
'gif': {
|
||||
'library': 'libgstgif',
|
||||
'examples': ['testvideosrc2gif'],
|
||||
},
|
||||
# gtk4 is added below
|
||||
'hsv': {'library': 'libgsthsv'},
|
||||
'png': {
|
||||
'library': 'libgstrspng',
|
||||
'examples': ['pngenc'],
|
||||
},
|
||||
'rav1e': {'library': 'libgstrav1e'},
|
||||
'videofx': {
|
||||
'library': 'libgstrsvideofx',
|
||||
'extra-deps': {'cairo-gobject': ''},
|
||||
},
|
||||
}
|
||||
|
||||
if get_option('examples').allowed()
|
||||
plugins += {
|
||||
'fallbackswitch': {
|
||||
'library': 'libgstfallbackswitch',
|
||||
'examples': ['gtk-fallbackswitch'],
|
||||
'features': ['gtk', 'gio', 'gst-plugin-gtk4'],
|
||||
},
|
||||
'livesync': {
|
||||
'library': 'libgstlivesync',
|
||||
'examples': ['gtk-livesync'],
|
||||
'features': ['gtk', 'gio', 'gst-plugin-gtk4'],
|
||||
},
|
||||
'togglerecord': {
|
||||
'library': 'libgsttogglerecord',
|
||||
'examples': ['gtk-recording'],
|
||||
'features': ['gtk', 'gio', 'gst-plugin-gtk4'],
|
||||
},
|
||||
}
|
||||
else
|
||||
plugins += {
|
||||
'fallbackswitch': { 'library': 'libgstfallbackswitch'},
|
||||
'livesync': { 'library': 'libgstlivesync'},
|
||||
'togglerecord': { 'library': 'libgsttogglerecord'},
|
||||
}
|
||||
endif
|
||||
|
||||
# Won't build on platforms where it bundles the sources because of:
|
||||
# https://github.com/qnighy/libwebp-sys2-rs/issues/12
|
||||
# the fix is:
|
||||
# https://github.com/qnighy/libwebp-sys2-rs/pull/13
|
||||
if host_system not in ['windows', 'darwin']
|
||||
# FIXME: libwebp-sys2 will build its bundled version on msvc and apple platforms
|
||||
# https://github.com/qnighy/libwebp-sys2-rs/issues/4
|
||||
plugins += {'webp': {
|
||||
'library': 'libgstrswebp',
|
||||
'extra-deps': {'libwebpdemux': ''},
|
||||
}}
|
||||
endif
|
||||
|
||||
sodium_opt = get_option('sodium')
|
||||
if sodium_opt.allowed()
|
||||
sodium_plugin = {'sodium': {
|
||||
'library': 'libgstsodium',
|
||||
'examples': ['generate-keys', 'encrypt-example', 'decrypt-example'],
|
||||
'features': ['serde', 'serde_json', 'clap'],
|
||||
}}
|
||||
if get_option('sodium-source') == 'system'
|
||||
sodium_dep = dependency('libsodium', required: sodium_opt.enabled())
|
||||
extra_env += {'SODIUM_USE_PKG_CONFIG': '1'}
|
||||
if sodium_dep.found()
|
||||
plugins += sodium_plugin
|
||||
endif
|
||||
else
|
||||
plugins += sodium_plugin
|
||||
endif
|
||||
endif
|
||||
|
||||
cc = meson.get_compiler('c')
|
||||
csound_option = get_option('csound')
|
||||
if csound_option.allowed()
|
||||
# if csound isn't distributed with pkg-config then user needs to define CSOUND_LIB_DIR with its location
|
||||
|
||||
res = run_command(python, '-c', 'import os; print(os.environ["CSOUND_LIB_DIR"])', check: false)
|
||||
if res.returncode() == 0
|
||||
csound_libdir = res.stdout().strip()
|
||||
csound_dep = cc.find_library('csound64', dirs: csound_libdir, required: false)
|
||||
if csound_dep.found()
|
||||
plugins += {'csound': {
|
||||
'library': 'libgstcsound',
|
||||
'examples': ['csound-effect'],
|
||||
}}
|
||||
extra_env += {'CSOUND_LIB_DIR': csound_libdir}
|
||||
elif csound_option.enabled()
|
||||
error('csound option is enabled, but csound64 library could not be found and CSOUND_LIB_DIR was not set')
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
if get_option('gtk4').allowed()
|
||||
gtk4_features = []
|
||||
gl_winsys = gst_gl_dep.get_variable('gl_winsys').split()
|
||||
gl_platforms = gst_gl_dep.get_variable('gl_platforms').split()
|
||||
if 'wayland' in gl_winsys
|
||||
gtk4_features += 'wayland'
|
||||
endif
|
||||
if 'x11' in gl_winsys
|
||||
if 'egl' in gl_platforms
|
||||
gtk4_features += 'x11egl'
|
||||
endif
|
||||
if 'glx' in gl_platforms
|
||||
gtk4_features += 'x11glx'
|
||||
endif
|
||||
endif
|
||||
plugins += {
|
||||
'gtk4': {
|
||||
'library': 'libgstgtk4',
|
||||
'examples': ['gtksink'],
|
||||
'extra-deps': {'gtk4': '>=4.6'},
|
||||
'features': gtk4_features,
|
||||
},
|
||||
}
|
||||
endif
|
||||
|
||||
# Process plugins list
|
||||
|
||||
default_library = get_option('default_library')
|
||||
|
||||
library_suffixes = []
|
||||
if default_library in ['shared', 'both']
|
||||
library_suffixes += [ext_dynamic]
|
||||
endif
|
||||
if default_library in ['static', 'both']
|
||||
library_suffixes += [ext_static]
|
||||
endif
|
||||
|
||||
# cargo packages (plugins) to build
|
||||
packages = []
|
||||
# cargo features
|
||||
features = []
|
||||
# examples to build
|
||||
examples = []
|
||||
# Add the plugin library files as output
|
||||
output = []
|
||||
|
||||
if get_option('gtk4').allowed()
|
||||
if glib_dep.version().version_compare('>=2.74')
|
||||
features += ['glib/v2_74', 'gio/v2_74']
|
||||
elif glib_dep.version().version_compare('>=2.72')
|
||||
features += ['glib/v2_72', 'gio/v2_72']
|
||||
elif glib_dep.version().version_compare('>=2.70')
|
||||
features += ['glib/v2_70', 'gio/v2_70']
|
||||
elif glib_dep.version().version_compare('>=2.68')
|
||||
features += ['glib/v2_68', 'gio/v2_68']
|
||||
elif glib_dep.version().version_compare('>=2.66')
|
||||
features += ['glib/v2_66', 'gio/v2_66']
|
||||
elif glib_dep.version().version_compare('>=2.64')
|
||||
features += ['glib/v2_64', 'gio/v2_64']
|
||||
elif glib_dep.version().version_compare('>=2.62')
|
||||
features += ['glib/v2_62', 'gio/v2_62']
|
||||
elif glib_dep.version().version_compare('>=2.60')
|
||||
features += ['glib/v2_60', 'gio/v2_60']
|
||||
elif glib_dep.version().version_compare('>=2.58')
|
||||
features += ['glib/v2_58', 'gio/v2_58']
|
||||
endif
|
||||
endif
|
||||
|
||||
if get_option('rav1e').allowed()
|
||||
nasm = find_program('nasm', required: false)
|
||||
if nasm.found()
|
||||
features += 'gst-plugin-rav1e/asm'
|
||||
extra_env += {'NASM': nasm.full_path()}
|
||||
endif
|
||||
endif
|
||||
|
||||
if get_option('default_library') == 'static'
|
||||
extra_env += {
|
||||
# Tell the pkg-config crate to think of all libraries as static
|
||||
'PKG_CONFIG_ALL_STATIC': '1',
|
||||
# Tell the system-deps crate to process linker flag for static deps
|
||||
'SYSTEM_DEPS_LINK': 'static'
|
||||
}
|
||||
endif
|
||||
|
||||
foreach plugin_name, details: plugins
|
||||
plugin_opt = get_option(plugin_name)
|
||||
if plugin_opt.allowed()
|
||||
plugin_deps_found = true
|
||||
foreach dep_name, dep_ver: details.get('extra-deps', {})
|
||||
if dep_ver != ''
|
||||
dep = dependency(dep_name, version: dep_ver, required: plugin_opt)
|
||||
else
|
||||
dep = dependency(dep_name, required: plugin_opt)
|
||||
endif
|
||||
deps_cache += {dep_name: dep}
|
||||
if not dep.found()
|
||||
plugin_deps_found = false
|
||||
break
|
||||
endif
|
||||
endforeach
|
||||
plugin_features = details.get('features', [])
|
||||
if plugin_deps_found
|
||||
# Validate gst-plugin features
|
||||
foreach feature: plugin_features
|
||||
if feature.startswith('gst-plugin') and not packages.contains(feature)
|
||||
plugin_deps_found = false
|
||||
break
|
||||
endif
|
||||
endforeach
|
||||
endif
|
||||
if plugin_deps_found
|
||||
packages += f'gst-plugin-@plugin_name@'
|
||||
features += plugin_features
|
||||
extra_features = run_command('dependencies.py', meson.current_source_dir(), plugin_name,
|
||||
'--feature', '--gst-version', gst_dep.version(), capture: true, check: true).stdout().strip()
|
||||
if extra_features != ''
|
||||
features += extra_features.split(',')
|
||||
endif
|
||||
|
||||
lib = details.get('library')
|
||||
# No 'lib' suffix with MSVC
|
||||
if cc.get_argument_syntax() == 'msvc'
|
||||
lib = lib.substring(3)
|
||||
endif
|
||||
if default_library in ['shared', 'both']
|
||||
output += [lib + '.' + ext_dynamic]
|
||||
endif
|
||||
if default_library in ['static', 'both']
|
||||
output += [lib + '.' + ext_static]
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endforeach
|
||||
extra_env_str = ','.join(extra_env_list)
|
||||
|
||||
feature_args = []
|
||||
if features.length() > 0
|
||||
feature_args += ['--features', features]
|
||||
endif
|
||||
|
||||
plugins_install_dir = get_option('libdir') / 'gstreamer-1.0'
|
||||
pkgconfig_install_dir = get_option('libdir') / 'pkgconfig'
|
||||
|
||||
extra_args = []
|
||||
if get_option('doc').disabled()
|
||||
disable_doc = ['--disable-doc']
|
||||
else
|
||||
disable_doc = []
|
||||
extra_args += ['--disable-doc']
|
||||
endif
|
||||
|
||||
rs_plugins = custom_target('gst-plugins-rs',
|
||||
build_by_default: true,
|
||||
output: output,
|
||||
console: true,
|
||||
install: true,
|
||||
install_dir: plugins_install_dir,
|
||||
depends: depends,
|
||||
depfile: 'gst-plugins-rs.dep',
|
||||
command: [cargo_wrapper,
|
||||
'build',
|
||||
meson.current_build_dir(),
|
||||
meson.current_source_dir(),
|
||||
meson.global_build_root(),
|
||||
target,
|
||||
include,
|
||||
extra_env_str,
|
||||
get_option('prefix'),
|
||||
get_option('libdir'),
|
||||
'--depfile', '@DEPFILE@',
|
||||
'--exts', extensions,
|
||||
] + disable_doc)
|
||||
# 'pkgconfig' is the entry in the machine file, if specified
|
||||
pkg_config = find_program('pkgconfig', 'pkg-config')
|
||||
if pkg_config.found()
|
||||
extra_env += {'PKG_CONFIG': pkg_config.full_path()}
|
||||
endif
|
||||
|
||||
plugins = rs_plugins.to_list()
|
||||
pkg_config_path = get_option('pkg_config_path')
|
||||
if pkg_config_path.length() > 0
|
||||
pathsep = ':'
|
||||
if host_system == 'windows'
|
||||
pathsep = ';'
|
||||
endif
|
||||
extra_env += {'PKG_CONFIG_PATH': pathsep.join(pkg_config_path)}
|
||||
endif
|
||||
|
||||
# get cmdline for rust
|
||||
extra_env += {'RUSTC': ' '.join(rustc.cmd_array())}
|
||||
|
||||
plugins = []
|
||||
if output.length() > 0
|
||||
rs_plugins = custom_target('gst-plugins-rs',
|
||||
build_by_default: true,
|
||||
output: output,
|
||||
console: true,
|
||||
install: true,
|
||||
install_dir: plugins_install_dir,
|
||||
depends: depends,
|
||||
depfile: 'gst-plugins-rs.dep',
|
||||
env: extra_env,
|
||||
command: [cargo_wrapper,
|
||||
'build',
|
||||
meson.current_build_dir(),
|
||||
meson.current_source_dir(),
|
||||
meson.global_build_root(),
|
||||
target,
|
||||
get_option('prefix'),
|
||||
get_option('libdir'),
|
||||
'--packages', packages,
|
||||
'--depfile', '@DEPFILE@',
|
||||
'--lib-suffixes', library_suffixes,
|
||||
] + feature_args + extra_args)
|
||||
plugins = rs_plugins.to_list()
|
||||
endif
|
||||
|
||||
# This is used by GStreamer to static link Rust plugins into gst-full
|
||||
gst_plugins = []
|
||||
pc_files = []
|
||||
plugin_names = []
|
||||
foreach plugin : plugins
|
||||
# skip the 'lib' prefix and extension from plugin path
|
||||
plugin_name = fs.stem(plugin.full_path()).substring(3)
|
||||
plugin_name = fs.stem(plugin.full_path())
|
||||
# skip the 'lib' prefix from plugin path when not building with MSVC
|
||||
if cc.get_argument_syntax() != 'msvc'
|
||||
plugin_name = plugin_name.substring(3)
|
||||
endif
|
||||
|
||||
option_name = plugin_name.substring(3)
|
||||
if option_name.startswith('rs')
|
||||
option_name = option_name.substring(2)
|
||||
endif
|
||||
if option_name == 'flv'
|
||||
option_name = 'flavors'
|
||||
endif
|
||||
if not get_option(option_name).allowed()
|
||||
continue
|
||||
endif
|
||||
|
||||
# Extract plugin dependencies from their Cargo.toml file
|
||||
plugin_deps = []
|
||||
|
@ -252,9 +496,9 @@ foreach plugin : plugins
|
|||
check: true)
|
||||
foreach dep_name : p.stdout().strip().split(',')
|
||||
dep_name_version = dep_name.split('|')
|
||||
dep_name = dep_name_version.get(0)
|
||||
dep_name = dep_name_version.get(0).strip()
|
||||
if dep_name_version.length() > 1
|
||||
extras = {'version': dep_name_version.get(1)}
|
||||
extras = {'version': dep_name_version.get(1).strip()}
|
||||
else
|
||||
extras = {}
|
||||
endif
|
||||
|
@ -274,17 +518,17 @@ foreach plugin : plugins
|
|||
)
|
||||
meson.override_dependency(plugin_name, dep)
|
||||
|
||||
if static_build and plugin_name in ['gstcsound', 'gstthreadshare']
|
||||
if default_library == 'static' and plugin_name in ['gstcsound', 'gstthreadshare', 'gstgtk4']
|
||||
warning('Static plugin @0@ is known to fail. It will not be included in libgstreamer-full.'.format(plugin_name))
|
||||
else
|
||||
gst_plugins += dep
|
||||
endif
|
||||
|
||||
pc_files += [plugin_name + '.pc']
|
||||
if plugin_name.startswith('gst')
|
||||
plugin_names += [plugin_name.substring(3)]
|
||||
else
|
||||
plugin_names += [plugin_name]
|
||||
pc_files += [plugin_name + '.pc']
|
||||
if plugin_name.startswith('gst')
|
||||
plugin_names += [plugin_name.substring(3)]
|
||||
else
|
||||
plugin_names += [plugin_name]
|
||||
endif
|
||||
endif
|
||||
endforeach
|
||||
|
||||
|
@ -293,50 +537,81 @@ subdir('docs')
|
|||
# We don't need to pass a command as we depends on the target above
|
||||
# but it is currently mandatory ( https://github.com/mesonbuild/meson/issues/8059 )
|
||||
# so use python as it's guaranteed to be present on any setup
|
||||
custom_target('gst-plugins-rs-pc-files',
|
||||
build_by_default: true,
|
||||
output: pc_files,
|
||||
console: true,
|
||||
install: true,
|
||||
install_dir: pkgconfig_install_dir,
|
||||
depends: rs_plugins,
|
||||
command: [python, '-c', '""'])
|
||||
if pc_files.length() > 0
|
||||
custom_target('gst-plugins-rs-pc-files',
|
||||
build_by_default: true,
|
||||
output: pc_files,
|
||||
console: true,
|
||||
install: true,
|
||||
install_dir: pkgconfig_install_dir,
|
||||
depends: rs_plugins,
|
||||
command: [python, '-c', '""'])
|
||||
endif
|
||||
|
||||
if get_option('webrtc').allowed()
|
||||
custom_target('gst-webrtc-signalling-server',
|
||||
build_by_default: true,
|
||||
output: 'gst-webrtc-signalling-server' + exe_suffix,
|
||||
console: true,
|
||||
install: true,
|
||||
install_dir: get_option('bindir'),
|
||||
depfile: 'gst-webrtc-signalling-server.dep',
|
||||
env: extra_env,
|
||||
command: [cargo_wrapper,
|
||||
'build',
|
||||
meson.current_build_dir(),
|
||||
meson.current_source_dir(),
|
||||
meson.global_build_root(),
|
||||
target,
|
||||
get_option('prefix'),
|
||||
get_option('libdir'),
|
||||
'--depfile', '@DEPFILE@',
|
||||
'--bin', 'gst-webrtc-signalling-server',
|
||||
'--exe-suffix', exe_suffix,
|
||||
])
|
||||
endif
|
||||
|
||||
custom_target('gst-webrtc-signalling-server',
|
||||
build_by_default: true,
|
||||
output: 'gst-webrtc-signalling-server',
|
||||
console: true,
|
||||
install: true,
|
||||
install_dir: get_option('bindir'),
|
||||
depfile: 'gst-webrtc-signalling-server.dep',
|
||||
command: [cargo_wrapper,
|
||||
'build',
|
||||
meson.current_build_dir(),
|
||||
meson.current_source_dir(),
|
||||
meson.global_build_root(),
|
||||
target,
|
||||
'',
|
||||
'',
|
||||
get_option('prefix'),
|
||||
get_option('libdir'),
|
||||
'--depfile', '@DEPFILE@',
|
||||
'--exts', ext_exe,
|
||||
'--bin', 'gst-webrtc-signalling-server'
|
||||
])
|
||||
if get_option('examples').allowed() and examples.length() > 0
|
||||
outputs = []
|
||||
foreach example: examples
|
||||
outputs += [example + exe_suffix]
|
||||
endforeach
|
||||
custom_target('gst-plugins-rs-examples',
|
||||
build_by_default: true,
|
||||
output: outputs,
|
||||
console: true,
|
||||
install: false,
|
||||
depfile: 'gst-plugins-rs-examples.dep',
|
||||
env: extra_env,
|
||||
command: [cargo_wrapper,
|
||||
'build',
|
||||
meson.current_build_dir(),
|
||||
meson.current_source_dir(),
|
||||
meson.global_build_root(),
|
||||
target,
|
||||
get_option('prefix'),
|
||||
get_option('libdir'),
|
||||
'--depfile', '@DEPFILE@',
|
||||
'--packages', packages,
|
||||
'--examples', examples,
|
||||
'--exe-suffix', exe_suffix,
|
||||
] + feature_args)
|
||||
endif
|
||||
|
||||
test('tests',
|
||||
cargo_wrapper,
|
||||
args: ['test',
|
||||
meson.current_build_dir(),
|
||||
meson.current_source_dir(),
|
||||
meson.global_build_root(),
|
||||
target,
|
||||
include,
|
||||
extra_env_str,
|
||||
get_option('prefix'),
|
||||
get_option('libdir')],
|
||||
timeout: 600)
|
||||
if get_option('tests').allowed()
|
||||
test('tests',
|
||||
cargo_wrapper,
|
||||
env: extra_env,
|
||||
args: ['test',
|
||||
meson.current_build_dir(),
|
||||
meson.current_source_dir(),
|
||||
meson.global_build_root(),
|
||||
target,
|
||||
get_option('prefix'),
|
||||
get_option('libdir'),
|
||||
'--packages', packages],
|
||||
timeout: 600)
|
||||
endif
|
||||
|
||||
summary({
|
||||
'Plugins': plugin_names,
|
||||
|
|
|
@ -1,12 +1,66 @@
|
|||
option('videofx', type : 'feature', value : 'auto', description : 'Build videofx plugin')
|
||||
option('closedcaption', type : 'feature', value : 'auto', description : 'Build closedcaption plugin')
|
||||
option('dav1d', type : 'feature', value : 'auto', description : 'Build dav1d plugin')
|
||||
option('sodium', type : 'combo',
|
||||
choices : ['system', 'built-in', 'disabled'], value : 'built-in',
|
||||
description : 'Weither to use libsodium from the system or the built-in version from the sodiumoxide crate')
|
||||
option('csound', type : 'feature', value : 'auto', description : 'Build csound plugin')
|
||||
option('gtk4', type : 'feature', value : 'auto', description : 'Build GTK4 plugin')
|
||||
# Same order as members in Cargo.toml
|
||||
|
||||
# audio
|
||||
option('audiofx', type: 'feature', value: 'auto', description: 'Build audiofx plugin')
|
||||
option('claxon', type: 'feature', value: 'auto', description: 'Build claxon plugin')
|
||||
option('csound', type: 'feature', value: 'auto', description: 'Build csound plugin')
|
||||
option('lewton', type: 'feature', value: 'auto', description: 'Build lewton plugin')
|
||||
option('spotify', type: 'feature', value: 'auto', description: 'Build spotify plugin')
|
||||
|
||||
# generic
|
||||
option('file', type: 'feature', value: 'auto', description: 'Build file plugin')
|
||||
option('sodium', type: 'feature', value: 'auto', description: 'Build sodium plugin')
|
||||
option('sodium-source', type: 'combo',
|
||||
choices: ['system', 'built-in'], value: 'built-in',
|
||||
description: 'Whether to use libsodium from the system or the built-in version from the sodiumoxide crate')
|
||||
option('threadshare', type: 'feature', value: 'auto', description: 'Build threadshare plugin')
|
||||
|
||||
# mux
|
||||
option('flavors', type: 'feature', value: 'auto', description: 'Build flavors plugin')
|
||||
option('fmp4', type: 'feature', value: 'auto', description: 'Build fmp4 plugin')
|
||||
option('mp4', type: 'feature', value: 'auto', description: 'Build mp4 plugin')
|
||||
|
||||
# net
|
||||
option('aws', type: 'feature', value: 'auto', description: 'Build aws plugin')
|
||||
option('hlssink3', type: 'feature', value: 'auto', description: 'Build hlssink3 plugin')
|
||||
option('ndi', type: 'feature', value: 'auto', description: 'Build ndi plugin')
|
||||
option('onvif', type: 'feature', value: 'auto', description: 'Build onvif plugin')
|
||||
option('raptorq', type: 'feature', value: 'auto', description: 'Build raptorq plugin')
|
||||
option('reqwest', type: 'feature', value: 'auto', description: 'Build reqwest plugin')
|
||||
option('rtp', type: 'feature', value: 'auto', description: 'Build rtp plugin')
|
||||
option('webrtc', type: 'feature', value: 'auto', description: 'Build webrtc plugin')
|
||||
option('webrtchttp', type: 'feature', value: 'auto', description: 'Build webrtchttp plugin')
|
||||
|
||||
# text
|
||||
option('textahead', type: 'feature', value: 'auto', description: 'Build textahead plugin')
|
||||
option('json', type: 'feature', value: 'auto', description: 'Build json plugin')
|
||||
option('regex', type: 'feature', value: 'auto', description: 'Build regex plugin')
|
||||
option('textwrap', type: 'feature', value: 'auto', description: 'Build textwrap plugin')
|
||||
|
||||
# utils
|
||||
option('fallbackswitch', type: 'feature', value: 'auto', description: 'Build fallbackswitch plugin')
|
||||
option('livesync', type: 'feature', value: 'auto', description: 'Build livesync plugin')
|
||||
option('togglerecord', type: 'feature', value: 'auto', description: 'Build togglerecord plugin')
|
||||
option('tracers', type: 'feature', value: 'auto', description: 'Build tracers plugin')
|
||||
option('uriplaylistbin', type: 'feature', value: 'auto', description: 'Build uriplaylistbin plugin')
|
||||
|
||||
# video
|
||||
option('cdg', type: 'feature', value: 'auto', description: 'Build cdg plugin')
|
||||
option('closedcaption', type: 'feature', value: 'auto', description: 'Build closedcaption plugin')
|
||||
option('dav1d', type: 'feature', value: 'auto', description: 'Build dav1d plugin')
|
||||
option('ffv1', type: 'feature', value: 'auto', description: 'Build ffv1 plugin')
|
||||
option('gif', type: 'feature', value: 'auto', description: 'Build gif plugin')
|
||||
option('gtk4', type: 'feature', value: 'auto', description: 'Build GTK4 plugin')
|
||||
option('hsv', type: 'feature', value: 'auto', description: 'Build hsv plugin')
|
||||
option('png', type: 'feature', value: 'auto', description: 'Build png plugin')
|
||||
option('rav1e', type: 'feature', value: 'auto', description: 'Build rav1e plugin')
|
||||
option('videofx', type: 'feature', value: 'auto', description: 'Build videofx plugin')
|
||||
option('webp', type: 'feature', value: 'auto', description: 'Build webp plugin')
|
||||
|
||||
# Common options
|
||||
option('doc', type : 'feature', value : 'auto', yield: true,
|
||||
description: 'Enable documentation.')
|
||||
option('doc', type: 'feature', value: 'auto', yield: true,
|
||||
description: 'Enable documentation')
|
||||
option('examples', type: 'feature', value: 'disabled', yield: true,
|
||||
description: 'Build examples')
|
||||
option('tests', type : 'feature', value : 'auto', yield : true,
|
||||
description : 'Build and enable unit tests')
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "gst-plugin-flavors"
|
||||
version = "0.9.0-alpha.1"
|
||||
version = "0.9.13"
|
||||
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
|
||||
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
@ -9,9 +9,9 @@ rust-version = "1.63"
|
|||
description = "GStreamer Rust FLV Plugin"
|
||||
|
||||
[dependencies]
|
||||
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
||||
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
||||
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
|
||||
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19.1" }
|
||||
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
|
||||
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" }
|
||||
num-rational = { version = "0.4", default-features = false, features = [] }
|
||||
nom = "7"
|
||||
flavors = { git = "https://github.com/rust-av/flavors" }
|
||||
|
@ -26,7 +26,7 @@ crate-type = ["cdylib", "rlib"]
|
|||
path = "src/lib.rs"
|
||||
|
||||
[build-dependencies]
|
||||
gst-plugin-version-helper = { path="../../version-helper" }
|
||||
gst-plugin-version-helper = { version = "0.7", path="../../version-helper" }
|
||||
|
||||
[features]
|
||||
static = []
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
../../LICENSE-APACHE
|
201
mux/flavors/LICENSE-APACHE
Normal file
201
mux/flavors/LICENSE-APACHE
Normal file
|
@ -0,0 +1,201 @@
|
|||
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 +0,0 @@
|
|||
../../LICENSE-MIT
|
23
mux/flavors/LICENSE-MIT
Normal file
23
mux/flavors/LICENSE-MIT
Normal file
|
@ -0,0 +1,23 @@
|
|||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue