Compare commits

...

31 commits
0.3.4 ... main

Author SHA1 Message Date
Stéphane Cerveau 1f200f4f30 app: update to gtk 0.8.0 and gst 0.22.2
In the change, the glib::channel has been dropped to use
async_io which achieves the same to receive message
for the logger and display it in the treeview.
Another message is received when a new gtkpaintablesink
has been instanciated.
2024-03-14 16:34:55 +01:00
Stéphane Cerveau 44d64ccdc4 cargo: update gtk and gst crate
gtk4 0.7.3
gst 0.21.3
gst-plugins-gtk 0.11.3
2024-01-25 20:04:38 +01:00
Stéphane Cerveau 3985458832 properties: can now support ParamSpecFloat 2024-01-23 10:29:57 +01:00
Stéphane Cerveau 7a70feedba pages: update the pages with release 0.3.5 2024-01-05 16:34:13 +01:00
Stéphane Cerveau 88afa4a99e release: 0.3.5 2024-01-05 15:36:00 +01:00
Stéphane Cerveau d32c75b639 ci: change to macos 13 runner 2024-01-05 14:37:13 +01:00
Stéphane Cerveau 7789588aef ci: fix yaml issue detected by pre-commit 2024-01-05 11:25:03 +01:00
Stéphane Cerveau b1ad0e958b graphmanager: fix typos detected by pre-commit 2024-01-05 11:25:03 +01:00
Stéphane Cerveau 9c8a578e05 gps: fix typos and format detected by pre-commit 2024-01-05 11:25:03 +01:00
Stéphane Cerveau 3e15b7cecb graphmanager: remove link export 2024-01-05 11:25:03 +01:00
Stéphane Cerveau 11cf962bfd ci: add pre-commit job 2024-01-05 11:25:03 +01:00
Stéphane Cerveau a25f0499c8 macos: disabled ges build
Due to an error with python, disable GES build
for now.
2024-01-05 09:32:40 +01:00
Stéphane Cerveau 51769d6061 logger: support variable length for logger split 2024-01-04 15:20:14 +00:00
Stéphane Cerveau af317eee96 app: rename gst_pipeline_studio to gst-pipeline-studio 2024-01-04 14:32:17 +01:00
Stéphane Cerveau 3aded523c2 element: add property special case for float 2024-01-03 13:52:04 +01:00
Stéphane Cerveau d3005335b8 logger: add function name in the logs 2024-01-03 13:25:49 +01:00
Stéphane Cerveau 18458e3465 properties: fix expect for ParamSpecFlags 2024-01-03 13:25:49 +01:00
Stéphane Cerveau a6f03db8f6 element: element property special convert for enum and flags
In element_property, add special case enum and flags
property to use a better value such as nick or the
proper u32 flags
2024-01-03 13:25:49 +01:00
Stéphane Cerveau bed8d6a58e logger: fix clippy complain 2024-01-02 14:51:38 +01:00
Stéphane Cerveau c5f9cac444 app: support cmd line to open a custom pipeline
Add a way to open a pipeline from the cmd line
2024-01-02 14:51:03 +01:00
Stéphane Cerveau 30baa56881 wix: installer to a different UI profile
In order to remove minimal and full option
uses only a minimal profile.
2023-12-22 15:08:18 +01:00
Stéphane Cerveau 13165fa9c0 ci: use release build for windows
Create an image with release build of
gstreamer and gtk
2023-12-22 11:40:59 +01:00
Stéphane Cerveau 6ca3059914 ci: set the bundler version to 2.4.22 2023-12-19 11:20:56 +01:00
Stéphane Cerveau f4019fd2af wix: change wix banner and dialog bmp 2023-12-19 10:29:32 +01:00
Stéphane Cerveau 8c6cda2e92 player: feed the app with gst logs
Get the debug callback to retrieve the gst logs
and display it in the debug tab.
2023-11-30 11:54:15 +01:00
Stéphane Cerveau 9c03de5d00 player: keep a reference to bus_watch_guard
In order to receive the message from the bus
the API enforces to keep a reference to the bus_watch_guard
otherwise the watch gets lost.
2023-11-30 11:52:28 +01:00
Stéphane Cerveau 24121856ee app: use the channel to receive other logs
Add another logger to receive message, events or GST logs
2023-11-30 11:52:28 +01:00
Stéphane Cerveau e410289a13 ui: support multiple tab in debug section
Add GST logs, message, events in the debug section
2023-11-29 23:16:06 +01:00
Stéphane Cerveau 2601454143 Update README.md to include the website 2023-10-05 12:28:41 +00:00
Stéphane Cerveau f47b0624fd appdata: add a release description 2023-09-29 15:48:02 +02:00
Stéphane Cerveau b2c6a8bc2a pages: update the pages with release 0.3.4 2023-09-28 19:06:06 +02:00
44 changed files with 1157 additions and 531 deletions

