mirror of
https://gitlab.freedesktop.org/dabrain34/GstPipelineStudio.git
synced 2024-05-19 00:38:10 +00:00
Compare commits
48 commits
Author | SHA1 | Date | |
---|---|---|---|
1f200f4f30 | |||
44d64ccdc4 | |||
3985458832 | |||
7a70feedba | |||
88afa4a99e | |||
d32c75b639 | |||
7789588aef | |||
b1ad0e958b | |||
9c8a578e05 | |||
3e15b7cecb | |||
11cf962bfd | |||
a25f0499c8 | |||
51769d6061 | |||
af317eee96 | |||
3aded523c2 | |||
d3005335b8 | |||
18458e3465 | |||
a6f03db8f6 | |||
bed8d6a58e | |||
c5f9cac444 | |||
30baa56881 | |||
13165fa9c0 | |||
6ca3059914 | |||
f4019fd2af | |||
8c6cda2e92 | |||
9c03de5d00 | |||
24121856ee | |||
e410289a13 | |||
2601454143 | |||
f47b0624fd | |||
b2c6a8bc2a | |||
d002e2811f | |||
0148a43946 | |||
e12fecf971 | |||
886c099dba | |||
f81bedb71a | |||
2787211f0d | |||
98d6451e74 | |||
970103ddee | |||
aa551db066 | |||
4569fa79e8 | |||
be2d7ab6d0 | |||
cf438b523c | |||
66cd1b9c15 | |||
1789bb0b25 | |||
c1ae2c37b6 | |||
a32b16c467 | |||
06b0ec8ead |
|
@ -9,6 +9,7 @@ stages:
|
|||
- lint
|
||||
- test
|
||||
- release
|
||||
- deploy
|
||||
|
||||
variables:
|
||||
FDO_UPSTREAM_REPO: "dabrain34/GstPipelineStudio"
|
||||
|
@ -21,7 +22,7 @@ variables:
|
|||
variables:
|
||||
FDO_DISTRIBUTION_VERSION: "38"
|
||||
# Update this to trigger a container rebuild
|
||||
FDO_DISTRIBUTION_TAG: "2023-08-25.1"
|
||||
FDO_DISTRIBUTION_TAG: $GST_RS_FDO_IMG_TAG
|
||||
before_script:
|
||||
- source ./ci/env.sh
|
||||
- mkdir .cargo && echo -e "[net]\ngit-fetch-with-cli = true" > .cargo/config
|
||||
|
@ -59,7 +60,8 @@ build-fedora-container:
|
|||
bison
|
||||
FDO_DISTRIBUTION_EXEC: >-
|
||||
ci/install-rust.sh stable &&
|
||||
pip3 install meson
|
||||
pip3 install meson &&
|
||||
pip3 install pre-commit
|
||||
|
||||
.windows rust docker build:
|
||||
stage: prepare
|
||||
|
@ -89,10 +91,9 @@ build-fedora-container:
|
|||
windows rust docker stable:
|
||||
extends: ".windows rust docker build"
|
||||
variables:
|
||||
RUST_IMAGE: !reference [variables, "WINDOWS_RUST_STABLE_IMAGE"]
|
||||
RUST_UPSTREAM_IMAGE:
|
||||
!reference [variables, "WINDOWS_RUST_STABLE_UPSTREAM_IMAGE"]
|
||||
RUST_VERSION: !reference [variables, "GST_RS_STABLE"]
|
||||
RUST_IMAGE: $WINDOWS_RUST_STABLE_IMAGE
|
||||
RUST_UPSTREAM_IMAGE: $WINDOWS_RUST_STABLE_UPSTREAM_IMAGE"]
|
||||
RUST_VERSION: $GST_RS_STABLE
|
||||
|
||||
.msvc2019 build:
|
||||
stage: test
|
||||
|
@ -119,15 +120,27 @@ windows installer stable:
|
|||
expire_in: 10 days
|
||||
when: "manual"
|
||||
|
||||
rustfmt-clippy:
|
||||
pre-commit:
|
||||
stage: "lint"
|
||||
extends:
|
||||
- .fedora
|
||||
- .fdo.distribution-image@fedora
|
||||
variables:
|
||||
PRE_COMMIT_HOME: ${CI_PROJECT_DIR}/.cache/pre-commit
|
||||
cache:
|
||||
paths:
|
||||
- ${PRE_COMMIT_HOME}
|
||||
script:
|
||||
- meson setup build
|
||||
- pre-commit run --all-files
|
||||
|
||||
clippy:
|
||||
extends:
|
||||
- .fedora
|
||||
- .fdo.distribution-image@fedora
|
||||
stage: lint
|
||||
script:
|
||||
- meson build
|
||||
- cargo fmt --version
|
||||
- cargo fmt -- --color=always --check
|
||||
- cargo clippy --version
|
||||
- cargo clippy --color=always --all-targets -- -D warnings
|
||||
|
||||
|
@ -227,7 +240,7 @@ flatpak:
|
|||
macos installer stable:
|
||||
stage: test
|
||||
tags:
|
||||
- gst-macos-11.1
|
||||
- gst-macos-13
|
||||
before_script:
|
||||
- pip3 install --upgrade pip
|
||||
# Make sure meson is up to date
|
||||
|
@ -265,3 +278,19 @@ macos installer release:
|
|||
- installer/**/GstPipelineStudio*.dmg
|
||||
- installer/**/GstPipelineStudio*.tar.gz
|
||||
when: "always"
|
||||
|
||||
pages:
|
||||
image: ruby:2.7
|
||||
stage: deploy
|
||||
script:
|
||||
- gem install bundler -v 2.4.22
|
||||
- bundle install
|
||||
- bundle exec jekyll build -d public
|
||||
artifacts:
|
||||
paths:
|
||||
# The folder that contains the files to be exposed at the Page URL
|
||||
- public
|
||||
rules:
|
||||
# This ensures that only pushes to the default branch will trigger
|
||||
# a pages deploy
|
||||
- if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
|
||||
|
|
29
.pre-commit-config.yaml
Normal file
29
.pre-commit-config.yaml
Normal file
|
@ -0,0 +1,29 @@
|
|||
# See https://pre-commit.com for more information
|
||||
# See https://pre-commit.com/hooks.html for more hooks
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v3.2.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: check-yaml
|
||||
- id: check-added-large-files
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.1.11
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
# Run the formatter.
|
||||
- id: ruff-format
|
||||
|
||||
- repo: https://github.com/crate-ci/typos
|
||||
rev: v1.17.0
|
||||
hooks:
|
||||
- id: typos
|
||||
exclude: '^$|\.svg$'
|
||||
|
||||
- repo: https://github.com/doublify/pre-commit-rust
|
||||
rev: v1.0
|
||||
hooks:
|
||||
- id: fmt
|
1002
Cargo.lock
generated
1002
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
15
Cargo.toml
15
Cargo.toml
|
@ -1,15 +1,15 @@
|
|||
[package]
|
||||
name = "gst_pipeline_studio"
|
||||
version = "0.3.2"
|
||||
name = "gst-pipeline-studio"
|
||||
version = "0.3.5"
|
||||
edition = "2018"
|
||||
rust-version = "1.70.0"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
gtk = { version = "0.7.2", package = "gtk4" }
|
||||
gst = { package = "gstreamer", version = "0.21.0" }
|
||||
gst-plugin-gtk4 = { version = "0.11.0", optional=true }
|
||||
gtk = { version = "0.8.0", package = "gtk4" }
|
||||
gst = { package = "gstreamer", version = "0.22.2" }
|
||||
gst-plugin-gtk4 = { version = "0.12.1", optional=true }
|
||||
anyhow = "1"
|
||||
log = "0.4.11"
|
||||
once_cell = "1.7.2"
|
||||
|
@ -18,6 +18,11 @@ serde = "1.0"
|
|||
serde_any = "0.5"
|
||||
simplelog = "0.11.2"
|
||||
futures-channel = "0.3"
|
||||
lazy_static = "1.4"
|
||||
chrono = "0.4"
|
||||
structopt = "0.3"
|
||||
async-channel = "2.0.0"
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
futures-executor = "0.3"
|
||||
|
|
23
ChangeLog.md
23
ChangeLog.md
|
@ -103,4 +103,25 @@
|
|||
|
||||
## 0.3.2
|
||||
### app
|
||||
- [x] check that element exists before creating it on file load.
|
||||
- [x] check that element exists before creating it on file load.
|
||||
|
||||
## 0.3.3
|
||||
|
||||
### app
|
||||
- [x] Fix MacOs GTK runtime dependencies
|
||||
- [x] Fix the maximize call with MacOS
|
||||
- [x] Fix the default size at GTK save/load state
|
||||
|
||||
## 0.3.4
|
||||
|
||||
### app
|
||||
- [x] Fix first run when application folder has not been created, fixes #23
|
||||
- [x] Fix windows installer to bring share folder and let filesrc work properly, fixes #24
|
||||
|
||||
## 0.3.5
|
||||
|
||||
### app
|
||||
- [x] logs: receive multiple log sources such as GST logs and messages.
|
||||
- [x] settings: add a log level selection
|
||||
- [x] rename gst_pipeline_studio to gst-pipeline-studio
|
||||
- [x] can open a pipeline from the command line
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
# Important
|
||||
|
||||
# [GstPipelineStudio](https://dabrain34.pages.freedesktop.org/GstPipelineStudio): Draw your own GStreamer pipeline ...
|
||||
|
||||
## Important
|
||||
|
||||
Until version 1.0, this software should be considered as **unstable**.
|
||||
The settings moreover the graph file format might change over the development phase.
|
||||
|
||||
# GstPipelineStudio: Draw your own GStreamer pipeline ...
|
||||
|
||||
## Description
|
||||
|
||||
|
@ -49,7 +51,7 @@ brew install gstreamer gst-plugins-base gst-plugins-bad
|
|||
```sh
|
||||
$ meson builddir -Dbuildtype=release
|
||||
$ ninja -C builddir
|
||||
$ ./builddir/target/release/gst_pipeline_studio
|
||||
$ ./builddir/target/release/gst-pipeline-studio
|
||||
```
|
||||
|
||||
## Flatpak
|
||||
|
|
2
TODO.md
2
TODO.md
|
@ -14,14 +14,12 @@
|
|||
- [ ] Control the connection between element
|
||||
- [ ] unable to connect element with incompatible caps.
|
||||
- [ ] Implement graph dot render/load
|
||||
|
||||
- [ ] Add probes on each pad to monitor the pipeline
|
||||
- [ ] Render a media file
|
||||
- [ ] Offer compatible element to a pad (autorender)
|
||||
- [ ] Display tags/meta/message detected
|
||||
- [ ] Change TreeView to ListView
|
||||
- [ ] Implement zoom on the view (https://gitlab.gnome.org/World/obfuscate/-/blob/master/src/widgets/drawing_area.rs)
|
||||
- [ ] Settings: add a log level selection
|
||||
- [ ] reopen the last log on prematured exit (crash)
|
||||
- [ ] Play/pause should be prevented until the pipeline is ready
|
||||
- [ ] Filter the elements by class/rank etc.
|
||||
|
|
|
@ -7,26 +7,39 @@ import shutil
|
|||
|
||||
env = os.environ
|
||||
|
||||
MESON_BUILD_ROOT=sys.argv[1]
|
||||
MESON_SOURCE_ROOT=sys.argv[2]
|
||||
CARGO_TARGET_DIR = os.path.join (MESON_BUILD_ROOT, "target")
|
||||
MESON_BUILD_ROOT = sys.argv[1]
|
||||
MESON_SOURCE_ROOT = sys.argv[2]
|
||||
CARGO_TARGET_DIR = os.path.join(MESON_BUILD_ROOT, "target")
|
||||
env["CARGO_TARGET_DIR"] = CARGO_TARGET_DIR
|
||||
env["CARGO_HOME"] = os.path.join (CARGO_TARGET_DIR, "cargo-home")
|
||||
OUTPUT=sys.argv[3]
|
||||
BUILDTYPE=sys.argv[4]
|
||||
APP_BIN=sys.argv[5]
|
||||
env["PKG_CONFIG_PATH"] = os.path.join(MESON_BUILD_ROOT, "meson-uninstalled") + os.pathsep + env.get("PKG_CONFIG_PATH",'')
|
||||
env["CARGO_HOME"] = os.path.join(CARGO_TARGET_DIR, "cargo-home")
|
||||
OUTPUT = sys.argv[3]
|
||||
BUILDTYPE = sys.argv[4]
|
||||
APP_BIN = sys.argv[5]
|
||||
env["PKG_CONFIG_PATH"] = (
|
||||
os.path.join(MESON_BUILD_ROOT, "meson-uninstalled")
|
||||
+ os.pathsep
|
||||
+ env.get("PKG_CONFIG_PATH", "")
|
||||
)
|
||||
|
||||
|
||||
if BUILDTYPE == "release":
|
||||
if BUILDTYPE == "release":
|
||||
print("RELEASE MODE")
|
||||
CMD = ['cargo', 'build', '--manifest-path', os.path.join(MESON_SOURCE_ROOT, 'Cargo.toml'), '--release']
|
||||
CMD = [
|
||||
"cargo",
|
||||
"build",
|
||||
"--manifest-path",
|
||||
os.path.join(MESON_SOURCE_ROOT, "Cargo.toml"),
|
||||
"--release",
|
||||
]
|
||||
subprocess.run(CMD, env=env)
|
||||
shutil.copy2(os.path.join(CARGO_TARGET_DIR, "release", APP_BIN), OUTPUT)
|
||||
else:
|
||||
print("DEBUG MODE")
|
||||
CMD = ['cargo', 'build', '--manifest-path', os.path.join(MESON_SOURCE_ROOT, 'Cargo.toml')]
|
||||
CMD = [
|
||||
"cargo",
|
||||
"build",
|
||||
"--manifest-path",
|
||||
os.path.join(MESON_SOURCE_ROOT, "Cargo.toml"),
|
||||
]
|
||||
subprocess.run(CMD, env=env)
|
||||
shutil.copy2(os.path.join(CARGO_TARGET_DIR, "debug", APP_BIN), OUTPUT)
|
||||
|
||||
|
||||
|
|
|
@ -3,19 +3,17 @@
|
|||
from os import environ, path
|
||||
from subprocess import call
|
||||
|
||||
prefix = environ.get('MESON_INSTALL_PREFIX', '/usr/local')
|
||||
datadir = path.join(prefix, 'share')
|
||||
destdir = environ.get('DESTDIR', '')
|
||||
prefix = environ.get("MESON_INSTALL_PREFIX", "/usr/local")
|
||||
datadir = path.join(prefix, "share")
|
||||
destdir = environ.get("DESTDIR", "")
|
||||
|
||||
# Package managers set this so we don't need to run
|
||||
if not destdir:
|
||||
print('Updating icon cache...')
|
||||
call(['gtk-update-icon-cache', '-qtf', path.join(datadir, 'icons', 'hicolor')])
|
||||
|
||||
print('Updating desktop database...')
|
||||
call(['update-desktop-database', '-q', path.join(datadir, 'applications')])
|
||||
|
||||
print('Compiling GSettings schemas...')
|
||||
call(['glib-compile-schemas', path.join(datadir, 'glib-2.0', 'schemas')])
|
||||
print("Updating icon cache...")
|
||||
call(["gtk-update-icon-cache", "-qtf", path.join(datadir, "icons", "hicolor")])
|
||||
|
||||
print("Updating desktop database...")
|
||||
call(["update-desktop-database", "-q", path.join(datadir, "applications")])
|
||||
|
||||
print("Compiling GSettings schemas...")
|
||||
call(["glib-compile-schemas", path.join(datadir, "glib-2.0", "schemas")])
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"sdk-extensions": [
|
||||
"org.freedesktop.Sdk.Extension.rust-stable"
|
||||
],
|
||||
"command": "gst_pipeline_studio",
|
||||
"command": "gst-pipeline-studio",
|
||||
"finish-args": [
|
||||
"--socket=fallback-x11",
|
||||
"--socket=wayland",
|
||||
|
@ -45,7 +45,7 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"name": "gst_pipeline_studio",
|
||||
"name": "gst-pipeline-studio",
|
||||
"buildsystem": "meson",
|
||||
"run-tests": true,
|
||||
"config-opts": [
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
$env:MESON_ARGS = "--prefix=C:\gst-install\"
|
||||
$env:MESON_ARGS = "--prefix=C:\gst-install\ -Dbuildtype=release"
|
||||
cmd.exe /C "C:\BuildTools\Common7\Tools\VsDevCmd.bat -host_arch=amd64 -arch=amd64 && meson _build $env:MESON_ARGS && meson compile -C _build && ninja -C _build install"
|
||||
if (!$?) {
|
||||
Write-Host "Failed to build and install GstPipelineStudio"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
variables:
|
||||
GST_RS_WIN_IMG_TAG: "2023-08-31.0"
|
||||
GST_RS_FDO_IMG_TAG: "2023-08-25.1"
|
||||
GST_RS_WIN_IMG_TAG: "2023-12-22.0"
|
||||
GST_RS_FDO_IMG_TAG: "2024-01-05.0"
|
||||
GST_RS_STABLE: "1.70.0"
|
||||
|
|
|
@ -26,7 +26,7 @@ if (!$?) {
|
|||
Exit 1
|
||||
}
|
||||
|
||||
$env:MESON_ARGS = "--prefix=C:\gst-install\ " +
|
||||
$env:MESON_ARGS = "--prefix=C:\gst-install\ -Dbuildtype=release " +
|
||||
"-Dglib:installed_tests=false " +
|
||||
"-Dlibnice:tests=disabled " +
|
||||
"-Dlibnice:examples=disabled " +
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;
|
||||
|
||||
$env:MESON_ARGS = "--prefix=C:\gst-install\"
|
||||
$env:MESON_ARGS = "--prefix=C:\gst-install\ -Dbuildtype=release"
|
||||
|
||||
# Download gtk and all its subprojects
|
||||
git clone -b $env:DEFAULT_GTK_BRANCH --depth 1 https://gitlab.gnome.org/gnome/gtk.git C:\gtk
|
||||
|
|
573
data/css/style.css
Normal file
573
data/css/style.css
Normal file
|
@ -0,0 +1,573 @@
|
|||
/*
|
||||
|
||||
GstPipelineStudio Website
|
||||
====================
|
||||
|
||||
shamelessly stolen CSS from Pipewire
|
||||
|
||||
*/
|
||||
|
||||
/* GNOME Color Palette */
|
||||
:root {
|
||||
--rounded-corner: 12px;
|
||||
--blue1: rgb(153, 193, 241);
|
||||
--blue2: rgb(98, 160, 234);
|
||||
--blue3: rgb(53, 132, 228);
|
||||
--blue4: rgb(28, 113, 216);
|
||||
--blue5: rgb(26, 95, 180);
|
||||
--green1: rgb(143, 240, 164);
|
||||
--green2: rgb(87, 227, 137);
|
||||
--green3: rgb(51, 209, 122);
|
||||
--green4: rgb(46, 194, 126);
|
||||
--green5: rgb(38, 162, 105);
|
||||
--yellow1: rgb(249, 240, 107);
|
||||
--yellow2: rgb(248, 228, 92);
|
||||
--yellow3: rgb(246, 211, 45);
|
||||
--yellow4: rgb(245, 194, 17);
|
||||
--yellow5: rgb(229, 165, 10);
|
||||
--orange1: rgb(255, 190, 111);
|
||||
--orange2: rgb(255, 163, 72);
|
||||
--orange3: rgb(255, 120, 0);
|
||||
--orange4: rgb(230, 97, 0);
|
||||
--orange5: rgb(198, 70, 0);
|
||||
--red1: rgb(246, 97, 81);
|
||||
--red2: rgb(237, 51, 59);
|
||||
--red3: rgb(224, 27, 36);
|
||||
--red4: rgb(192, 28, 40);
|
||||
--red5: rgb(165, 29, 45);
|
||||
--purple1: rgb(220, 138, 221);
|
||||
--purple2: rgb(192, 97, 203);
|
||||
--purple3: rgb(145, 65, 172);
|
||||
--purple4: rgb(129, 61, 156);
|
||||
--purple5: rgb(97, 53, 131);
|
||||
--brown1: rgb(205, 171, 143);
|
||||
--brown2: rgb(181, 131, 90);
|
||||
--brown3: rgb(152, 106, 68);
|
||||
--brown4: rgb(134, 94, 60);
|
||||
--brown5: rgb(99, 69, 44);
|
||||
--light1: rgb(255, 255, 255);
|
||||
--light2: rgb(246, 245, 244);
|
||||
--light3: rgb(222, 221, 218);
|
||||
--light4: rgb(192, 191, 188);
|
||||
--light5: rgb(154, 153, 150);
|
||||
--dark1: rgb(119, 118, 123);
|
||||
--dark2: rgb(94, 92, 100);
|
||||
--dark3: rgb(61, 56, 70);
|
||||
--dark4: rgb(36, 31, 49);
|
||||
--dark5: rgb(0, 0, 0);
|
||||
--primary-color: var(--blue5);
|
||||
/* Set your project color */
|
||||
--borders: var(--light3);
|
||||
}
|
||||
|
||||
/* Typography */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter Var';
|
||||
font-weight: 100 900;
|
||||
font-display: swap;
|
||||
font-style: oblique 0deg 10deg;
|
||||
src: url("fonts/Inter.var.woff2?v=3.19") format("woff2");
|
||||
}
|
||||
|
||||
* {
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 16px;
|
||||
font-family: "Inter Var", sans-serif;
|
||||
font-weight: 400;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
body {
|
||||
color: #241f31;
|
||||
background-color: #f6f5f4;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin: 2rem 0 -6px;
|
||||
font-weight: 600;
|
||||
line-height: 1.25;
|
||||
font-variation-settings: "wght" 600;
|
||||
/* needed for webkit */
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 30px;
|
||||
font-weight: 100;
|
||||
font-style: normal;
|
||||
margin: 3rem 0 2rem;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 650px) {
|
||||
h1 {
|
||||
font-size: 38px;
|
||||
}
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
filter: invert(100%) hue-rotate(180deg);
|
||||
}
|
||||
|
||||
html {
|
||||
background-color: var(--dark5);
|
||||
}
|
||||
|
||||
img,
|
||||
video,
|
||||
iframe {
|
||||
filter: invert(100%) hue-rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
color: var(--primary-color);
|
||||
cursor: pointer;
|
||||
font-variation-settings: "wght" 600;
|
||||
/* needed for webkit */
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
b {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
small {
|
||||
color: #777;
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 3rem auto 4rem;
|
||||
width: 40%;
|
||||
opacity: 40%;
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
margin: 2rem auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
img.full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
img.pixels {
|
||||
image-rendering: crisp-edges;
|
||||
/* older firefox browsers */
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
||||
|
||||
/* Layout */
|
||||
.container {
|
||||
width: 80%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 720px;
|
||||
}
|
||||
|
||||
/* Singletons */
|
||||
#logo {
|
||||
display: block;
|
||||
width: 35%;
|
||||
height: 35%;
|
||||
color: rgba(0, 0, 0, 0);
|
||||
/* make text transparent */
|
||||
background: url('../icons/org.freedesktop.dabrain34.GstPipelineStudio.ico') no-repeat center;
|
||||
padding: 5rem 0 3rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.page-logo>img {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
a.page-logo {
|
||||
filter: invert(100%) hue-rotate(180deg);
|
||||
/* uninvert */
|
||||
background-image: url('assets/page-logo-i.svg');
|
||||
}
|
||||
}
|
||||
|
||||
.brand-white {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.brand-green {
|
||||
background-color: #30D475;
|
||||
}
|
||||
|
||||
.brand-black {
|
||||
background-color: #201A26;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.page-link::after {
|
||||
content: " ➜";
|
||||
}
|
||||
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
text-align: center;
|
||||
padding: 3em 0 3em;
|
||||
font-size: 1em;
|
||||
margin-top: 4rem;
|
||||
}
|
||||
|
||||
/* Make tables vertically aligned to the top */
|
||||
tbody td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
/* Github Code Highlighting */
|
||||
.highlight table td {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.highlight table pre {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.highlight .cm {
|
||||
color: #999988;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.highlight .cp {
|
||||
color: #999999;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.highlight .c1 {
|
||||
color: #999988;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.highlight .cs {
|
||||
color: #999999;
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.highlight .c,
|
||||
.highlight .ch,
|
||||
.highlight .cd,
|
||||
.highlight .cpf {
|
||||
color: #999988;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.highlight .err {
|
||||
color: #a61717;
|
||||
background-color: #e3d2d2;
|
||||
}
|
||||
|
||||
.highlight .gd {
|
||||
color: #000000;
|
||||
background-color: #ffdddd;
|
||||
}
|
||||
|
||||
.highlight .ge {
|
||||
color: #000000;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.highlight .gr {
|
||||
color: #aa0000;
|
||||
}
|
||||
|
||||
.highlight .gh {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.highlight .gi {
|
||||
color: #000000;
|
||||
background-color: #ddffdd;
|
||||
}
|
||||
|
||||
.highlight .go {
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
.highlight .gp {
|
||||
color: #555555;
|
||||
}
|
||||
|
||||
.highlight .gs {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.highlight .gu {
|
||||
color: #aaaaaa;
|
||||
}
|
||||
|
||||
.highlight .gt {
|
||||
color: #aa0000;
|
||||
}
|
||||
|
||||
.highlight .kc {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.highlight .kd {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.highlight .kn {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.highlight .kp {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.highlight .kr {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.highlight .kt {
|
||||
color: #445588;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.highlight .k,
|
||||
.highlight .kv {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.highlight .mf {
|
||||
color: #009999;
|
||||
}
|
||||
|
||||
.highlight .mh {
|
||||
color: #009999;
|
||||
}
|
||||
|
||||
.highlight .il {
|
||||
color: #009999;
|
||||
}
|
||||
|
||||
.highlight .mi {
|
||||
color: #009999;
|
||||
}
|
||||
|
||||
.highlight .mo {
|
||||
color: #009999;
|
||||
}
|
||||
|
||||
.highlight .m,
|
||||
.highlight .mb,
|
||||
.highlight .mx {
|
||||
color: #009999;
|
||||
}
|
||||
|
||||
.highlight .sb {
|
||||
color: #d14;
|
||||
}
|
||||
|
||||
.highlight .sc {
|
||||
color: #d14;
|
||||
}
|
||||
|
||||
.highlight .sd {
|
||||
color: #d14;
|
||||
}
|
||||
|
||||
.highlight .s2 {
|
||||
color: #d14;
|
||||
}
|
||||
|
||||
.highlight .se {
|
||||
color: #d14;
|
||||
}
|
||||
|
||||
.highlight .sh {
|
||||
color: #d14;
|
||||
}
|
||||
|
||||
.highlight .si {
|
||||
color: #d14;
|
||||
}
|
||||
|
||||
.highlight .sx {
|
||||
color: #d14;
|
||||
}
|
||||
|
||||
.highlight .sr {
|
||||
color: #009926;
|
||||
}
|
||||
|
||||
.highlight .s1 {
|
||||
color: #d14;
|
||||
}
|
||||
|
||||
.highlight .ss {
|
||||
color: #990073;
|
||||
}
|
||||
|
||||
.highlight .s,
|
||||
.highlight .sa,
|
||||
.highlight .dl {
|
||||
color: #d14;
|
||||
}
|
||||
|
||||
.highlight .na {
|
||||
color: #008080;
|
||||
}
|
||||
|
||||
.highlight .bp {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.highlight .nb {
|
||||
color: #0086B3;
|
||||
}
|
||||
|
||||
.highlight .nc {
|
||||
color: #445588;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.highlight .no {
|
||||
color: #008080;
|
||||
}
|
||||
|
||||
.highlight .nd {
|
||||
color: #3c5d5d;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.highlight .ni {
|
||||
color: #800080;
|
||||
}
|
||||
|
||||
.highlight .ne {
|
||||
color: #990000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.highlight .nf,
|
||||
.highlight .fm {
|
||||
color: #990000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.highlight .nl {
|
||||
color: #990000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.highlight .nn {
|
||||
color: #555555;
|
||||
}
|
||||
|
||||
.highlight .nt {
|
||||
color: #000080;
|
||||
}
|
||||
|
||||
.highlight .vc {
|
||||
color: #008080;
|
||||
}
|
||||
|
||||
.highlight .vg {
|
||||
color: #008080;
|
||||
}
|
||||
|
||||
.highlight .vi {
|
||||
color: #008080;
|
||||
}
|
||||
|
||||
.highlight .nv,
|
||||
.highlight .vm {
|
||||
color: #008080;
|
||||
}
|
||||
|
||||
.highlight .ow {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.highlight .o {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.highlight .w {
|
||||
color: #bbbbbb;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
|
||||
/* Code Blocks */
|
||||
.highlighter-rouge {
|
||||
padding: 2px 1rem;
|
||||
border-radius: 5px;
|
||||
background-color: white;
|
||||
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.highlighter-rouge * {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
/* Inline Code */
|
||||
code.highlighter-rouge {
|
||||
padding: 2px 6px;
|
||||
background-color: rgba(0, 0, 0, 0.07);
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
|
||||
.dialog-buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
margin-top: 6rem;
|
||||
}
|
||||
|
||||
.inline-button {
|
||||
display: inline-block;
|
||||
font-weight: 900;
|
||||
font-size: 90%;
|
||||
padding: .4rem 1rem;
|
||||
border-radius: var(--rounded-corner);
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
color: var(--dark5);
|
||||
}
|
|
@ -15,7 +15,7 @@
|
|||
<caption>Composition</caption>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
<url type="homepage">https://gitlab.freedesktop.org/dabrain34/GstPipelineStudio</url>
|
||||
<url type="homepage">https://dabrain34.pages.freedesktop.org/GstPipelineStudio</url>
|
||||
<url type="bugtracker">https://gitlab.freedesktop.org/dabrain34/GstPipelineStudio/issues</url>
|
||||
<categories>
|
||||
<category>Audio</category>
|
||||
|
@ -27,7 +27,17 @@
|
|||
<translation type="gettext">@gettext-package@</translation>
|
||||
<launchable type="desktop-id">@app-id@.desktop</launchable>
|
||||
<releases>
|
||||
<release version="@version@" date="@current_date@"/>
|
||||
<release version="@version@" date="@current_date@">
|
||||
<description>
|
||||
<p>Welcome to GstPipelineStudio</p>
|
||||
<ul>
|
||||
<li>logs: receive multiple log sources such as GST logs and messages.</li>
|
||||
<li>settings: add a log level selection</li>
|
||||
<li>rename gst_pipeline_studio to gst-pipeline-studio</li>
|
||||
<li>can open a pipeline from the command line</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
</releases>
|
||||
<content_rating type="oars-1.1">
|
||||
<content_attribute id="violence-cartoon">none</content_attribute>
|
||||
|
|
|
@ -3,7 +3,7 @@ Name=GstPipelineStudio
|
|||
GenericName=GPS
|
||||
Comment=A GUI for GStreamer
|
||||
Type=Application
|
||||
Exec=gst_pipeline_studio
|
||||
Exec=gst-pipeline-studio
|
||||
Terminal=false
|
||||
Categories=AudioVideo;Audio;Video;Midi;Settings;GNOME;GTK;
|
||||
Icon=@icon@
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 267 KiB After Width: | Height: | Size: 210 KiB |
53
index.html
Normal file
53
index.html
Normal file
|
@ -0,0 +1,53 @@
|
|||
<html>
|
||||
< <head>
|
||||
<title>GstPipelineStudio</title>
|
||||
<link rel="stylesheet" href="data/css/style.css">
|
||||
<link rel="icon" href="data/icons/org.freedesktop.dabrain34.GstPipelineStudio.ico">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1 id="logo"></h1>
|
||||
<h1>GstPipelineStudio: Draw your own pipeline ...</h1>
|
||||
<p><img src="data/screenshots/gps_screenshot.png" alt=""></p>
|
||||
<p>GstPipelineStudio aims to provide a graphical user interface to the GStreamer framework. From a first
|
||||
step in the framework with a simple pipeline to a complex pipeline debugging, the tool provides a
|
||||
friendly interface to add elements to a pipeline and debug it.</p>
|
||||
<h2>GstPipelineStudio 0.3.5 is out, checkout the <a
|
||||
href="https://gitlab.freedesktop.org/dabrain34/GstPipelineStudio/-/blob/main/ChangeLog.md?ref_type=heads#anchor-033">
|
||||
Release Notes</a> !
|
||||
<br>
|
||||
<br>
|
||||
<p>Download:</p>
|
||||
<ul>
|
||||
<li><a href="https://flathub.org/apps/org.freedesktop.dabrain34.GstPipelineStudio">Flathub</a></li>
|
||||
<li><a
|
||||
href="https://gitlab.freedesktop.org/dabrain34/GstPipelineStudio/uploads/0ab4098a9385f21d72881d27530e6e27/GstPipelineStudio-0.3.5.msi">Windows
|
||||
MSI</a>
|
||||
|
||||
</li>
|
||||
<li><a
|
||||
href="https://gitlab.freedesktop.org/dabrain34/GstPipelineStudio/uploads/d6d703809e3023de04e7ad1449fcb4aa/GstPipelineStudio-0.3.5.dmg">MacOS</a>
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="getting-gps">Contributing to GstPipelineStudio</h2>
|
||||
<p>If you want to get the the code from Gitlab, you can visit this <a
|
||||
href="https://flathub.org/apps/org.freedesktop.dabrain34.GstPipelineStudio">webpage</a>
|
||||
and follow the <a
|
||||
href="https://flathub.org/apps/org.freedesktop.dabrain34.GstPipelineStudio/README.md">README</a>
|
||||
to build your own version of GstPipelineStudio.
|
||||
</p>
|
||||
|
||||
|
||||
</h2>
|
||||
</body>
|
||||
<footer class="site-footer">
|
||||
<p>© GstPipelineStudio, 2023</p>
|
||||
|
||||
<p><a href="https://gitlab.freedesktop.org/dabrain34/GstPipelineStudio">Website source</a></p>
|
||||
</footer>
|
||||
|
||||
|
||||
</html>
|
|
@ -9,7 +9,7 @@ test_ok() {
|
|||
|
||||
}
|
||||
|
||||
# depenency library:
|
||||
# dependency library:
|
||||
# Make a .app file: https://gist.github.com/oubiwann/453744744da1141ccc542ff75b47e0cf
|
||||
# Make a .dmg file: https://github.com/LinusU/node-appdmg
|
||||
# Can't find library: https://www.jianshu.com/p/441a7553700f
|
||||
|
@ -25,14 +25,15 @@ GSTREAMER_OPTS="
|
|||
-Dforce_fallback_for=gstreamer-1.0,libffi,pcre2 \
|
||||
-Dgstreamer-1.0:libav=disabled \
|
||||
-Dgstreamer-1.0:examples=disabled \
|
||||
-Dgstreamer-1.0:introspection=disabled \
|
||||
-Dgstreamer-1.0:introspection=disabled \
|
||||
-Dgstreamer-1.0:rtsp_server=disabled \
|
||||
-Dgstreamer-1.0:devtools=disabled \
|
||||
-Dgst-plugins-base:tests=disabled \
|
||||
-Dgstreamer-1.0:tests=disabled \
|
||||
-Dgst-plugins-bad:openexr=disabled -Dgstreamer-1.0:gst-examples=disabled \
|
||||
-Dorc:gtk_doc=disabled \
|
||||
-Dgstreamer-1.0:python=disabled"
|
||||
-Dgst-plugins-base:tests=disabled \
|
||||
-Dgstreamer-1.0:tests=disabled \
|
||||
-Dgst-plugins-bad:openexr=disabled -Dgstreamer-1.0:gst-examples=disabled \
|
||||
-Dorc:gtk_doc=disabled \
|
||||
-Dgstreamer-1.0:ges=disabled \
|
||||
-Dgstreamer-1.0:python=disabled"
|
||||
|
||||
# rebuild app release version
|
||||
rm -rf "${TARGETDIR}"
|
||||
|
@ -64,7 +65,7 @@ function lib_dependency_copy
|
|||
if [[ '@loader_path' == ${lib:0:12} ]]; then
|
||||
cp -n "${lib/@loader_path/$lib_dir}" $folder
|
||||
else
|
||||
echo "Unsupport path: $lib"
|
||||
echo "Unsupported path: $lib"
|
||||
fi
|
||||
else
|
||||
if [[ $lib != $target ]]; then
|
||||
|
@ -93,10 +94,10 @@ function lib_dependency_analyze
|
|||
# copy app dependency library to target dir
|
||||
echo -n "Copy app dependency library......"
|
||||
|
||||
|
||||
lib_dependency_copy ${TARGETDIR}/bin/gst_pipeline_studio "${TARGETDIR}/bin"
|
||||
lib_dependency_copy ${TARGETDIR}/bin/gst-pipeline-studio "${TARGETDIR}/bin"
|
||||
lib_dependency_copy ${TARGETDIR}/lib/libgobject-2.0.0.dylib "${TARGETDIR}/bin"
|
||||
lib_dependency_copy ${TARGETDIR}/lib/libsoup-2.4.1.dylib "${TARGETDIR}/bin"
|
||||
lib_dependency_copy "${TARGETDIR}/bin/libgtk-4.1.dylib" "${TARGETDIR}/bin"
|
||||
|
||||
|
||||
for file in ${TARGETDIR}/lib/gstreamer-1.0/*.dylib
|
||||
|
@ -107,12 +108,7 @@ done
|
|||
|
||||
test_ok cp -f "${PROJECTDIR}/macos/mac_launcher.sh" "${TARGETDIR}/bin/launcher.sh"
|
||||
|
||||
|
||||
# copy GStreamer dependencies
|
||||
# cp -f /usr/local/lib/gstreamer-1.0/libgtk-gtk4.1.dylib "${TARGETDIR}/lib/gstreamer-1.0"
|
||||
|
||||
|
||||
|
||||
# FIXME should build and install gtk4 instead of using homebrew
|
||||
|
||||
# copy GTK runtime dependencies resource
|
||||
# echo -n "Copy GTK runtime resource......"
|
||||
|
@ -122,8 +118,9 @@ test_ok cp -f "${PROJECTDIR}/macos/mac_launcher.sh" "${TARGETDIR}/bin/launcher.s
|
|||
# cp -rf /usr/local/lib/girepository-1.0 "${TARGETDIR}/lib/"
|
||||
# cp -rf /usr/local/lib/libgda-5.0 "${TARGETDIR}/lib/"
|
||||
# # Avoid override the latest locale file
|
||||
# cp -r /usr/local/share/locale "${TARGETDIR}/share/"
|
||||
# cp -rf /usr/local/share/icons "${TARGETDIR}/share/"
|
||||
cp -r /opt/homebrew/share/locale "${TARGETDIR}/share/"
|
||||
cp -rf /opt/homebrew/share/icons "${TARGETDIR}/share/"
|
||||
cp -rf /opt/homebrew/share/gtk4-0 "${TARGETDIR}/share/"
|
||||
# cp -rf /usr/local/share/fontconfig "${TARGETDIR}/share/"
|
||||
# cp -rf /usr/local/share/themes/Mac "${TARGETDIR}/share/themes/"
|
||||
# cp -rf /usr/local/share/themes/Default "${TARGETDIR}/share/themes/"
|
||||
|
@ -131,9 +128,9 @@ test_ok cp -f "${PROJECTDIR}/macos/mac_launcher.sh" "${TARGETDIR}/bin/launcher.s
|
|||
# glib-compile-schemas /usr/local/share/glib-2.0/schemas
|
||||
# cp -f /usr/local/share/glib-2.0/schemas/gschema* "${TARGETDIR}/share/glib-2.0/schemas"
|
||||
# # find "${TARGETDIR}/bin" -type f -path '*.dll.a' -exec rm '{}' \;
|
||||
# lib_dependency_analyze ${TARGETDIR}/lib ${TARGETDIR}/bin
|
||||
# lib_dependency_analyze ${TARGETDIR}/bin ${TARGETDIR}/bin
|
||||
# echo "[done]"
|
||||
lib_dependency_analyze ${TARGETDIR}/lib ${TARGETDIR}/bin
|
||||
lib_dependency_analyze ${TARGETDIR}/bin ${TARGETDIR}/bin
|
||||
echo "[done]"
|
||||
|
||||
# copy app icons and license files to target dir
|
||||
echo -n "Copy app icon(svg) files......"
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<key>GtkOSXLaunchScriptFile</key>
|
||||
<string>launcher.sh</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>gst_pipeline_studio</string>
|
||||
<string>gst-pipeline-studio</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>GstPipelineStudio.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
|
@ -38,6 +38,6 @@
|
|||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -93,10 +93,10 @@ cp -rf "$APP_BUILD/etc" "$APP_RES_DIR"
|
|||
cp -rf "$APP_BUILD/lib" "$APP_RES_DIR"
|
||||
cp -rf "$APP_BUILD/share" "$APP_RES_DIR"
|
||||
cp -rf "$APP_BUILD/libexec" "$APP_RES_DIR"
|
||||
cp $APP_BUILD/bin/gst_pipeline_studio $APP_EXE_DIR/gst_pipeline_studio-real
|
||||
cp $APP_BUILD/bin/launcher.sh $APP_EXE_DIR/gst_pipeline_studio
|
||||
chmod 766 "$APP_EXE_DIR/gst_pipeline_studio"
|
||||
chmod 766 "$APP_EXE_DIR/gst_pipeline_studio-real"
|
||||
cp $APP_BUILD/bin/gst-pipeline-studio $APP_EXE_DIR/gst-pipeline-studio-real
|
||||
cp $APP_BUILD/bin/launcher.sh $APP_EXE_DIR/gst-pipeline-studio
|
||||
chmod 766 "$APP_EXE_DIR/gst-pipeline-studio"
|
||||
chmod 766 "$APP_EXE_DIR/gst-pipeline-studio-real"
|
||||
chmod -R 766 "$APP_RES_DIR"/libexec/gstreamer-1.0
|
||||
|
||||
|
||||
|
@ -123,7 +123,7 @@ done
|
|||
lib_change_paths \
|
||||
@executable_path/../Resources/lib \
|
||||
$APP_LIB_DIR \
|
||||
$APP_EXE_DIR/gst_pipeline_studio-real
|
||||
$APP_EXE_DIR/gst-pipeline-studio-real
|
||||
|
||||
|
||||
lib_change_siblings $APP_LIB_DIR @loader_path
|
||||
|
|
|
@ -31,7 +31,7 @@ function lib_change_path
|
|||
# This is a simple wrapper around install_name_tool to reduce the
|
||||
# number of arguments (like $source does not have to be provided
|
||||
# here as it can be deducted from $target).
|
||||
# Also, the requested change can be applied to multipe binaries
|
||||
# Also, the requested change can be applied to multiple binaries
|
||||
# at once since 2-n arguments can be supplied.
|
||||
|
||||
local target=$1 # new path to dynamically linked library
|
||||
|
@ -99,7 +99,7 @@ function lib_change_siblings
|
|||
lib_change_path $target/$(basename $linked_lib) $lib
|
||||
fi
|
||||
done
|
||||
done
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
|
@ -167,4 +167,4 @@ if /bin/expr "x$1" : '^x-psn_' > /dev/null; then
|
|||
shift 1
|
||||
fi
|
||||
|
||||
$EXEC "$bundle_contents/MacOS/gst_pipeline_studio-real" "$@" $EXTRA_ARGS
|
||||
$EXEC "$bundle_contents/MacOS/gst-pipeline-studio-real" "$@" $EXTRA_ARGS
|
||||
|
|
|
@ -78,7 +78,7 @@ This is a tricky question. We believe software patents should not exist, so that
|
|||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\hich\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1031\sl276\slmult1\sb0\sa200{\b0\rtlch \ltrch\loch\fs24\lang255\loch\f6
|
||||
Software patents are widely available in the USA. Despite they are formally prohibited in the European Union, they indeed are granted by the thousand by the European Patent Office, and also some national patent offices follow the same path. In other countries they are not available.}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\hich\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1031\sl276\slmult1\sb0\sa200{\b0\rtlch \ltrch\loch\fs24\lang255\loch\f6
|
||||
Since patent protection is a national state-granted monopoly, distributing software that violates patents in a given country could be entirely safe if done in another country. Fair use exceptions also exist. So we cannot advice you whether the software we provide would be considered violating patents in your country or in any other country, but that can be said for virtually all kinds of sofware. Only, since we deal with audio-video standards, and these standards are by and large designed to use certain patented technologies, it is common wisdom that the pieces of software that implement these standards are sensitive in this respect.}
|
||||
Since patent protection is a national state-granted monopoly, distributing software that violates patents in a given country could be entirely safe if done in another country. Fair use exceptions also exist. So we cannot advice you whether the software we provide would be considered violating patents in your country or in any other country, but that can be said for virtually all kinds of software. Only, since we deal with audio-video standards, and these standards are by and large designed to use certain patented technologies, it is common wisdom that the pieces of software that implement these standards are sensitive in this respect.}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\hich\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1031\sl276\slmult1\sb0\sa200{\b0\rtlch \ltrch\loch\fs24\lang255\loch\f6
|
||||
This is why GStreamer has taken a modular approach, so that you can use a Free plugins or a proprietary, patent royalty bearing, plugin for a given standard.}
|
||||
\par \pard\plain \s0\nowidctlpar{\*\hyphen2\hyphlead2\hyphtrail2\hyphmax0}\cf0\kerning1\hich\af7\langfe2052\dbch\af8\afs24\alang1081\loch\f3\fs24\lang1031\sl276\slmult1\sb0\sa200{\b\rtlch \ltrch\loch\fs36\lang255\loch\f6
|
||||
|
|
|
@ -33,13 +33,15 @@ try
|
|||
# GST and GTK are installed in this folder by prepare_gstreamer.ps1.
|
||||
# GST and GTK are built by the docker image.
|
||||
$gstreamerInstallDir="c:\gst-install-clean"
|
||||
$gstreamerBinInstallDir= Join-Path $gstreamerInstallDir -ChildPath "bin/"
|
||||
$gstreamerPluginInstallDir= Join-Path $gstreamerInstallDir -ChildPath "lib\gstreamer-1.0"
|
||||
$gstreamerBinInstallDir= Join-Path $gstreamerInstallDir -ChildPath "bin"
|
||||
$gstreamerPluginInstallDir= Join-Path $gstreamerInstallDir -ChildPath "lib"
|
||||
$gstreamerShareInstallDir= Join-Path $gstreamerInstallDir -ChildPath "share"
|
||||
|
||||
& "$heatToolPath" dir "$gstreamerBinInstallDir" -gg -sfrag -template:fragment -out gstreamer-1.0.wxs -cg "_gstreamer" -var var.gstreamerBinInstallDir -dr INSTALLFOLDER
|
||||
& "$heatToolPath" dir "$gstreamerPluginInstallDir" -gg -sfrag -template:fragment -out gstreamer-plugins-1.0.wxs -cg "_gstreamer_plugins" -var var.gstreamerPluginInstallDir -dr INSTALLFOLDER
|
||||
& "$heatToolPath" dir "$gstreamerShareInstallDir" -v -ke -gg -sfrag -template:fragment -out gstreamer-share-1.0.wxs -cg "_gstreamer_share" -var var.gstreamerShareInstallDir -dr INSTALLFOLDER
|
||||
|
||||
$files = "gps gstreamer-1.0 gstreamer-plugins-1.0"
|
||||
$files = "gps gstreamer-1.0 gstreamer-plugins-1.0 gstreamer-share-1.0"
|
||||
$wxs_files = @()
|
||||
$obj_files = @()
|
||||
foreach ($f in $files.split(" ")){
|
||||
|
@ -51,7 +53,7 @@ try
|
|||
# compiling wxs file into wixobj
|
||||
$msiFileName = "GstPipelineStudio-$GPSVersion.msi"
|
||||
foreach ($f in $wxs_files){
|
||||
& "$candleToolPath" "$f" -dPlatform=x64 -dGPSUpgradeCode="$GPSUpgradeCode" -dGPSVersion="$GPSVersion" -dgstreamerBinInstallDir="$gstreamerBinInstallDir" -dgstreamerPluginInstallDir="$gstreamerPluginInstallDir"
|
||||
& "$candleToolPath" "$f" -dPlatform=x64 -dGPSUpgradeCode="$GPSUpgradeCode" -dGPSVersion="$GPSVersion" -dgstreamerBinInstallDir="$gstreamerBinInstallDir" -dgstreamerPluginInstallDir="$gstreamerPluginInstallDir" -dgstreamerShareInstallDir="$gstreamerShareInstallDir"
|
||||
if($LASTEXITCODE -ne 0)
|
||||
{
|
||||
throw "Compilation of $wxsFileName failed with exit code $LASTEXITCODE"
|
||||
|
|
|
@ -3,6 +3,6 @@ set MYDIR=%~dp0
|
|||
setlocal
|
||||
set PATH=%MYDIR%bin;%PATH%
|
||||
echo %PATH%
|
||||
set GST_PLUGIN_PATH=%MYDIR%/gstreamer-1.0
|
||||
set GST_PLUGIN_PATH=%MYDIR%\lib\gstreamer-1.0
|
||||
echo %GST_PLUGIN_PATH%
|
||||
gst_pipeline_studio.exe
|
||||
gst-pipeline-studio.exe
|
|
@ -10,10 +10,12 @@
|
|||
<Package InstallScope="perMachine" Compressed="yes" />
|
||||
|
||||
<MajorUpgrade DowngradeErrorMessage="A later version of [ProductName] is already installed. Setup will now exit." />
|
||||
<WixVariable Id="WixUIBannerBmp" Value="wixbanner.bmp" />
|
||||
<WixVariable Id="WixUIDialogBmp" Value="wixdialog.bmp" />
|
||||
|
||||
<MediaTemplate EmbedCab="yes" />
|
||||
|
||||
<UIRef Id="WixUI_Mondo" />
|
||||
<UIRef Id="WixUI_InstallDir" />
|
||||
<WixVariable Id="WixUILicenseRtf" Value="LICENSE.rtf" />
|
||||
|
||||
<Directory Id="TARGETDIR" Name="SourceDir">
|
||||
|
@ -59,6 +61,7 @@
|
|||
<ComponentRef Id="ProductComponent" />
|
||||
<ComponentGroupRef Id="_gstreamer" />
|
||||
<ComponentGroupRef Id="_gstreamer_plugins" />
|
||||
<ComponentGroupRef Id="_gstreamer_share" />
|
||||
<ComponentRef Id="UninstallShortcut" />
|
||||
</Feature>
|
||||
|
||||
|
|
|
@ -9,3 +9,4 @@ Copy-Item -Path C:\gst-install\bin\*.exe -Destination c:\gst-install-clean\bin\
|
|||
New-Item c:\gst-install-clean\lib\gstreamer-1.0 -ItemType Directory
|
||||
Copy-Item -Path C:\gst-install\lib\gstreamer-1.0\*.dll -Destination c:\gst-install-clean\lib\gstreamer-1.0
|
||||
|
||||
Copy-Item -Path C:\gst-install\share -Destination c:\gst-install-clean\ -Recurse
|
||||
|
|
BIN
installer/wix/wixbanner.bmp
Normal file
BIN
installer/wix/wixbanner.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 112 KiB |
BIN
installer/wix/wixdialog.bmp
Normal file
BIN
installer/wix/wixdialog.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 507 KiB |
12
meson.build
12
meson.build
|
@ -1,6 +1,6 @@
|
|||
project('gst_pipeline_studio',
|
||||
version: '0.3.2',
|
||||
meson_version: '>= 0.50.0',
|
||||
project('gst-pipeline-studio',
|
||||
version: '0.3.5',
|
||||
meson_version: '>= 0.63.0',
|
||||
default_options: [ 'warning_level=2',
|
||||
],
|
||||
license: 'GPL-3.0-or-later',
|
||||
|
@ -36,7 +36,7 @@ localedir = prefix / get_option('localedir')
|
|||
datadir = prefix / get_option('datadir')
|
||||
pkgdatadir = datadir / meson.project_name()
|
||||
iconsdir = datadir / 'icons'
|
||||
podir = meson.source_root () / 'po'
|
||||
podir = meson.project_source_root () / 'po'
|
||||
gettext_package = meson.project_name()
|
||||
base_id = 'org.freedesktop.dabrain34.GstPipelineStudio'
|
||||
|
||||
|
@ -70,8 +70,8 @@ subdir('po')
|
|||
|
||||
meson.add_dist_script(
|
||||
'build-aux/dist-vendor.sh',
|
||||
meson.source_root(),
|
||||
join_paths(meson.build_root(), 'meson-dist', meson.project_name() + '-' + version)
|
||||
meson.project_source_root(),
|
||||
join_paths(meson.project_build_root(), 'meson-dist', meson.project_name() + '-' + version)
|
||||
)
|
||||
if host_system == 'linux'
|
||||
meson.add_install_script('build-aux/meson/postinstall.py')
|
||||
|
|
17
release.md
17
release.md
|
@ -3,15 +3,22 @@
|
|||
- Update to the given version:
|
||||
- meson.build
|
||||
- cargo.toml
|
||||
- installer/wix/build_installer.ps1
|
||||
- VERSION
|
||||
- index.html
|
||||
- And rebuild to regenerate the cargo.lock
|
||||
- update the changelog in org.freedesktop.dabrain34.GstPipelineStudio.appdata.xml.in.in within release/description
|
||||
|
||||
- create a tag on gitlab
|
||||
- meson builddir -Dbuildtype=release
|
||||
- ninja -C builddir dist
|
||||
- upload the package and the sha256 to gitlab for Flatpak in the release notes
|
||||
- Fetch the package from the `linux release` job or you can make it manually with:
|
||||
- meson builddir -Dbuildtype=release
|
||||
- ninja -C builddir dist
|
||||
- Upload the package .xz file and the sha256 to gitlab release page in the release notes
|
||||
see https://gitlab.freedesktop.org/dabrain34/GstPipelineStudio/-/releases/0.3.2
|
||||
|
||||
# flathub
|
||||
|
||||
https://github.com/flathub/org.freedesktop.dabrain34.GstPipelineStudio
|
||||
|
||||
- Need to update the package and the sha256 generated by ninja -C builddir dist
|
||||
- Need to update the package and the sha256 from the release page, ie https://gitlab.freedesktop.org/dabrain34/GstPipelineStudio/-/releases/0.3.2
|
||||
- Create a pull request with the package update
|
||||
- Wait at lest 2-3 hours after merging to get the update available.
|
||||
|
|
79
src/app.rs
79
src/app.rs
|
@ -105,12 +105,14 @@ impl GPSApp {
|
|||
}));
|
||||
let settings = Settings::load_settings();
|
||||
|
||||
app.window
|
||||
.set_default_size(settings.app_width, settings.app_height);
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
if settings.app_maximized {
|
||||
app.window.maximize();
|
||||
} else {
|
||||
app.window
|
||||
.set_size_request(settings.app_width, settings.app_height);
|
||||
}
|
||||
|
||||
app.set_paned_position(&settings, "graph_dashboard-paned", 100);
|
||||
app.set_paned_position(&settings, "graph_logs-paned", 100);
|
||||
app.set_paned_position(&settings, "elements_preview-paned", 100);
|
||||
|
@ -148,7 +150,7 @@ impl GPSApp {
|
|||
.insert(paned_name.to_string(), paned.position());
|
||||
}
|
||||
|
||||
pub fn on_startup(application: >k::Application) {
|
||||
pub fn on_startup(application: >k::Application, pipeline_desc: &String) {
|
||||
// Create application and error out if that fails for whatever reason
|
||||
let app = match GPSApp::new(application) {
|
||||
Ok(app) => app,
|
||||
|
@ -158,13 +160,7 @@ impl GPSApp {
|
|||
}
|
||||
};
|
||||
|
||||
// When the application is activated show the UI. This happens when the first process is
|
||||
// started, and in the first process whenever a second process is started
|
||||
let app_weak = app.downgrade();
|
||||
application.connect_activate(glib::clone!(@weak application => move |_| {
|
||||
let app = upgrade_weak!(app_weak);
|
||||
app.build_ui(&application);
|
||||
}));
|
||||
app.build_ui(application, pipeline_desc);
|
||||
|
||||
let app_weak = app.downgrade();
|
||||
let slider: gtk::Scale = app
|
||||
|
@ -227,8 +223,8 @@ impl GPSApp {
|
|||
.expect("Couldn't get the main window");
|
||||
let mut settings = Settings::load_settings();
|
||||
settings.app_maximized = window.is_maximized();
|
||||
settings.app_width = window.width();
|
||||
settings.app_height = window.height();
|
||||
settings.app_width = window.default_width();
|
||||
settings.app_height = window.default_height();
|
||||
app.save_paned_position(&mut settings, "graph_dashboard-paned");
|
||||
app.save_paned_position(&mut settings, "graph_logs-paned");
|
||||
app.save_paned_position(&mut settings, "elements_preview-paned");
|
||||
|
@ -447,23 +443,29 @@ impl GPSApp {
|
|||
notebook_preview.set_current_page(Some(n_video_sink as u32));
|
||||
}
|
||||
|
||||
pub fn build_ui(&self, application: &Application) {
|
||||
pub fn build_ui(&self, application: &Application, pipeline_desc: &String) {
|
||||
graphbook::setup_graphbook(self);
|
||||
graphbook::create_graphtab(self, 0, None);
|
||||
let (ready_tx, ready_rx) = async_channel::unbounded::<(logger::LogType, String)>();
|
||||
|
||||
// Setup the logger to get messages into the TreeView
|
||||
let (ready_tx, ready_rx) = glib::MainContext::channel(glib::Priority::DEFAULT);
|
||||
let app_weak = self.downgrade();
|
||||
|
||||
logger::init_logger(
|
||||
ready_tx,
|
||||
Settings::default_log_file_path()
|
||||
ready_tx.clone(),
|
||||
Settings::log_file_path()
|
||||
.to_str()
|
||||
.expect("Unable to convert log file path to a string"),
|
||||
);
|
||||
GPSUI::logger::setup_logger_list(self);
|
||||
let _ = ready_rx.attach(None, move |msg: String| {
|
||||
let app = upgrade_weak!(app_weak, glib::ControlFlow::Break);
|
||||
GPSUI::logger::add_to_logger_list(&app, &msg);
|
||||
logger::init_msg_logger(ready_tx);
|
||||
GPSUI::logger::setup_logger_list(self, "treeview-app-logger", logger::LogType::App);
|
||||
GPSUI::logger::setup_logger_list(self, "treeview-msg-logger", logger::LogType::Message);
|
||||
GPSUI::logger::setup_logger_list(self, "treeview-gst-logger", logger::LogType::Gst);
|
||||
let app_weak = self.downgrade();
|
||||
glib::spawn_future_local(async move {
|
||||
while let Ok(msg) = ready_rx.recv().await {
|
||||
let app = upgrade_weak!(app_weak, glib::ControlFlow::Break);
|
||||
GPSUI::logger::add_to_logger_list(&app, msg.0, &msg.1);
|
||||
}
|
||||
glib::ControlFlow::Continue
|
||||
});
|
||||
|
||||
|
@ -509,8 +511,9 @@ impl GPSApp {
|
|||
&Settings::recent_pipeline_description(),
|
||||
&app,
|
||||
move |app, pipeline_desc| {
|
||||
app.load_pipeline(&pipeline_desc)
|
||||
.unwrap_or_else(|_| GPS_ERROR!("Unable to open file {}", pipeline_desc));
|
||||
app.load_pipeline(&pipeline_desc).unwrap_or_else(|_| {
|
||||
GPS_ERROR!("Unable to open pipeline description {}", pipeline_desc)
|
||||
});
|
||||
Settings::set_recent_pipeline_description(&pipeline_desc);
|
||||
},
|
||||
);
|
||||
|
@ -602,17 +605,22 @@ impl GPSApp {
|
|||
GPSUI::elements::setup_favorite_list(self);
|
||||
// Setup the favorite list
|
||||
GPSUI::elements::setup_elements_list(self);
|
||||
|
||||
let _ = self
|
||||
.load_graph(
|
||||
Settings::default_graph_file_path()
|
||||
.to_str()
|
||||
.expect("Unable to convert to string"),
|
||||
true,
|
||||
)
|
||||
.map_err(|_e| {
|
||||
GPS_WARN!("Unable to load default graph");
|
||||
if pipeline_desc.is_empty() {
|
||||
let _ = self
|
||||
.load_graph(
|
||||
Settings::graph_file_path()
|
||||
.to_str()
|
||||
.expect("Unable to convert to string"),
|
||||
true,
|
||||
)
|
||||
.map_err(|_e| {
|
||||
GPS_WARN!("Unable to load default graph");
|
||||
});
|
||||
} else {
|
||||
self.load_pipeline(pipeline_desc).unwrap_or_else(|_| {
|
||||
GPS_ERROR!("Unable to open pipeline description {}", pipeline_desc)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Downgrade to a weak reference
|
||||
|
@ -773,9 +781,10 @@ impl GPSApp {
|
|||
|
||||
fn load_pipeline(&self, pipeline_desc: &str) -> anyhow::Result<()> {
|
||||
let graphtab = graphbook::current_graphtab(self);
|
||||
let pd_parsed = pipeline_desc.replace('\\', "");
|
||||
graphtab
|
||||
.player()
|
||||
.graphview_from_pipeline_description(&graphtab.graphview(), pipeline_desc);
|
||||
.graphview_from_pipeline_description(&graphtab.graphview(), &pd_parsed);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
pub static APP_ID: &str = @APP_ID@;
|
||||
pub static VERSION: &str = @VERSION@;
|
||||
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
|
|
@ -156,13 +156,31 @@ impl ElementInfo {
|
|||
}
|
||||
|
||||
pub fn element_property(element: &gst::Element, property_name: &str) -> anyhow::Result<String> {
|
||||
let value = element
|
||||
.property_value(property_name)
|
||||
.transform::<String>()
|
||||
.expect("Unable to transform to string")
|
||||
.get::<String>()
|
||||
.unwrap_or_default();
|
||||
Ok(value)
|
||||
let value = element.property_value(property_name);
|
||||
if value.type_().is_a(glib::Type::ENUM) {
|
||||
let value = value.get::<&glib::EnumValue>().unwrap().nick().to_string();
|
||||
Ok(value)
|
||||
} else if value.type_().is_a(glib::Type::FLAGS) {
|
||||
let value = value.get::<Vec<&glib::FlagsValue>>().unwrap();
|
||||
let flags = value.iter().copied().fold(0, |acc, val| acc | val.value());
|
||||
Ok(flags.to_string())
|
||||
} else if value.type_().is_a(glib::Type::F64) || value.type_().is_a(glib::Type::F32) {
|
||||
let value = value
|
||||
.transform::<String>()
|
||||
.expect("Unable to transform to string")
|
||||
.get::<String>()
|
||||
.unwrap()
|
||||
.replace(',', ".");
|
||||
Ok(value)
|
||||
} else {
|
||||
let value = value
|
||||
.transform::<String>()
|
||||
.expect("Unable to transform to string")
|
||||
.get::<String>()
|
||||
.unwrap_or_default()
|
||||
.to_lowercase();
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn element_property_by_feature_name(
|
||||
|
@ -181,7 +199,7 @@ impl ElementInfo {
|
|||
element: &gst::Element,
|
||||
) -> anyhow::Result<HashMap<String, glib::ParamSpec>> {
|
||||
let mut properties_list = HashMap::new();
|
||||
let params = element.class().list_properties();
|
||||
let params = element.list_properties();
|
||||
|
||||
for param in params.iter() {
|
||||
GPS_INFO!("Property_name {}", param.name());
|
||||
|
@ -258,7 +276,7 @@ impl ElementInfo {
|
|||
None
|
||||
}
|
||||
|
||||
pub fn search_fo_element(bin: &gst::Bin, element_name: &str) -> Vec<gst::Element> {
|
||||
pub fn search_for_element(bin: &gst::Bin, element_name: &str) -> Vec<gst::Element> {
|
||||
let mut iter = bin.iterate_elements();
|
||||
let mut elements: Vec<gst::Element> = Vec::new();
|
||||
elements = loop {
|
||||
|
@ -266,7 +284,7 @@ impl ElementInfo {
|
|||
Ok(Some(element)) => {
|
||||
if element.is::<gst::Bin>() {
|
||||
let bin = element.dynamic_cast::<gst::Bin>().unwrap();
|
||||
let mut bin_elements = ElementInfo::search_fo_element(&bin, element_name);
|
||||
let mut bin_elements = ElementInfo::search_for_element(&bin, element_name);
|
||||
elements.append(&mut bin_elements);
|
||||
} else {
|
||||
GPS_INFO!("Found factory: {}", element.factory().unwrap().name());
|
||||
|
|
|
@ -61,12 +61,35 @@ impl PlayerWeak {
|
|||
}
|
||||
}
|
||||
|
||||
fn gst_log_handler(
|
||||
category: gst::DebugCategory,
|
||||
level: gst::DebugLevel,
|
||||
file: &glib::GStr,
|
||||
function: &glib::GStr,
|
||||
line: u32,
|
||||
_obj: Option<&gst::LoggedObject>,
|
||||
message: &gst::DebugMessage,
|
||||
) {
|
||||
let log_message = format!(
|
||||
"{}\t{}\t{}:{}:{}\t{}",
|
||||
level,
|
||||
category.name(),
|
||||
line,
|
||||
file.as_str(),
|
||||
function.as_str(),
|
||||
message.get().unwrap().as_str()
|
||||
);
|
||||
|
||||
GPS_GST_LOG!("{}", log_message);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PlayerInner {
|
||||
app: RefCell<Option<GPSApp>>,
|
||||
pipeline: RefCell<Option<gst::Pipeline>>,
|
||||
current_state: Cell<PipelineState>,
|
||||
n_video_sink: Cell<usize>,
|
||||
bus_watch_guard: RefCell<Option<gst::bus::BusWatchGuard>>,
|
||||
}
|
||||
|
||||
impl Player {
|
||||
|
@ -76,8 +99,9 @@ impl Player {
|
|||
pipeline: RefCell::new(None),
|
||||
current_state: Cell::new(PipelineState::Stopped),
|
||||
n_video_sink: Cell::new(0),
|
||||
bus_watch_guard: RefCell::new(None),
|
||||
}));
|
||||
|
||||
gst::log::add_log_function(gst_log_handler);
|
||||
Ok(pipeline)
|
||||
}
|
||||
|
||||
|
@ -99,13 +123,13 @@ impl Player {
|
|||
.parse::<bool>()
|
||||
.expect("Should a boolean value")
|
||||
{
|
||||
ElementInfo::element_update_rank("gtk4paintablesink", gst::Rank::Primary);
|
||||
ElementInfo::element_update_rank("gtk4paintablesink", gst::Rank::PRIMARY);
|
||||
} else {
|
||||
ElementInfo::element_update_rank("gtk4paintablesink", gst::Rank::Marginal);
|
||||
ElementInfo::element_update_rank("gtk4paintablesink", gst::Rank::MARGINAL);
|
||||
}
|
||||
|
||||
gst::log::set_threshold_from_string(settings::Settings::gst_log_level().as_str(), true);
|
||||
// Create pipeline from the description
|
||||
let pipeline = gst::parse_launch(description)?;
|
||||
let pipeline = gst::parse::launch(description)?;
|
||||
let pipeline = pipeline.downcast::<gst::Pipeline>();
|
||||
/* start playing */
|
||||
if pipeline.is_err() {
|
||||
|
@ -117,19 +141,21 @@ impl Player {
|
|||
self.check_for_gtk4sink(pipeline.as_ref().unwrap());
|
||||
// GPSApp is not Send(trait) ready , so we use a channel to exchange the given data with the main thread and use
|
||||
// GPSApp.
|
||||
let (ready_tx, ready_rx) = glib::MainContext::channel(glib::Priority::DEFAULT);
|
||||
let (ready_tx, ready_rx) = async_channel::unbounded::<gst::Element>();
|
||||
let player_weak = self.downgrade();
|
||||
let _ = ready_rx.attach(None, move |element: gst::Element| {
|
||||
let player = upgrade_weak!(player_weak, glib::ControlFlow::Break);
|
||||
let paintable = element.property::<gdk::Paintable>("paintable");
|
||||
let n_sink = player.n_video_sink.get();
|
||||
player
|
||||
.app
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.expect("App should be available")
|
||||
.set_app_preview(&paintable, n_sink);
|
||||
player.n_video_sink.set(n_sink + 1);
|
||||
glib::spawn_future_local(async move {
|
||||
while let Ok(element) = ready_rx.recv().await {
|
||||
let player = upgrade_weak!(player_weak, glib::ControlFlow::Break);
|
||||
let paintable = element.property::<gdk::Paintable>("paintable");
|
||||
let n_sink = player.n_video_sink.get();
|
||||
player
|
||||
.app
|
||||
.borrow()
|
||||
.as_ref()
|
||||
.expect("App should be available")
|
||||
.set_app_preview(&paintable, n_sink);
|
||||
player.n_video_sink.set(n_sink + 1);
|
||||
}
|
||||
glib::ControlFlow::Continue
|
||||
});
|
||||
let bin = pipeline.unwrap().dynamic_cast::<gst::Bin>();
|
||||
|
@ -138,7 +164,7 @@ impl Player {
|
|||
if let Some(factory) = element.factory() {
|
||||
GPS_INFO!("Received the signal deep element added {}", factory.name());
|
||||
if factory.name() == "gtk4paintablesink" {
|
||||
let _ = ready_tx.send(element.clone());
|
||||
let _ = ready_tx.try_send(element.clone());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -149,7 +175,7 @@ impl Player {
|
|||
|
||||
pub fn check_for_gtk4sink(&self, pipeline: &gst::Pipeline) {
|
||||
let bin = pipeline.clone().dynamic_cast::<gst::Bin>().unwrap();
|
||||
let gtksinks = ElementInfo::search_fo_element(&bin, "gtk4paintablesink");
|
||||
let gtksinks = ElementInfo::search_for_element(&bin, "gtk4paintablesink");
|
||||
|
||||
for (first_sink, gtksink) in gtksinks.into_iter().enumerate() {
|
||||
let paintable = gtksink.property::<gdk::Paintable>("paintable");
|
||||
|
@ -176,12 +202,13 @@ impl Player {
|
|||
|
||||
let bus = pipeline.bus().expect("Pipeline had no bus");
|
||||
let pipeline_weak = self.downgrade();
|
||||
let _ = bus.add_watch_local(move |_bus, msg| {
|
||||
let bus_watch_guard = bus.add_watch_local(move |_bus, msg| {
|
||||
let pipeline = upgrade_weak!(pipeline_weak, glib::ControlFlow::Break);
|
||||
pipeline.on_pipeline_message(msg);
|
||||
glib::ControlFlow::Continue
|
||||
})?;
|
||||
*self.pipeline.borrow_mut() = Some(pipeline);
|
||||
*self.bus_watch_guard.borrow_mut() = Some(bus_watch_guard);
|
||||
}
|
||||
|
||||
self.set_state(new_state).map_err(|error| {
|
||||
|
@ -279,6 +306,9 @@ impl Player {
|
|||
|
||||
fn on_pipeline_message(&self, msg: &gst::MessageRef) {
|
||||
use gst::MessageView;
|
||||
if let Some(message) = msg.structure() {
|
||||
GPS_MSG_LOG!("{:?}", message);
|
||||
}
|
||||
match msg.view() {
|
||||
MessageView::Eos(_) => {
|
||||
GPS_INFO!("EOS received");
|
||||
|
@ -317,7 +347,7 @@ impl Player {
|
|||
.unwrap()
|
||||
.dynamic_cast::<gst::Bin>()
|
||||
.unwrap();
|
||||
let elements_name: Vec<String> = ElementInfo::search_fo_element(&bin, "")
|
||||
let elements_name: Vec<String> = ElementInfo::search_for_element(&bin, "")
|
||||
.iter()
|
||||
.map(|e| e.factory().unwrap().name().to_string())
|
||||
.collect();
|
||||
|
|
|
@ -221,7 +221,7 @@ pub fn create_graphtab(app: &GPSApp, id: u32, name: Option<&str>) {
|
|||
GPS_DEBUG!("Graph updated id={}", id);
|
||||
let _ = app
|
||||
.save_graph(
|
||||
Settings::default_graph_file_path()
|
||||
Settings::graph_file_path()
|
||||
.to_str()
|
||||
.expect("Unable to convert to string"),
|
||||
)
|
||||
|
@ -244,7 +244,6 @@ pub fn create_graphtab(app: &GPSApp, id: u32, name: Option<&str>) {
|
|||
node.set_tooltip_markup(description.as_deref());
|
||||
if !GPS::ElementInfo::element_factory_exists(&node.name()) {
|
||||
node.set_light(true);
|
||||
node.set_tooltip_markup(description.as_deref());
|
||||
}
|
||||
for port in node.all_ports(GM::PortDirection::All) {
|
||||
let caps = PropertyExt::property(&port,"_caps");
|
||||
|
|
|
@ -492,7 +492,7 @@ impl GraphView {
|
|||
private.id.set(id)
|
||||
}
|
||||
|
||||
/// Retrives the graphview id
|
||||
/// Retrieves the graphview id
|
||||
///
|
||||
pub fn id(&self) -> u32 {
|
||||
let private = imp::GraphView::from_obj(self);
|
||||
|
@ -544,7 +544,7 @@ impl GraphView {
|
|||
let private = imp::GraphView::from_obj(self);
|
||||
node.set_parent(self);
|
||||
|
||||
// Place widgets in colums of 3, growing down
|
||||
// Place widgets in columns of 3, growing down
|
||||
let x = if let Some(node_type) = node.node_type() {
|
||||
match node_type {
|
||||
NodeType::Source => 20.0,
|
||||
|
@ -593,7 +593,7 @@ impl GraphView {
|
|||
}
|
||||
node.unparent();
|
||||
} else {
|
||||
warn!("Tried to remove non-existant node (id={}) from graph", id);
|
||||
warn!("Tried to remove non-existent node (id={}) from graph", id);
|
||||
}
|
||||
self.queue_draw();
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ mod property;
|
|||
mod selection;
|
||||
|
||||
pub use graphview::GraphView;
|
||||
pub use link::Link;
|
||||
pub use node::Node;
|
||||
pub use node::NodeType;
|
||||
pub use port::Port;
|
||||
|
|
|
@ -48,7 +48,7 @@ impl PortDirection {
|
|||
/// Port's presence
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Copy)]
|
||||
pub enum PortPresence {
|
||||
/// Can not be removed from his parent independantly
|
||||
/// Can not be removed from his parent independently
|
||||
Always,
|
||||
/// Can be removed from a node
|
||||
Sometimes,
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use gtk::glib::Sender;
|
||||
use log::{debug, error, info, trace, warn};
|
||||
use simplelog::*;
|
||||
use std::fmt;
|
||||
|
@ -14,6 +13,13 @@ use std::io;
|
|||
|
||||
use std::fs::File;
|
||||
|
||||
use chrono::Local;
|
||||
use std::sync::Mutex;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref MSG_LOGGER: Mutex<Option<MessageLogger>> = Mutex::new(None);
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||
|
||||
pub enum LogLevel {
|
||||
|
@ -25,6 +31,13 @@ pub enum LogLevel {
|
|||
Trace,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum LogType {
|
||||
App,
|
||||
Gst,
|
||||
Message,
|
||||
}
|
||||
|
||||
impl LogLevel {
|
||||
pub fn from_u32(value: u32) -> LogLevel {
|
||||
match value {
|
||||
|
@ -49,7 +62,7 @@ impl fmt::Display for LogLevel {
|
|||
macro_rules! GPS_ERROR (
|
||||
() => ($crate::print!("\n"));
|
||||
($($arg:tt)*) => ({
|
||||
logger::print_log(logger::LogLevel::Error, format_args!($($arg)*).to_string());
|
||||
logger::print_log(logger::LogLevel::Error, format!("{}\t{}",glib::function_name!(),format_args!($($arg)*).to_string()));
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -57,7 +70,7 @@ macro_rules! GPS_ERROR (
|
|||
macro_rules! GPS_WARN (
|
||||
() => ($crate::print!("\n"));
|
||||
($($arg:tt)*) => ({
|
||||
logger::print_log(logger::LogLevel::Warning, format_args!($($arg)*).to_string());
|
||||
logger::print_log(logger::LogLevel::Warning, format!("{}\t{}",glib::function_name!(),format_args!($($arg)*).to_string()));
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -65,7 +78,7 @@ macro_rules! GPS_WARN (
|
|||
macro_rules! GPS_INFO (
|
||||
() => ($crate::print!("\n"));
|
||||
($($arg:tt)*) => ({
|
||||
logger::print_log(logger::LogLevel::Info, format_args!($($arg)*).to_string());
|
||||
logger::print_log(logger::LogLevel::Info, format!("{}\t{}",glib::function_name!(),format_args!($($arg)*).to_string()));
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -73,7 +86,23 @@ macro_rules! GPS_INFO (
|
|||
macro_rules! GPS_DEBUG (
|
||||
() => ($crate::print!("\n"));
|
||||
($($arg:tt)*) => ({
|
||||
logger::print_log(logger::LogLevel::Debug, format_args!($($arg)*).to_string());
|
||||
logger::print_log(logger::LogLevel::Debug, format!("{}\t{}",glib::function_name!(),format_args!($($arg)*).to_string()));
|
||||
})
|
||||
);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! GPS_MSG_LOG (
|
||||
() => ($crate::print!("\n"));
|
||||
($($arg:tt)*) => ({
|
||||
logger::print_msg_logger(logger::LogType::Message, format_args!($($arg)*).to_string());
|
||||
})
|
||||
);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! GPS_GST_LOG (
|
||||
() => ($crate::print!("\n"));
|
||||
($($arg:tt)*) => ({
|
||||
logger::print_msg_logger(logger::LogType::Gst, format_args!($($arg)*).to_string());
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -86,7 +115,7 @@ macro_rules! GPS_TRACE (
|
|||
);
|
||||
|
||||
struct WriteAdapter {
|
||||
sender: Sender<String>,
|
||||
sender: async_channel::Sender<(LogType, String)>,
|
||||
buffer: String,
|
||||
}
|
||||
|
||||
|
@ -97,8 +126,8 @@ impl io::Write for WriteAdapter {
|
|||
.push_str(&String::from_utf8(buf.to_vec()).unwrap());
|
||||
if self.buffer.ends_with('\n') {
|
||||
self.buffer.pop();
|
||||
self.sender.send(self.buffer.clone()).unwrap();
|
||||
self.buffer = String::from("");
|
||||
let _ = self.sender.try_send((LogType::App, self.buffer.clone()));
|
||||
self.buffer.clear();
|
||||
}
|
||||
|
||||
Ok(buf.len())
|
||||
|
@ -120,7 +149,7 @@ fn translate_to_simple_logger(log_level: LogLevel) -> LevelFilter {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn init_logger(sender: Sender<String>, log_file: &str) {
|
||||
pub fn init_logger(sender: async_channel::Sender<(LogType, String)>, log_file: &str) {
|
||||
simplelog::CombinedLogger::init(vec![
|
||||
WriteLogger::new(
|
||||
translate_to_simple_logger(LogLevel::Trace),
|
||||
|
@ -169,3 +198,36 @@ pub fn print_log(log_level: LogLevel, msg: String) {
|
|||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MessageLogger {
|
||||
sender: async_channel::Sender<(LogType, String)>,
|
||||
}
|
||||
|
||||
impl MessageLogger {
|
||||
pub fn new(sender: async_channel::Sender<(LogType, String)>) -> Self {
|
||||
Self { sender }
|
||||
}
|
||||
|
||||
pub fn print_log(&self, log_type: LogType, msg: String) {
|
||||
let to_send = format!("{}\t{}", Local::now().format("%H:%M:%S"), msg);
|
||||
self.sender
|
||||
.try_send((log_type, to_send))
|
||||
.expect("Unable to send the log");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_msg_logger(sender: async_channel::Sender<(LogType, String)>) {
|
||||
let mut msg_logger = MSG_LOGGER.lock().unwrap();
|
||||
if msg_logger.is_none() {
|
||||
// Initialize the variable
|
||||
*msg_logger = Some(MessageLogger::new(sender));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_msg_logger(log_type: LogType, msg: String) {
|
||||
let msg_logger = MSG_LOGGER.lock().unwrap();
|
||||
if let Some(logger) = msg_logger.as_ref() {
|
||||
logger.print_log(log_type, msg);
|
||||
}
|
||||
}
|
||||
|
|
19
src/main.rs
19
src/main.rs
|
@ -22,14 +22,29 @@ use gtk::prelude::*;
|
|||
|
||||
use crate::app::GPSApp;
|
||||
use crate::common::init;
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[derive(StructOpt, Debug)]
|
||||
struct Command {
|
||||
#[structopt(about = "Sets the pipeline description", default_value = "")]
|
||||
pipeline: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// gio::resources_register_include!("compiled.gresource").unwrap();
|
||||
init().expect("Unable to init app");
|
||||
let application = gtk::Application::new(Some(config::APP_ID), Default::default());
|
||||
let application = gtk::Application::new(
|
||||
Some(config::APP_ID),
|
||||
gtk::gio::ApplicationFlags::HANDLES_COMMAND_LINE,
|
||||
);
|
||||
application.connect_startup(|application| {
|
||||
GPSApp::on_startup(application);
|
||||
let args = Command::from_args();
|
||||
GPSApp::on_startup(application, &args.pipeline);
|
||||
});
|
||||
|
||||
application.connect_command_line(|_app, _cmd_line| {
|
||||
// structopt already handled arguments
|
||||
0
|
||||
});
|
||||
application.run();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
conf = configuration_data()
|
||||
conf.set_quoted('APP_ID', application_id)
|
||||
conf.set_quoted('VERSION', version + version_suffix)
|
||||
|
||||
configure_file(
|
||||
input: 'config.rs.in',
|
||||
|
@ -9,7 +8,7 @@ configure_file(
|
|||
)
|
||||
|
||||
# Copy the config.rs output to the source directory.
|
||||
run_command(python3, '-c', 'import shutil; shutil.copyfile("@0@", "@1@")'.format(join_paths(meson.build_root(), 'src', 'config.rs'), join_paths(meson.source_root(), 'src', 'config.rs')))
|
||||
run_command(python3, '-c', 'import shutil; shutil.copyfile("@0@", "@1@")'.format(join_paths(meson.project_build_root(), 'src', 'config.rs'), join_paths(meson.project_source_root(), 'src', 'config.rs')))
|
||||
|
||||
rust_sources = files(
|
||||
'gps/player.rs',
|
||||
|
@ -38,7 +37,7 @@ rust_sources = files(
|
|||
|
||||
sources = [cargo_sources, rust_sources]
|
||||
|
||||
cargo_script = find_program(join_paths(meson.source_root(), 'build-aux/cargo.py'))
|
||||
cargo_script = find_program(join_paths(meson.project_source_root(), 'build-aux/cargo.py'))
|
||||
|
||||
app_name = meson.project_name()
|
||||
if host_system == 'windows'
|
||||
|
@ -55,8 +54,8 @@ cargo_release = custom_target(
|
|||
install_dir: get_option('bindir'),
|
||||
command: [
|
||||
cargo_script,
|
||||
meson.build_root(),
|
||||
meson.source_root(),
|
||||
meson.project_build_root(),
|
||||
meson.project_source_root(),
|
||||
'@OUTPUT@',
|
||||
get_option('buildtype'),
|
||||
app_name,
|
||||
|
|
|
@ -31,45 +31,55 @@ pub struct Settings {
|
|||
}
|
||||
|
||||
impl Settings {
|
||||
fn settings_file_exist() {
|
||||
let s = Settings::settings_file_path();
|
||||
|
||||
fn create_path_if_not(s: &PathBuf) {
|
||||
if !s.exists() {
|
||||
if let Some(parent_dir) = s.parent() {
|
||||
if !parent_dir.exists() {
|
||||
if let Err(e) = create_dir_all(parent_dir) {
|
||||
GPS_ERROR!(
|
||||
"Error while trying to build settings snapshot_directory '{}': {}",
|
||||
parent_dir.display(),
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
if let Err(e) = create_dir_all(s) {
|
||||
GPS_ERROR!(
|
||||
"Error while trying to build settings snapshot_directory '{}': {}",
|
||||
s.display(),
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn settings_file_path() -> PathBuf {
|
||||
fn default_app_folder() -> PathBuf {
|
||||
let mut path = glib::user_config_dir();
|
||||
path.push(config::APP_ID);
|
||||
path
|
||||
}
|
||||
|
||||
fn settings_file_path() -> PathBuf {
|
||||
let mut path = Settings::default_app_folder();
|
||||
Settings::create_path_if_not(&path);
|
||||
path.push("settings.toml");
|
||||
path
|
||||
}
|
||||
// Public methods
|
||||
pub fn default_graph_file_path() -> PathBuf {
|
||||
let mut path = glib::user_config_dir();
|
||||
path.push(config::APP_ID);
|
||||
pub fn graph_file_path() -> PathBuf {
|
||||
let mut path = Settings::default_app_folder();
|
||||
Settings::create_path_if_not(&path);
|
||||
path.push("default_graph.toml");
|
||||
path
|
||||
}
|
||||
|
||||
pub fn default_log_file_path() -> PathBuf {
|
||||
let mut path = glib::user_config_dir();
|
||||
path.push(config::APP_ID);
|
||||
pub fn log_file_path() -> PathBuf {
|
||||
let mut path = Settings::default_app_folder();
|
||||
Settings::create_path_if_not(&path);
|
||||
path.push("gstpipelinestudio.log");
|
||||
path
|
||||
}
|
||||
|
||||
pub fn gst_log_level() -> String {
|
||||
let settings = Settings::load_settings();
|
||||
let binding = "0".to_string();
|
||||
let level = settings
|
||||
.preferences
|
||||
.get("gst_log_level")
|
||||
.unwrap_or(&binding);
|
||||
level.clone()
|
||||
}
|
||||
|
||||
pub fn set_recent_pipeline_description(pipeline: &str) {
|
||||
let mut settings = Settings::load_settings();
|
||||
settings.recent_pipeline = pipeline.to_string();
|
||||
|
@ -105,7 +115,6 @@ impl Settings {
|
|||
|
||||
// Save the provided settings to the settings path
|
||||
pub fn save_settings(settings: &Settings) {
|
||||
Settings::settings_file_exist();
|
||||
let s = Settings::settings_file_path();
|
||||
if let Err(e) = serde_any::to_file(&s, settings) {
|
||||
GPS_ERROR!("Error while trying to save file: {} {}", s.display(), e);
|
||||
|
|
|
@ -222,11 +222,56 @@
|
|||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="child">
|
||||
<object class="GtkTreeView" id="treeview-logger"/>
|
||||
</property>
|
||||
</object>
|
||||
<object class="GtkNotebook" id="notebook-debug">
|
||||
<child>
|
||||
<object class="GtkNotebookPage">
|
||||
<property name="child">
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="child">
|
||||
<object class="GtkTreeView" id="treeview-app-logger"/>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
<property name="tab">
|
||||
<object class="GtkLabel" id="label-app-logger">
|
||||
<property name="label" translatable="1">App</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkNotebookPage">
|
||||
<property name="child">
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="child">
|
||||
<object class="GtkTreeView" id="treeview-gst-logger"/>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
<property name="tab">
|
||||
<object class="GtkLabel" id="label-gst-logger">
|
||||
<property name="label" translatable="1">GST</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkNotebookPage">
|
||||
<property name="child">
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="child">
|
||||
<object class="GtkTreeView" id="treeview-msg-logger"/>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
<property name="tab">
|
||||
<object class="GtkLabel" id="label-messages-logger">
|
||||
<property name="label" translatable="1">Messages</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::app::GPSApp;
|
||||
use crate::logger;
|
||||
use crate::ui::treeview;
|
||||
use gtk::prelude::*;
|
||||
use gtk::{gio, glib};
|
||||
|
@ -18,18 +19,37 @@ fn reset_logger_list(logger_list: &TreeView) {
|
|||
String::static_type(),
|
||||
String::static_type(),
|
||||
String::static_type(),
|
||||
String::static_type(),
|
||||
String::static_type(),
|
||||
]);
|
||||
logger_list.set_model(Some(&model));
|
||||
}
|
||||
|
||||
pub fn setup_logger_list(app: &GPSApp) {
|
||||
treeview::add_column_to_treeview(app, "treeview-logger", "TIME", 0, false);
|
||||
treeview::add_column_to_treeview(app, "treeview-logger", "LEVEL", 1, false);
|
||||
treeview::add_column_to_treeview(app, "treeview-logger", "LOG", 2, true);
|
||||
pub fn setup_logger_list(app: &GPSApp, logger_name: &str, log_type: logger::LogType) {
|
||||
match log_type {
|
||||
logger::LogType::App => {
|
||||
treeview::add_column_to_treeview(app, logger_name, "TIME", 0, false);
|
||||
treeview::add_column_to_treeview(app, logger_name, "LEVEL", 1, false);
|
||||
treeview::add_column_to_treeview(app, logger_name, "LOG", 2, true);
|
||||
}
|
||||
logger::LogType::Gst => {
|
||||
treeview::add_column_to_treeview(app, logger_name, "TIME", 0, false);
|
||||
treeview::add_column_to_treeview(app, logger_name, "LEVEL", 1, false);
|
||||
treeview::add_column_to_treeview(app, logger_name, "CATEGORY", 2, false);
|
||||
treeview::add_column_to_treeview(app, logger_name, "FILE", 3, false);
|
||||
treeview::add_column_to_treeview(app, logger_name, "LOG", 4, true);
|
||||
}
|
||||
logger::LogType::Message => {
|
||||
treeview::add_column_to_treeview(app, logger_name, "TIME", 0, false);
|
||||
treeview::add_column_to_treeview(app, logger_name, "LEVEL", 1, false);
|
||||
treeview::add_column_to_treeview(app, logger_name, "LOG", 2, true);
|
||||
}
|
||||
}
|
||||
|
||||
let logger_list: TreeView = app
|
||||
.builder
|
||||
.object("treeview-logger")
|
||||
.expect("Couldn't get treeview-logger");
|
||||
.object(logger_name)
|
||||
.expect("Couldn't get treeview-app-logger");
|
||||
reset_logger_list(&logger_list);
|
||||
|
||||
let gesture = gtk::GestureClick::new();
|
||||
|
@ -59,17 +79,45 @@ pub fn setup_logger_list(app: &GPSApp) {
|
|||
logger_list.add_controller(gesture);
|
||||
}
|
||||
|
||||
pub fn add_to_logger_list(app: &GPSApp, log_entry: &str) {
|
||||
fn log_tree_id_from_log_type(log_type: logger::LogType) -> String {
|
||||
match log_type {
|
||||
logger::LogType::App => String::from("treeview-app-logger"),
|
||||
logger::LogType::Gst => String::from("treeview-gst-logger"),
|
||||
logger::LogType::Message => String::from("treeview-msg-logger"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_to_logger_list(app: &GPSApp, log_type: logger::LogType, log_entry: &str) {
|
||||
let log_tree_name = log_tree_id_from_log_type(log_type.clone());
|
||||
let logger_list: TreeView = app
|
||||
.builder
|
||||
.object("treeview-logger")
|
||||
.expect("Couldn't get treeview-logger");
|
||||
.object(log_tree_name.as_str())
|
||||
.expect("Couldn't get treeview");
|
||||
if let Some(model) = logger_list.model() {
|
||||
let list_store = model
|
||||
.dynamic_cast::<ListStore>()
|
||||
.expect("Could not cast to ListStore");
|
||||
let log: Vec<&str> = log_entry.splitn(3, ' ').collect();
|
||||
list_store.insert_with_values(Some(0), &[(0, &log[0]), (1, &log[1]), (2, &log[2])]);
|
||||
if log_type == logger::LogType::Gst {
|
||||
let log: Vec<&str> = log_entry.splitn(5, '\t').collect();
|
||||
list_store.insert_with_values(
|
||||
Some(0),
|
||||
&[
|
||||
(0, &log[0]),
|
||||
(1, &log[1]),
|
||||
(2, &log[2]),
|
||||
(3, &log[3]),
|
||||
(4, &log[4]),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
let log: Vec<&str> = log_entry.splitn(3, ' ').collect();
|
||||
let mut indexed_vec: Vec<(u32, &dyn ToValue)> = Vec::new();
|
||||
|
||||
for (index, item) in log.iter().enumerate() {
|
||||
indexed_vec.push((index as u32, item));
|
||||
}
|
||||
list_store.insert_with_values(Some(0), &indexed_vec);
|
||||
}
|
||||
// Scroll to the first element.
|
||||
if let Some(model) = logger_list.model() {
|
||||
if let Some(iter) = model.iter_first() {
|
||||
|
|
|
@ -82,5 +82,16 @@ pub fn display_settings(app: &GPSApp) {
|
|||
dialog.close();
|
||||
});
|
||||
|
||||
let widget = gtk::Entry::new();
|
||||
widget.set_text(settings::Settings::gst_log_level().as_str());
|
||||
widget.connect_changed(glib::clone!(@weak widget => move |c| {
|
||||
let mut settings = settings::Settings::load_settings();
|
||||
settings.preferences.insert("gst_log_level".to_string(), c.text().to_string());
|
||||
settings::Settings::save_settings(&settings);
|
||||
}));
|
||||
let widget = widget
|
||||
.dynamic_cast::<gtk::Widget>()
|
||||
.expect("Should be a widget");
|
||||
add_settings_widget(&grid, "GST Log level", &widget, 2);
|
||||
dialog.show();
|
||||
}
|
||||
|
|
|
@ -57,6 +57,7 @@ pub fn property_to_widget<F: Fn(String, String) + 'static>(
|
|||
glib::ParamSpecInt64::static_type(),
|
||||
glib::ParamSpecUInt64::static_type(),
|
||||
glib::ParamSpecString::static_type(),
|
||||
glib::ParamSpecFloat::static_type(),
|
||||
]
|
||||
.contains(&t) =>
|
||||
{
|
||||
|
@ -113,7 +114,7 @@ pub fn property_to_widget<F: Fn(String, String) + 'static>(
|
|||
let param = param
|
||||
.clone()
|
||||
.downcast::<glib::ParamSpecFlags>()
|
||||
.expect("Should be a ParamSpecEnum");
|
||||
.expect("Should be a ParamSpecFlags");
|
||||
let flags = param.flags_class();
|
||||
for value in flags.values() {
|
||||
combo.append_text(&format!(
|
||||
|
|
Loading…
Reference in a new issue