feat(gst): Added gst a high level wrapper over gstreamer
chore(example): Added hdr-gstreamer-wgpu example chore(license): Added MIT license to all crates
This commit is contained in:
1
gst/.envrc
Normal file
1
gst/.envrc
Normal file
@@ -0,0 +1 @@
|
||||
use flake
|
||||
62
gst/.github/workflows/build.yaml
vendored
Normal file
62
gst/.github/workflows/build.yaml
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
checks-matrix:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: DeterminateSystems/nix-installer-action@main
|
||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
- id: set-matrix
|
||||
name: Generate Nix Matrix
|
||||
run: |
|
||||
set -Eeu
|
||||
matrix="$(nix eval --json '.#githubActions.matrix')"
|
||||
echo "matrix=$matrix" >> "$GITHUB_OUTPUT"
|
||||
|
||||
checks-build:
|
||||
needs: checks-matrix
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix: ${{fromJSON(needs.checks-matrix.outputs.matrix)}}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: DeterminateSystems/nix-installer-action@main
|
||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
- run: nix build -L '.#${{ matrix.attr }}'
|
||||
|
||||
codecov:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: "write"
|
||||
contents: "read"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: DeterminateSystems/nix-installer-action@main
|
||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
|
||||
- name: Run codecov
|
||||
run: nix build .#checks.x86_64-linux.hello-llvm-cov
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v4.0.1
|
||||
with:
|
||||
flags: unittests
|
||||
name: codecov-hello
|
||||
fail_ci_if_error: true
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ./result
|
||||
verbose: true
|
||||
|
||||
38
gst/.github/workflows/docs.yaml
vendored
Normal file
38
gst/.github/workflows/docs.yaml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
docs:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: "write"
|
||||
contents: "read"
|
||||
pages: "write"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: DeterminateSystems/nix-installer-action@main
|
||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
- uses: DeterminateSystems/flake-checker-action@main
|
||||
|
||||
- name: Generate docs
|
||||
run: nix build .#checks.x86_64-linux.hello-docs
|
||||
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v5
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: result/share/doc
|
||||
|
||||
- name: Deploy to gh-pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
|
||||
3
gst/.gitignore
vendored
Normal file
3
gst/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/result
|
||||
/target
|
||||
.direnv
|
||||
1040
gst/Cargo.lock
generated
Normal file
1040
gst/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
14
gst/Cargo.toml
Normal file
14
gst/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "gst"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
error-stack = "0.6"
|
||||
gstreamer = "0.24.4"
|
||||
gstreamer-app = "0.24.4"
|
||||
thiserror = "2.0"
|
||||
tracing = "0.1"
|
||||
wgpu = { version = "27.0.1", default-features = false }
|
||||
136
gst/flake.lock
generated
Normal file
136
gst/flake.lock
generated
Normal file
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"nodes": {
|
||||
"advisory-db": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1765465865,
|
||||
"narHash": "sha256-jAyDD6FKEWZafIKN4KjzdQywcS/gR9sHz4zzjxefXcA=",
|
||||
"owner": "rustsec",
|
||||
"repo": "advisory-db",
|
||||
"rev": "d0bdb37b2b1dc8a81f47e2042d59227b1f06473f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "rustsec",
|
||||
"repo": "advisory-db",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"crane": {
|
||||
"locked": {
|
||||
"lastModified": 1765739568,
|
||||
"narHash": "sha256-gQYx35Of4UDKUjAYvmxjUEh/DdszYeTtT6MDin4loGE=",
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"rev": "67d2baff0f9f677af35db61b32b5df6863bcc075",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix-github-actions": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1737420293,
|
||||
"narHash": "sha256-F1G5ifvqTpJq7fdkT34e/Jy9VCyzd5XfJ9TO8fHhJWE=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nix-github-actions",
|
||||
"rev": "f4158fa080ef4503c8f4c820967d946c2af31ec9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "nix-github-actions",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1765472234,
|
||||
"narHash": "sha256-9VvC20PJPsleGMewwcWYKGzDIyjckEz8uWmT0vCDYK0=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "2fbfb1d73d239d2402a8fe03963e37aab15abe8b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"advisory-db": "advisory-db",
|
||||
"crane": "crane",
|
||||
"flake-utils": "flake-utils",
|
||||
"nix-github-actions": "nix-github-actions",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"rust-overlay": "rust-overlay"
|
||||
}
|
||||
},
|
||||
"rust-overlay": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1765766816,
|
||||
"narHash": "sha256-m2au5a2x9L3ikyBi0g3/NRJSjmHVDvT42mn+O6FlyPs=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "4f53a635709d82652567f51ef7af4365fbc0c88b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
161
gst/flake.nix
Normal file
161
gst/flake.nix
Normal file
@@ -0,0 +1,161 @@
|
||||
{
|
||||
description = "A simple rust flake using rust-overlay and craneLib";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
crane.url = "github:ipetkov/crane";
|
||||
nix-github-actions = {
|
||||
url = "github:nix-community/nix-github-actions";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
rust-overlay = {
|
||||
url = "github:oxalica/rust-overlay";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
advisory-db = {
|
||||
url = "github:rustsec/advisory-db";
|
||||
flake = false;
|
||||
};
|
||||
};
|
||||
|
||||
outputs = {
|
||||
self,
|
||||
crane,
|
||||
flake-utils,
|
||||
nixpkgs,
|
||||
rust-overlay,
|
||||
advisory-db,
|
||||
nix-github-actions,
|
||||
...
|
||||
}:
|
||||
flake-utils.lib.eachDefaultSystem (
|
||||
system: let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
overlays = [
|
||||
rust-overlay.overlays.default
|
||||
];
|
||||
};
|
||||
inherit (pkgs) lib;
|
||||
cargoToml = builtins.fromTOML (builtins.readFile ./Cargo.toml);
|
||||
name = cargoToml.package.name;
|
||||
|
||||
stableToolchain = pkgs.rust-bin.stable.latest.default;
|
||||
stableToolchainWithLLvmTools = stableToolchain.override {
|
||||
extensions = ["rust-src" "llvm-tools"];
|
||||
};
|
||||
stableToolchainWithRustAnalyzer = stableToolchain.override {
|
||||
extensions = ["rust-src" "rust-analyzer"];
|
||||
};
|
||||
craneLib = (crane.mkLib pkgs).overrideToolchain stableToolchain;
|
||||
craneLibLLvmTools = (crane.mkLib pkgs).overrideToolchain stableToolchainWithLLvmTools;
|
||||
|
||||
src = let
|
||||
filterBySuffix = path: exts: lib.any (ext: lib.hasSuffix ext path) exts;
|
||||
sourceFilters = path: type: (craneLib.filterCargoSources path type) || filterBySuffix path [".c" ".h" ".hpp" ".cpp" ".cc"];
|
||||
in
|
||||
lib.cleanSourceWith {
|
||||
filter = sourceFilters;
|
||||
src = ./.;
|
||||
};
|
||||
commonArgs =
|
||||
{
|
||||
inherit src;
|
||||
pname = name;
|
||||
stdenv = p: p.clangStdenv;
|
||||
doCheck = false;
|
||||
# LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
|
||||
# LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs;
|
||||
# SYSTEM_DEPS_LINK = "static";
|
||||
# PKG_CONFIG_ALL_STATIC = "1";
|
||||
GIO_EXTRA_MODULES = "${pkgs.glib-networking}/lib/gio/modules";
|
||||
nativeBuildInputs = with pkgs; [
|
||||
pkg-config
|
||||
];
|
||||
buildInputs = with pkgs;
|
||||
[
|
||||
gst_all_1.gst-libav
|
||||
gst_all_1.gst-plugins-base
|
||||
gst_all_1.gst-plugins-good
|
||||
gst_all_1.gst-plugins-bad
|
||||
gst_all_1.gst-plugins-ugly
|
||||
gst_all_1.gst-plugins-rs
|
||||
gst_all_1.gstreamer
|
||||
glib
|
||||
glib-networking
|
||||
]
|
||||
++ (lib.optionals pkgs.stdenv.isDarwin [
|
||||
libiconv
|
||||
apple-sdk_26
|
||||
]);
|
||||
}
|
||||
// (lib.optionalAttrs pkgs.stdenv.isLinux {
|
||||
# BINDGEN_EXTRA_CLANG_ARGS = "-I${pkgs.llvmPackages.libclang.lib}/lib/clang/18/include";
|
||||
});
|
||||
cargoArtifacts = craneLib.buildPackage commonArgs;
|
||||
in {
|
||||
checks =
|
||||
{
|
||||
"${name}-clippy" = craneLib.cargoClippy (commonArgs
|
||||
// {
|
||||
inherit cargoArtifacts;
|
||||
cargoClippyExtraArgs = "--all-targets -- --deny warnings";
|
||||
});
|
||||
"${name}-docs" = craneLib.cargoDoc (commonArgs // {inherit cargoArtifacts;});
|
||||
"${name}-fmt" = craneLib.cargoFmt {inherit src;};
|
||||
"${name}-toml-fmt" = craneLib.taploFmt {
|
||||
src = pkgs.lib.sources.sourceFilesBySuffices src [".toml"];
|
||||
};
|
||||
# Audit dependencies
|
||||
"${name}-audit" = craneLib.cargoAudit {
|
||||
inherit src advisory-db;
|
||||
};
|
||||
|
||||
# Audit licenses
|
||||
"${name}-deny" = craneLib.cargoDeny {
|
||||
inherit src;
|
||||
};
|
||||
"${name}-nextest" = craneLib.cargoNextest (commonArgs
|
||||
// {
|
||||
inherit cargoArtifacts;
|
||||
partitions = 1;
|
||||
partitionType = "count";
|
||||
});
|
||||
}
|
||||
// lib.optionalAttrs (!pkgs.stdenv.isDarwin) {
|
||||
"${name}-llvm-cov" = craneLibLLvmTools.cargoLlvmCov (commonArgs // {inherit cargoArtifacts;});
|
||||
};
|
||||
|
||||
packages = let
|
||||
pkg = craneLib.buildPackage (
|
||||
commonArgs
|
||||
// {inherit cargoArtifacts;}
|
||||
);
|
||||
in {
|
||||
"${name}" = pkg;
|
||||
default = pkg;
|
||||
};
|
||||
|
||||
devShells = {
|
||||
default = pkgs.mkShell.override {stdenv = pkgs.clangStdenv;} (commonArgs
|
||||
// {
|
||||
packages = with pkgs;
|
||||
[
|
||||
stableToolchainWithRustAnalyzer
|
||||
cargo-nextest
|
||||
cargo-deny
|
||||
]
|
||||
++ (lib.optionals pkgs.stdenv.isDarwin [
|
||||
apple-sdk_26
|
||||
]);
|
||||
});
|
||||
};
|
||||
}
|
||||
)
|
||||
// {
|
||||
githubActions = nix-github-actions.lib.mkGithubMatrix {
|
||||
checks = nixpkgs.lib.getAttrs ["x86_64-linux"] self.checks;
|
||||
};
|
||||
};
|
||||
}
|
||||
7
gst/src/errors.rs
Normal file
7
gst/src/errors.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
pub use error_stack::{Report, ResultExt};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("An error occurred")]
|
||||
pub struct Error;
|
||||
|
||||
pub type Result<T, E = error_stack::Report<Error>> = core::result::Result<T, E>;
|
||||
245
gst/src/lib.rs
Normal file
245
gst/src/lib.rs
Normal file
@@ -0,0 +1,245 @@
|
||||
pub mod errors;
|
||||
use errors::*;
|
||||
use gstreamer::prelude::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
static GST: std::sync::LazyLock<std::sync::Arc<Gst>> = std::sync::LazyLock::new(|| {
|
||||
gstreamer::init().expect("Failed to initialize GStreamer");
|
||||
std::sync::Arc::new(Gst {
|
||||
__private: core::marker::PhantomData,
|
||||
})
|
||||
});
|
||||
|
||||
/// This should be a global singleton
|
||||
pub struct Gst {
|
||||
__private: core::marker::PhantomData<()>,
|
||||
}
|
||||
|
||||
impl Gst {
|
||||
pub fn new() -> Arc<Self> {
|
||||
Arc::clone(&GST)
|
||||
}
|
||||
|
||||
pub fn pipeline_from_str(&self, s: &str) -> Result<Pipeline> {
|
||||
let pipeline = gstreamer::parse::launch(s).change_context(Error)?;
|
||||
let pipeline = pipeline.downcast::<gstreamer::Pipeline>();
|
||||
let pipeline = match pipeline {
|
||||
Err(_e) => return Err(Error).attach("Failed to downcast to Pipeline"),
|
||||
Ok(p) => p,
|
||||
};
|
||||
Ok(Pipeline { pipeline })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Sink {
|
||||
element: gstreamer::Element,
|
||||
}
|
||||
|
||||
pub struct Pipeline {
|
||||
pipeline: gstreamer::Pipeline,
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for Pipeline {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("Pipeline")
|
||||
.field("pipeline", &self.pipeline)
|
||||
// .field("state", &self.pipeline.state(gstreamer::ClockTime::NONE))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Pipeline {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.pipeline.set_state(gstreamer::State::Null);
|
||||
}
|
||||
}
|
||||
|
||||
impl Pipeline {
|
||||
pub fn bus(&self) -> Result<Bus> {
|
||||
let bus = self
|
||||
.pipeline
|
||||
.bus()
|
||||
.ok_or(Error)
|
||||
.attach("Failed to get bus from pipeline")?;
|
||||
Ok(Bus { bus })
|
||||
}
|
||||
|
||||
pub fn set_state(&self, state: gstreamer::State) -> Result<gstreamer::StateChangeSuccess> {
|
||||
let result = self
|
||||
.pipeline
|
||||
.set_state(state)
|
||||
.change_context(Error)
|
||||
.attach("Failed to set pipeline state")?;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Bus {
|
||||
bus: gstreamer::Bus,
|
||||
}
|
||||
|
||||
impl Bus {
|
||||
pub fn iter_timed(
|
||||
&self,
|
||||
timeout: impl Into<Option<gstreamer::ClockTime>>,
|
||||
) -> gstreamer::bus::Iter<'_> {
|
||||
self.bus.iter_timed(timeout)
|
||||
}
|
||||
}
|
||||
|
||||
/// Pads are link points between elements
|
||||
pub struct Pad {
|
||||
pad: gstreamer::Pad,
|
||||
}
|
||||
|
||||
pub struct Element {
|
||||
element: gstreamer::Element,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gst_parse_pipeline() {
|
||||
let gst = Gst::new();
|
||||
let pipeline = gst
|
||||
.pipeline_from_str("videotestsrc ! autovideosink")
|
||||
.expect("Failed to create pipeline");
|
||||
println!("{:?}", pipeline);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gst_parse_invalid_pipeline() {
|
||||
let gst = Gst::new();
|
||||
let result = gst.pipeline_from_str("invalidpipeline");
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gst_play_pipeline() {
|
||||
let gst = Gst::new();
|
||||
let pipeline = gst
|
||||
.pipeline_from_str("videotestsrc ! autovideosink")
|
||||
.expect("Failed to create pipeline");
|
||||
let bus = pipeline.bus().expect("Failed to get bus from pipeline");
|
||||
|
||||
pipeline
|
||||
.set_state(gstreamer::State::Playing)
|
||||
.expect("Unable to set the pipeline to the `Playing` state");
|
||||
|
||||
for msg in bus.iter_timed(gstreamer::ClockTime::NONE) {
|
||||
use gstreamer::MessageView;
|
||||
|
||||
match msg.view() {
|
||||
MessageView::Eos(..) => break,
|
||||
MessageView::Error(err) => {
|
||||
eprintln!(
|
||||
"Error from {:?}: {} ({:?})",
|
||||
err.src().map(|s| s.path_string()),
|
||||
err.error(),
|
||||
err.debug()
|
||||
);
|
||||
break;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
pipeline
|
||||
.set_state(gstreamer::State::Null)
|
||||
.expect("Unable to set the pipeline to the `Null` state");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn gstreamer_unwrapped() {
|
||||
gstreamer::init();
|
||||
let uri = "https://gstreamer.freedesktop.org/data/media/sintel_trailer-480p.webm";
|
||||
let pipeline = gstreamer::parse::launch(&format!("playbin uri={}", uri)).unwrap();
|
||||
use gstreamer::prelude::*;
|
||||
|
||||
pipeline.set_state(gstreamer::State::Playing).unwrap();
|
||||
|
||||
let bus = pipeline.bus().unwrap();
|
||||
for msg in bus.iter_timed(gstreamer::ClockTime::NONE) {
|
||||
use gstreamer::MessageView;
|
||||
|
||||
match msg.view() {
|
||||
MessageView::Eos(..) => break,
|
||||
MessageView::Error(err) => {
|
||||
eprintln!(
|
||||
"Error from {:?}: {} ({:?})",
|
||||
err.src().map(|s| s.path_string()),
|
||||
err.error(),
|
||||
err.debug()
|
||||
);
|
||||
break;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
pipeline.set_state(gstreamer::State::Null).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_appsink() {
|
||||
let gst = Gst::new();
|
||||
let pipeline = gst
|
||||
.pipeline_from_str(
|
||||
"videotestsrc ! videoconvert | capsfilter name=video-filter ! appsink name=video-sink",
|
||||
)
|
||||
.expect("Failed to create pipeline");
|
||||
|
||||
// let video_sink = pipeline.
|
||||
|
||||
let bus = pipeline.bus().expect("Failed to get bus from pipeline");
|
||||
|
||||
let sink = pipeline
|
||||
.pipeline
|
||||
.by_name("video-sink")
|
||||
.expect("Sink not found")
|
||||
.downcast::<gstreamer_app::AppSink>()
|
||||
.expect("Failed to downcast to AppSink");
|
||||
let capsfilter = pipeline
|
||||
.pipeline
|
||||
.by_name("video-filter")
|
||||
.expect("Capsfilter not found");
|
||||
|
||||
let caps = gstreamer::Caps::builder("video/x-raw")
|
||||
.field("format", "RGBA")
|
||||
.build();
|
||||
capsfilter.set_property("caps", &caps);
|
||||
|
||||
sink.set_callbacks(
|
||||
gstreamer_app::AppSinkCallbacks::builder()
|
||||
.new_sample(|sink| {
|
||||
// foo
|
||||
Ok(gstreamer::FlowSuccess::Ok)
|
||||
})
|
||||
.build(),
|
||||
);
|
||||
|
||||
// let appsink = sink
|
||||
// .dynamic_cast::<gstreamer_app::AppSink>()
|
||||
// .expect("Failed to cast to AppSink");
|
||||
|
||||
pipeline
|
||||
.set_state(gstreamer::State::Playing)
|
||||
.expect("Unable to set the pipeline to the `Playing` state");
|
||||
|
||||
for msg in bus.iter_timed(gstreamer::ClockTime::NONE) {
|
||||
use gstreamer::MessageView;
|
||||
|
||||
match msg.view() {
|
||||
MessageView::Eos(..) => break,
|
||||
MessageView::Error(err) => {
|
||||
eprintln!(
|
||||
"Error from {:?}: {} ({:?})",
|
||||
err.src().map(|s| s.path_string()),
|
||||
err.error(),
|
||||
err.debug()
|
||||
);
|
||||
break;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
0
gst/src/wgpu.rs
Normal file
0
gst/src/wgpu.rs
Normal file
Reference in New Issue
Block a user