View file

@ -22,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
@ -60,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
@ -90,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
@ -120,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,11 +239,8 @@ flatpak:
macos installer stable:
stage: test
needs:
- job: "windows rust docker stable"
artifacts: false
tags:
- gst-macos-11.1
- gst-macos-13
before_script:
- pip3 install --upgrade pip
# Make sure meson is up to date
@ -274,7 +283,7 @@ pages:
image: ruby:2.7
stage: deploy
script:
- gem install bundler
- gem install bundler -v 2.4.22
- bundle install
- bundle exec jekyll build -d public
artifacts:

29
.pre-commit-config.yaml Normal file
View 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

File diff suppressed because it is too large Load diff

View file

@ -1,15 +1,15 @@
[package]
name = "gst_pipeline_studio"
version = "0.3.4"
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"

View file

@ -112,8 +112,16 @@
- [x] Fix the maximize call with MacOS
- [x] Fix the default size at GTK save/load state
## 0.3.4
## 0.3.4
### app
### 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
- [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

View file

@ -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

View file

@ -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.

View file

@ -1 +1 @@
0.3.4
0.3.5

View file

@ -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)

View file

@ -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")])

View file

@ -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": [

View file

@ -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"

View file

@ -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"

View file

@ -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 " +

View file

@ -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

View file

@ -3,7 +3,7 @@
GstPipelineStudio Website
====================
shamelessy stolen CSS from Pipewire
shamelessly stolen CSS from Pipewire
*/

View file

@ -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>

View file

@ -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@

View file

@ -13,7 +13,7 @@
<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.4 is out, checkout the <a
<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>
@ -22,12 +22,12 @@
<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/f9efb2a7e798d6bb53c64e46ccc28fc1/GstPipelineStudio-0.3.3.msi">Windows
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/dd3bd214be3538eefffca8d3247986f4/GstPipelineStudio-0.3.3.dmg">MacOS</a>
href="https://gitlab.freedesktop.org/dabrain34/GstPipelineStudio/uploads/d6d703809e3023de04e7ad1449fcb4aa/GstPipelineStudio-0.3.5.dmg">MacOS</a>
</li>
</ul>

View file

@ -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,7 +94,7 @@ 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"

View file

@ -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>

View file

@ -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

View file

@ -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
}

View file

@ -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

View file

@ -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

View file

@ -5,4 +5,4 @@ set PATH=%MYDIR%bin;%PATH%
echo %PATH%
set GST_PLUGIN_PATH=%MYDIR%\lib\gstreamer-1.0
echo %GST_PLUGIN_PATH%
gst_pipeline_studio.exe
gst-pipeline-studio.exe

View file

@ -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">

BIN
installer/wix/wixbanner.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

BIN
installer/wix/wixdialog.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 507 KiB

View file

@ -1,5 +1,5 @@
project('gst_pipeline_studio',
version: '0.3.4',
project('gst-pipeline-studio',
version: '0.3.5',
meson_version: '>= 0.63.0',
default_options: [ 'warning_level=2',
],

View file

@ -6,6 +6,7 @@
- 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
- Fetch the package from the `linux release` job or you can make it manually with:

View file

@ -150,7 +150,7 @@ impl GPSApp {
.insert(paned_name.to_string(), paned.position());
}
pub fn on_startup(application: &gtk::Application) {
pub fn on_startup(application: &gtk::Application, pipeline_desc: &String) {
// Create application and error out if that fails for whatever reason
let app = match GPSApp::new(application) {
Ok(app) => app,
@ -160,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
@ -449,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,
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
});
@ -511,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);
},
);
@ -604,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::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
@ -775,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(())
}
}

View file

@ -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());

View file

@ -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();

View file

@ -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();
}

View file

@ -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;

View file

@ -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,

View file

@ -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);
}
}

View file

@ -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();
}

View file

@ -70,6 +70,16 @@ impl Settings {
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();

View file

@ -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>

View file

@ -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() {

View file

@ -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();
}

View file

@ -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!(