104 Commits

Author SHA1 Message Date
Rafał Dzięgiel
a6d6020c35 WIP: clapper-app: Ability to preview GStreamer pipeline 2024-12-10 21:18:31 +01:00
Rafał Dzięgiel
1eea63076c clapper: Add a function to get pipeline graph data 2024-11-17 17:21:00 +01:00
Rafał Dzięgiel
8e6599ae81 Merge pull request #508 from Rafostar/nightly-fix
Update Flatpak Nightly
2024-11-16 22:33:31 +01:00
Rafał Dzięgiel
77ee519f8c flatpak-nightly: Update manifest
Use 24.08 for ffmpeg and switch to Rust stable with added recommended env vars
2024-11-16 17:25:49 +01:00
Rafał Dzięgiel
c42e12d2be workflows: Update nightly extensions to 24.08
Also change to Rust stable to fix build failing
2024-11-16 17:25:03 +01:00
Rafał Dzięgiel
2b916ef856 Merge pull request #506 from Rafostar/adaptive
Add adaptive streaming bitrate control to Clapper API
2024-11-16 16:15:19 +01:00
Rafał Dzięgiel
303bda4d65 clapper: Do not reset "adaptive-bandwidth" to zero
When new instance of adaptive demuxer is created (different video is played)
this prop value would become zero. We want to avoid that and instead report
only bandwidth after fragment is downloaded, so when video will be unplayable,
next one will not start from lowest quality.
2024-11-16 15:26:24 +01:00
Rafał Dzięgiel
22430620a8 clapper-app: Apply "adaptive-start-bitrate" on startup
Watch for "adaptive-bandwidth" changes during adaptive streaming, use these to
set "adaptive-start-bitrate" player property, so we do not always start streaming
from some constant bitrate value which might not be the best for everyone.

Additionally, store last value in GSettings on app exit and also add a command
line arg to set this too.
2024-11-15 23:39:00 +01:00
Rafał Dzięgiel
e4045b98b3 clapper: Add "adaptive-bandwidth" readable property
Apps can use it to determine and set an optimal value for
start bitrate instead of starting at some constant value.
2024-11-15 23:38:50 +01:00
Rafał Dzięgiel
aa819cf2d3 flatpak: Update runtime to 47 2024-11-14 23:19:38 +01:00
Rafał Dzięgiel
a065b81846 flatpak: Add GStreamer 1.24 for testing
We do not yet have GStreamer 1.24 in Flathub repo, so for testing purposes add it here temporarily
2024-11-14 23:18:16 +01:00
Rafał Dzięgiel
9fbba4d66a meson: Bump min required GStreamer version
Setting "start-bitrate" property requires at least GStreamer 1.24.

We do not want to introduce APIs that work only if user has a certain
dependency version, thus this bump instead of version check.

Related work on GStreamer side:
https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3894
https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3895

Since these were contributed to GStreamer almost 2 years ago with the intention
of using them in Clapper, I think its about time to start do so.
2024-11-14 22:28:21 +01:00
Rafał Dzięgiel
ab659d0951 clapper: Use playbin3 by default
New GStreamer adaptive demuxers work only within playbin3. In order to not
introduce new APIs (setting adaptive bitrate) that do not work by default
and to have the whole "clapper-enhancers" concept working correctly, its
about time to move on to playbin3.
2024-11-13 20:06:34 +01:00
Rafał Dzięgiel
bd3ce28716 clapper: Ability to request adaptive bitrates
Add APIs to allow apps to select start, min and max bitrates for adaptive streaming.

Combining min+max values can allow to implement a video quality selector,
although possible bitrates/qualities are not communicated with an app yet.
2024-11-13 20:00:59 +01:00
Rafał Dzięgiel
0a15e70d02 Merge pull request #501 from Rafostar/libpeas
Support libpeas based plugin system
2024-11-11 13:22:10 +01:00
Rafał Dzięgiel
d42b17a749 workflows: Add libpeas to windows build
Required for enhancers loader
2024-11-11 12:49:23 +01:00
Rafał Dzięgiel
719a4043b5 workflows: Disable "enhancers-loader" for docs build
Not needed to build the docs, so disable it
2024-11-11 12:49:19 +01:00
Rafał Dzięgiel
ee3d07b158 flatpak: Replace gtuber with Clapper enhancers
Also add all required dependencies
2024-11-11 12:49:17 +01:00
Rafał Dzięgiel
c8ef0f891d clapper: Support libpeas based plugin system
Allow loading external plugins called "Enhancers" that as the name suggests,
enhance Clapper library capabilities.

Currently implemented is a "ClapperExtractable" interface meant to extract
an actual media that GStreamer can later play from an initial input URI.
Additionally, an internal GStreamer elements that work with it are ported/moved
here from "gtuber" library that this functionality replaces.
2024-11-11 12:49:07 +01:00
Rafał Dzięgiel
220913de14 Merge pull request #499 from Rafostar/esc-unfullscreen
clapper-app: Add unfullscreen keyboard shortcut
2024-10-15 19:53:32 +02:00
Rafał Dzięgiel
2d1f6f471b clapper-app: Add unfullscreen keyboard shortcut
Unfullscreen with Escape key

Closes #494
2024-10-15 19:32:48 +02:00
Rafał Dzięgiel
af4a876edc New Crowdin updates (#473)
* New translations clapper-gtk.pot (Hindi)

* New translations clapper-app.pot (Russian)

* New translations clapper-gtk.pot (Czech)

* New translations clapper-gtk.pot (Persian)

* New translations clapper-gtk.pot (Norwegian)
2024-10-10 20:23:28 +02:00
Rafał Dzięgiel
14cdbef81c clapper-gtk: Fix metadata ignore for different languages
Since missing plugin string is translated, word describing its type
(e.g. Decoder) might come before plugin name itself, so we need to
check whole text to find substring instead of checking only prefix.

Fixes #477
2024-08-02 17:22:27 +02:00
Rafał Dzięgiel
931dc255c1 clapper-gtk: Fix some video props not being writable
They should have been writable as we even have setters for them
2024-07-23 17:55:11 +02:00
Rafał Dzięgiel
2f39fef47b readme: Update screenshots 2024-06-30 21:38:29 +02:00
Rafał Dzięgiel
c05a6361b7 clapper-app: metainfo: Add captions to screenshots 2024-06-30 21:29:26 +02:00
Rafał Dzięgiel
c152f3d791 clapper-app: Update LINGUAS file 2024-06-29 18:24:40 +02:00
Rafał Dzięgiel
f967e169b5 clapper-gtk: Update LINGUAS file 2024-06-29 18:24:26 +02:00
Rafał Dzięgiel
04a5bc8523 New Crowdin updates (#431)
* New translations clapper-gtk.pot (Portuguese, Brazilian)

* New translations clapper-app.pot (Portuguese, Brazilian)

* New translations clapper-app.pot (Catalan)

* New translations clapper-app.pot (Chinese Simplified)

* New translations clapper-gtk.pot (Chinese Simplified)

* New translations clapper-gtk.pot (Occitan)

* New translations clapper-app.pot (Occitan)
2024-06-29 18:18:06 +02:00
Rafał Dzięgiel
135be60497 clapper-gtk: billboard: Make transition a tad faster 2024-06-29 17:49:41 +02:00
Rafał Dzięgiel
736fddbe46 meson: Do not list Clapper API features when not building it
No need to list API features if API is not being built.

This avoids an issue with meson script error when Clapper API
build is skipped, since these are not declared then.

Fixes #468
2024-06-29 17:35:47 +02:00
Rafał Dzięgiel
63f2fa1946 examples: Start playback after assembling app window
This is the correct order of how things should be done, so teach what is right
2024-06-29 15:07:59 +02:00
Rafał Dzięgiel
99708e1267 clapper-gtk: status: Apply ".osd" style class
Otherwise text is invisible on light themed apps,
since it renders as black letters on a black background
2024-06-29 15:05:12 +02:00
Rafał Dzięgiel
636e61ead1 clapper-gtk: video: Use "scaletempo" by default
Do not require applications to import GStreamer and their devs to learn its
basics just to set this single element. Similarly as ClapperGtkVideo sets
"clappersink" during construction, it can also set "scaletempo" as this is
the most common choice for an audio filter.

Elements can still be changed like before if desired by setting corresponding
player properties.
2024-06-29 14:52:25 +02:00
Rafał Dzięgiel
147399afd8 clapper: media-item: Fix construct with NULL as "cache-location" 2024-06-28 19:47:07 +02:00
Rafał Dzięgiel
2843a17879 Merge pull request #467 from Rafostar/cached-items
clapper: Add media item creation with cached file
2024-06-27 22:13:36 +02:00
Rafał Dzięgiel
bf0a8b68db clapper: Add media item creation with cached file
Allow creating item with a previously downloaded cache file.
When set, file at location will be tried to be loaded first.
If this fails, URI will be used as normal.
2024-06-27 20:57:59 +02:00
Rafał Dzięgiel
af4ae2c942 clapper: Fix missing "download-complete" signal on short videos
Short video might be fully downloaded before playback starts.
This prevents us from emitting "download-complete" signal with
corresponding media item when download cache is enabled.

Check if we have pending item, as this will be the item downloaded
in such case (before it becomes playing item).
2024-06-22 11:20:57 +02:00
Rafał Dzięgiel
c0221dedc8 examples: Fix typo 2024-06-22 11:12:10 +02:00
Rafał Dzięgiel
9f54c1058c Merge pull request #463 from Rafostar/ms-win
Support MS Windows
2024-06-21 20:32:50 +02:00
Rafał Dzięgiel
07f850d58a workflows: Add windows build job 2024-06-21 18:50:24 +02:00
Rafał Dzięgiel
650b568319 clapper-app: meson: Do not install mimes on Windows
They are unsupported. We use file extensions instead.
2024-06-21 18:50:21 +02:00
Rafał Dzięgiel
abff579b70 clapper-app: Fix missing media in file dialog on Windows
Windows does not support mime-types file filters, so use file extensions instead
2024-06-21 18:50:13 +02:00
Rafał Dzięgiel
0b253b41f6 clapper-app: meson: Binaries with/without console on Windows 2024-06-20 18:32:50 +02:00
Rafał Dzięgiel
d5832bd2db gst-plugin: Improve importers lookup on Win32 2024-06-19 22:44:27 +02:00
Rafał Dzięgiel
98371e822e gst-plugin: Allow overriding importers path with env variable 2024-06-17 21:41:11 +02:00
Rafał Dzięgiel
3f2e5d569d clapper-gtk: video: Fix signals disconnection after dispose
A possibility exists that something else keeps a reference to the player
even after video is disposed. In this case signals from the player would
not be disconnected.

We have to disconnect them manually here to ensure their callbacks will
not be used after disposal.
2024-06-17 19:26:18 +02:00
Rafał Dzięgiel
f58e722002 clapper-app: Fix unused variable warning when no MPRIS
When compiling without MPRIS feature "instance_count" is left unused,
so skip it with preprocessor in order to avoid warning.
2024-06-16 17:35:06 +02:00
Rafał Dzięgiel
262983e857 clapper-app: Ensure private types for GtkBuilder
We need to ensure types that are not part of public API, otherwise GtkBuilder will fail to use them
(e81aad25a1/gtk/glade/README.glade (L15)).
2024-06-16 15:21:23 +02:00
Rafał Dzięgiel
6ca1bccd1a clapper-gtk: Ensure private types before template init
We need to ensure types that are not part of public API, otherwise GtkBuilder will fail to use them
(e81aad25a1/gtk/glade/README.glade (L15)).
2024-06-16 14:51:56 +02:00
Rafał Dzięgiel
c25cfd9598 clapper-app: meson: Do not skip whole data install on non-linux
We cannot run app without schemas, thus we need to compile and install them on any OS
2024-06-16 13:50:54 +02:00
Rafał Dzięgiel
3ce937c2b9 gst-plugin: Properly export external API symbols
We do not install headers from gst-plugin, but still need to export
symbols to compile modules that use plugin provided shared libraries.
2024-06-16 13:50:49 +02:00
Rafał Dzięgiel
521a83bad0 clapper: meson: Add additional check for gdbus-codegen
Check at meson level if "gdbus-codegen" is available and otherwise
exit cleanly, possibly building without MPRIS feature instead of failing.
2024-06-16 13:50:41 +02:00
Rafał Dzięgiel
d4598846d3 clapper-gtk: Properly export external API symbols 2024-06-16 13:48:27 +02:00
Rafał Dzięgiel
50d4fbe43b clapper: Properly export external API symbols 2024-06-16 13:48:17 +02:00
Rafał Dzięgiel
df371c698a clapper-app: Fix opened file content type detection
Fixes issues with detecting whether file has one of supported subtitles mime types
2024-06-14 17:32:03 +02:00
Rafał Dzięgiel
2eaf5032cd Merge pull request #451 from Rafostar/resize-me
Resize app window to match aspect ratio with middle click
2024-05-26 17:06:58 +02:00
Rafał Dzięgiel
28de283a98 clapper-app: Add auto window resize keyboard shortcut
In addition to middle click, also add "Super+r" keyboard shortcut to perform window resize to fit video action
2024-05-25 21:30:26 +02:00
Rafał Dzięgiel
ae6512b233 clapper-app: Resize window to match aspect ratio with middle click 2024-05-25 21:16:59 +02:00
Rafał Dzięgiel
5049f620eb Merge pull request #449 from Rafostar/new-window
Add "New Window" functionality in UI
2024-05-20 22:55:14 +02:00
Rafał Dzięgiel
439e5831e2 clapper-app: Apply options from active window to newly created one
When a new window is created, apply all current values to it (volume, speed,
set GStreamer elements, etc.). It would not be the best experience when user
opens a new window, plays something and 100% volume is used in it.
2024-05-20 22:40:28 +02:00
Rafał Dzięgiel
6484599e46 clapper-gtk: Fix GtkWindowControls style when OSD on video 2024-05-15 21:37:48 +02:00
Rafał Dzięgiel
333f8b4692 clapper-app: Make info window close with Esc key
Fixes #448
2024-05-14 21:13:02 +02:00
Rafał Dzięgiel
9b7c12c3d0 flatpak: Fix MPRIS permission
Since now we do multiple windows, an instance number is added
to owned connection name (as specified in MPRIS spec).

Update Flatpak MPRIS permission to handle that.
2024-05-13 20:32:54 +02:00
Rafał Dzięgiel
6fb25dd22b clapper-app: Add new window keyboard shortcut 2024-05-13 20:05:59 +02:00
Rafał Dzięgiel
cc5c2b2403 clapper-app: Add "New Window" menu option
Allow creating new windows from within the app
2024-05-13 19:15:31 +02:00
Rafał Dzięgiel
71142ab0c6 Merge pull request #444 from Rafostar/cmd-options
Add more command line options
2024-05-12 20:47:45 +02:00
Rafał Dzięgiel
cc8ed7b488 clapper-app: Do not store "enqueue" value in options
It is used immediately, so no need to store it.
2024-05-12 20:30:19 +02:00
Rafał Dzięgiel
6f0ed93b08 clapper-app: Only restore saved settings to new windows 2024-05-12 20:30:16 +02:00
Rafał Dzięgiel
10e0421342 clapper-app: Clamp command line args to allowed values
Instead of running into warnings or other problems when user
specifies an out of range value for command line option, just
clamp them to nearest allowed one.
2024-05-12 20:30:14 +02:00
Rafał Dzięgiel
bcd1d48922 clapper-app: Add command line context params string
Describe what kind of remaining (non-options) strings can
be passed when launching via command line.
2024-05-12 20:30:11 +02:00
Rafał Dzięgiel
aac050e252 clapper-app: Add command line option to enter fullscreen 2024-05-12 20:30:09 +02:00
Rafał Dzięgiel
1330e49ed8 clapper-app: Add command line option to set progression mode 2024-05-12 20:30:07 +02:00
Rafał Dzięgiel
22fb88d0f2 clapper-app: Support creating multiple windows 2024-05-12 20:30:01 +02:00
Rafał Dzięgiel
42b5acd967 clapper: mpris: Fix MPRIS own-name documentation 2024-05-12 20:16:28 +02:00
Rafał Dzięgiel
8e14ec1697 clapper-app: Cleanup to not store app options as global and static
Now that we are using both local and global command line vfuncs, we
can just get and apply options in them and free values later.

This avoids having options global and static.
2024-05-12 19:34:37 +02:00
Rafał Dzięgiel
a257d3f003 clapper-app: Add command line option to enqueue files 2024-05-12 19:34:33 +02:00
Rafał Dzięgiel
eb0baa6fe6 clapper-app: Add command line option to set speed 2024-05-12 16:50:04 +02:00
Rafał Dzięgiel
1001fda256 clapper-app: Add command line option to set volume 2024-05-12 16:50:02 +02:00
Rafał Dzięgiel
6edffb9a4b clapper-app: Add command line options to set GStreamer elements
Allows setting audio/video filters and sinks.
By setting value to "none" default filter can be disabled.
2024-05-12 16:49:54 +02:00
Rafał Dzięgiel
b013cb7405 Merge pull request #438 from Rafostar/download-cache
clapper: Add media caching via download to local storage
2024-05-10 00:14:28 +02:00
Rafał Dzięgiel
07ca728b5b workflows: doc: Do not error on nothing to commit
Not always there are changes to the documentation
2024-05-09 23:50:25 +02:00
Rafał Dzięgiel
938cd44d7e workflows: doc: Continue on commit/push errors 2024-05-09 23:43:30 +02:00
Rafał Dzięgiel
8f4723906f Merge pull request #447 from Rafostar/auto-doc
workflows: Add build documentation job
2024-05-09 23:15:58 +02:00
Rafał Dzięgiel
419354fe8f doc: Add "clapper" GIR path when building "clapper-gtk" doc
Fixes building of clapper-gtk documentation on a clean system
2024-05-09 22:54:08 +02:00
Rafał Dzięgiel
4ec7349a04 workflows: Add build documentation job 2024-05-09 22:53:58 +02:00
Rafał Dzięgiel
03b2cb099f examples: Add download cache code example 2024-05-07 22:15:34 +02:00
Rafał Dzięgiel
f67d5bef2e clapper: Add media caching via download to local storage
The aim here is to stream an online video/audio while also at the
same time download/cache it to disk (excluding adaptive content).

After download is complete, further playback and seeking are done using the
locally cached file. This functionality uses GStreamer "downloadbuffer" element.

Player will emit a signal with a local download location after it completes,
so application will know where downloaded file for media item is stored in
case it wants to reuse it in the future.

It is up to application to set download dir and later manage downloaded
content in it, removing files its not going to use on next application
run and any incomplete downloads.
2024-05-07 22:15:25 +02:00
Rafał Dzięgiel
8f4107aab6 examples: Add simple clapper-gtk usage example 2024-05-07 22:09:41 +02:00
Rafał Dzięgiel
6941f1b042 clapper: Small fix for "init_check" doc
The error arg was removed, but documentation was not updated then
2024-05-02 14:38:25 +02:00
Rafał Dzięgiel
df50802871 clapper-gtk: billboard: Documentation fixes
Billboard does not post automatic volume/speed announcements. It did at
some point during development, but it was scrapped in favour of letting
app use callbacks of "notify" signals to call announce speed/volume.
2024-05-02 13:17:09 +02:00
Rafał Dzięgiel
b1613273d0 gst-plugin: Avoid GTK crash after context is destroyed
It seems GTK continues using created contexts, so we cannot unref it here.
Looking at GTK sink implementation for GtkVideo, I see that they do not
unref it either, but since we use surfaceless context it would be probably
better to have a static GstDisplay and reuse the same context in future.
2024-05-02 11:14:29 +02:00
Rafał Dzięgiel
35527e5c14 clapper-gtk: Ignore missing plugins for metadata
Some media files have custom/proprietary metadata.
It should be safe to simply ignore these, since we do
not map metadata to any sort of Clapper stream anyway.

Fixes #441
2024-05-01 16:32:35 +02:00
Rafał Dzięgiel
e060048969 Merge pull request #429 from 1Maxnet1/patch-1
Complement com.github.rafostar.Clapper.metainfo.xml
2024-05-01 11:45:25 +02:00
Rafał Dzięgiel
09648bbc43 Mention Rust bindings in README.md
Closes #411
2024-04-29 21:38:37 +02:00
Max Buchholz
9e5f9e3d07 Add comment to document categories
Co-authored-by: Rafał Dzięgiel <40623528+Rafostar@users.noreply.github.com>
2024-04-29 20:19:10 +02:00
Rafał Dzięgiel
23c5e7ca65 clapper: Fix MARKER_NO_END value in GIR
Needs a cast from int, otherwise GIR is generated
incorrectly and it ends up as positive number.
2024-04-29 17:50:54 +02:00
Rafał Dzięgiel
6d47aa3c6d clapper-app: metainfo: Update branding colors
Change to values suggested by Tobias Bernard
2024-04-27 16:20:33 +02:00
Rafał Dzięgiel
ae3d75a8b5 clapper-app: metainfo: Update app summary
Change to a text suggested by Tobias Bernard
2024-04-27 16:20:17 +02:00
Rafał Dzięgiel
3561bc8c6f Update README.md 2024-04-27 15:59:28 +02:00
Rafał Dzięgiel
368a484741 flatpak: Sync with Flathub 2024-04-27 15:40:38 +02:00
Rafał Dzięgiel
b3ac64e5b1 clapper-app: Show development versions in about window
Since during development we do not put new release tag into metainfo
which about window uses to know app version, set it manually to the
library version since they are the same.
2024-04-27 14:19:45 +02:00
Rafał Dzięgiel
c873c35fa7 Back to development 2024-04-27 14:14:39 +02:00
Max Buchholz
af47dcb5e5 Complement com.github.rafostar.Clapper.metainfo.xml 2024-04-25 10:54:09 +02:00
118 changed files with 7194 additions and 515 deletions

58
.github/workflows/documentation.yml vendored Normal file
View File

@@ -0,0 +1,58 @@
on:
workflow_dispatch:
push:
branches:
- master
pull_request:
branches:
- master
name: "Documentation"
jobs:
documentation:
runs-on: ubuntu-latest
container:
image: registry.fedoraproject.org/fedora
options: --privileged
steps:
- name: Prepare
run: |
dnf -y install git gcc meson glib2-devel \
gstreamer1-devel gstreamer1-plugins-base-devel \
gtk4-devel libsoup3-devel libmicrodns-devel \
gobject-introspection-devel graphviz gi-docgen
- name: Checkout
uses: actions/checkout@v4
with:
persist-credentials: false
fetch-depth: 0
- name: Build
run: |
meson setup builddir -Dauto_features=enabled \
-Denhancers-loader=disabled \
-Dclapper-app=disabled -Dvapi=disabled -Ddoc=true \
-Dglimporter=auto -Dgluploader=auto -Drawimporter=auto
cd builddir
meson compile
- name: Commit files
if: ${{ success() && github.ref == 'refs/heads/master' }}
continue-on-error: true
run: |
git config --global --add safe.directory $GITHUB_WORKSPACE
git checkout gh-pages
rm -rf doc/clapper/*
rm -rf doc/clapper-gtk/*
mv builddir/doc/reference/clapper/clapper.toml doc/clapper/
mv builddir/doc/reference/clapper/clapper/* doc/clapper/
mv builddir/doc/reference/clapper-gtk/clapper-gtk.toml doc/clapper-gtk/
mv builddir/doc/reference/clapper-gtk/clapper-gtk/* doc/clapper-gtk/
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add doc/clapper/* doc/clapper-gtk/*
git commit -m "doc: Update" || true
- name: Push changes
uses: ad-m/github-push-action@master
if: ${{ success() && github.ref == 'refs/heads/master' }}
continue-on-error: true
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
branch: gh-pages

View File

@@ -30,8 +30,8 @@ jobs:
platforms: arm64
- name: Prepare Runtime
run: |
flatpak --system install -y --noninteractive flathub org.freedesktop.Sdk.Extension.rust-nightly/${{ matrix.arch }}/23.08
flatpak --system install -y --noninteractive flathub org.freedesktop.Sdk.Extension.llvm16/${{ matrix.arch }}/23.08
flatpak --system install -y --noninteractive flathub org.freedesktop.Sdk.Extension.rust-stable/${{ matrix.arch }}/24.08
flatpak --system install -y --noninteractive flathub org.freedesktop.Sdk.Extension.llvm18/${{ matrix.arch }}/24.08
- name: Build
uses: bilelmoussaoui/flatpak-github-actions/flatpak-builder@v6
with:

90
.github/workflows/windows.yml vendored Normal file
View File

@@ -0,0 +1,90 @@
on:
workflow_dispatch:
push:
branches:
- master
pull_request:
branches:
- master
name: "Windows"
jobs:
windows:
name: "Windows"
runs-on: windows-latest
strategy:
matrix:
arch: [x86_64]
defaults:
run:
shell: msys2 {0}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup MSYS2
uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
update: true
install: |-
mingw-w64-${{ matrix.arch }}-meson
mingw-w64-${{ matrix.arch }}-gcc
mingw-w64-${{ matrix.arch }}-glib2
mingw-w64-${{ matrix.arch }}-gstreamer
mingw-w64-${{ matrix.arch }}-gst-plugins-base
mingw-w64-${{ matrix.arch }}-gst-plugins-good
mingw-w64-${{ matrix.arch }}-gst-plugins-bad
mingw-w64-${{ matrix.arch }}-gst-plugins-ugly
mingw-w64-${{ matrix.arch }}-gst-libav
mingw-w64-${{ matrix.arch }}-libpeas2
mingw-w64-${{ matrix.arch }}-libsoup3
mingw-w64-${{ matrix.arch }}-libmicrodns
mingw-w64-${{ matrix.arch }}-gtk4
mingw-w64-${{ matrix.arch }}-libadwaita
- name: Prepare
run: |
BUILD_PREFIX="$GITHUB_WORKSPACE/clapper-win-${{ matrix.arch }}"
mkdir -p $BUILD_PREFIX/bin
cp /mingw64/bin/gdbus.exe $BUILD_PREFIX/bin/
cp /mingw64/bin/gst-inspect-1.0.exe $BUILD_PREFIX/bin/
mkdir -p $BUILD_PREFIX/lib
cp -r /mingw64/lib/gio $BUILD_PREFIX/lib/
cp -r /mingw64/lib/gstreamer-1.0 $BUILD_PREFIX/lib/
cp -r /mingw64/lib/gdk-pixbuf-2.0 $BUILD_PREFIX/lib/
mkdir -p $BUILD_PREFIX/share/glib-2.0/schemas
cp -r /mingw64/share/glib-2.0/schemas/*.xml $BUILD_PREFIX/share/glib-2.0/schemas/
cp -r /mingw64/share/icons $BUILD_PREFIX/share/
mkdir -p $BUILD_PREFIX/share/xml/iso-codes
cp /mingw64/share/xml/iso-codes/iso_639.xml $BUILD_PREFIX/share/xml/iso-codes/
cd "$BUILD_PREFIX/lib/gstreamer-1.0"
rm -f \
libgstadpcmenc.dll libgstamfcodec.dll libgstdvbsubenc.dll libgstencoding.dll \
libgstfrei0r.dll libgstinter.dll libgstlame.dll libgstldac.dll libgstmpeg2enc.dll \
libgstmpegpsmux.dll libgstmpegtsmux.dll libgstmplex.dll libgstrealmedia.dll \
libgstsubenc.dll libgstsvtav1.dll libgstsvthevcenc.dll libgsttwolame.dll \
libgstvoamrwbenc.dll libgstwavenc.dll libgstx264.dll libgstx265.dll \
libgstxingmux.dll libgsty4menc.dll libgstzbar.dll
- name: Build
run: |
meson setup builddir --prefix=$GITHUB_WORKSPACE/clapper-win-${{ matrix.arch }}
cd builddir
meson compile
meson install
- name: Package
run: |
BUILD_PREFIX="$GITHUB_WORKSPACE/clapper-win-${{ matrix.arch }}"
ldd $BUILD_PREFIX/bin/clapper.exe | grep '\/mingw.*\.dll' -o | xargs -I{} cp -n "{}" $BUILD_PREFIX/bin
find $BUILD_PREFIX/lib/clapper-0.0/ -name '*\.dll' -type f -exec ldd "{}" \; | grep '\/mingw.*\.dll' -o | xargs -I{} cp -n "{}" $BUILD_PREFIX/bin
find $BUILD_PREFIX/lib/gstreamer-1.0/ -name '*\.dll' -type f -exec ldd "{}" \; | grep '\/mingw.*\.dll' -o | xargs -I{} cp -n "{}" $BUILD_PREFIX/bin
find $BUILD_PREFIX/lib/gio/ -name '*\.dll' -type f -exec ldd "{}" \; | grep '\/mingw.*\.dll' -o | xargs -I{} cp -n "{}" $BUILD_PREFIX/bin
- name: Upload
uses: actions/upload-artifact@v4
with:
name: clapper-win-${{ matrix.arch }}
path: clapper-win-${{ matrix.arch }}
if-no-files-found: error

View File

@@ -31,6 +31,8 @@ Both libraries support *GObject Introspection* bindings. A simple application ex
Above libraries are licensed under `LGPL-2.1-or-later`. You are free to use them in your own projects as long as you comply with license terms.
Please note that until version 1.0 they should be considered as an unstable API (some things may change without prior notice).
Clapper `Vala` bindings are part of this repo, while `Rust` bindings can be found [here](https://gitlab.gnome.org/JanGernert/clapper-rs).
## Installation from Flatpak
The `Flatpak` package includes all required dependencies and codecs.
Additionally it also has a few patches, thus some functionalities work better in `Flatpak` version (until my changes are accepted upstream).
@@ -54,6 +56,8 @@ meson compile
sudo meson install
```
If you want to compile app as `Flatpak`, remember to clone this repo with `--recurse-submodules` option.
## Questions?
Feel free to ask me any questions. Come and talk on Matrix: [#clapper-player:matrix.org](https://matrix.to/#/#clapper-player:matrix.org)
@@ -68,5 +72,6 @@ so if your language is not available, please contact me first.
Many thanks to [sp1ritCS](https://github.com/sp1ritCS) for creating and maintaining package build files.
Big thanks to [bridadan](https://github.com/bridadan) and [Uniformbuffer3](https://github.com/Uniformbuffer3) for helping
with testing V4L2 and NVDEC hardware acceleration methods.
Also words of appreciation for [JanGernert](https://gitlab.gnome.org/JanGernert) who made and is sharing Clapper Rust bindings.
Thanks a lot to all the people who are supporting the development with their anonymous donations through [Liberapay](https://liberapay.com/Clapper/). I :heart: U.

View File

@@ -0,0 +1,57 @@
#!/usr/bin/env python3
import gi
gi.require_version('Adw', '1')
gi.require_version('Clapper', '0.0')
gi.require_version('ClapperGtk', '0.0')
gi.require_version('GLib', '2.0')
gi.require_version('Gtk', '4.0')
from gi.repository import Adw, Clapper, ClapperGtk, GLib, Gtk
import shutil
Clapper.init(None)
download_dir = GLib.build_filenamev([GLib.get_user_cache_dir(), "example_download_dir", None])
print('Using cache directory: {0}'.format(download_dir))
def on_download_complete(player, item, location):
# Media downloaded. Data from this file is still used for current playback (including seeking).
print('Download complete: {0} => {1}'.format(item.props.uri, location))
def on_activate(app):
win = Gtk.ApplicationWindow(application=app, default_width=640, default_height=396)
video = ClapperGtk.Video()
controls = ClapperGtk.SimpleControls(fullscreenable=False)
# Enable local storage caching and monitor it
video.props.player.set_download_dir(download_dir)
video.props.player.set_download_enabled(True)
video.props.player.connect('download-complete', on_download_complete)
# Configure playback
video.props.player.props.queue.set_progression_mode(Clapper.QueueProgressionMode.CAROUSEL)
video.props.player.set_autoplay(True)
# Assemble window
video.add_fading_overlay(controls)
win.set_child(video)
win.present()
# Create and add media for playback
item_1 = Clapper.MediaItem(uri='http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4')
item_2 = Clapper.MediaItem(uri='http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4')
video.props.player.props.queue.add_item(item_1)
video.props.player.props.queue.add_item(item_2)
# Create a new application
app = Adw.Application(application_id='com.example.ClapperDownloadCache')
app.connect('activate', on_activate)
# Run the application
app.run(None)
# Finally app should cleanup before exit. Possibly moving data to
# another dir if it wants to use it on next run and deleting what's
# left (so any unfinished downloads will also be removed).
print('Cleanup')
shutil.rmtree(download_dir)

View File

@@ -0,0 +1,45 @@
#!/usr/bin/env python3
import gi
gi.require_version('Adw', '1')
gi.require_version('Clapper', '0.0')
gi.require_version('ClapperGtk', '0.0')
gi.require_version('Gtk', '4.0')
from gi.repository import Adw, Clapper, ClapperGtk, Gtk
Clapper.init(None)
def on_toggle_fullscreen(video, win):
# Since this example uses only video inside normal window, all we
# need to toggle fullscreen is to invert its fullscreened property
win.props.fullscreened ^= True
def on_activate(app):
win = Gtk.ApplicationWindow(application=app, default_width=640, default_height=396)
video = ClapperGtk.Video()
controls = ClapperGtk.SimpleControls()
# This signal will be emitted when user requests fullscreen state change.
# It is app job to fullscreen video only (which might require something
# more than simply inverting fullscreen on the whole window).
video.connect('toggle-fullscreen', on_toggle_fullscreen, win)
# Create and add media for playback. First added media item to empty
# playback queue will be automatically selected.
item = Clapper.MediaItem(uri='http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4')
video.props.player.props.queue.add_item(item)
# Assemble window
video.add_fading_overlay(controls)
win.set_child(video)
win.present()
# Start playback
video.props.player.play()
# Create a new application
app = Adw.Application(application_id='com.example.ClapperSimple')
app.connect('activate', on_activate)
# Run the application
app.run(None)

View File

@@ -1,5 +1,5 @@
project('clapper', 'c',
version: '0.6.1',
version: '0.7.0',
meson_version: '>= 0.64.0',
license: 'LGPL-2.1-or-later AND GPL-3.0-or-later', # LGPL-2.1+ for libs and gst-plugin, GPL-3.0+ for app
default_options: [
@@ -9,7 +9,7 @@ project('clapper', 'c',
)
glib_req = '>= 2.76.0'
gst_req = '>= 1.20.0'
gst_req = '>= 1.24.0'
gtk4_req = '>= 4.10.0'
adw_req = '>= 1.4.0'
@@ -85,6 +85,11 @@ libadwaita_dep = dependency('libadwaita-1',
required: false,
)
# Optional
peas_dep = dependency('libpeas-2',
required: false,
)
cc = meson.get_compiler('c')
libm = cc.find_library('m', required: false)
@@ -147,6 +152,9 @@ summary('vapi', build_vapi ? 'Yes' : 'No', section: 'Build')
summary('doc', build_doc ? 'Yes' : 'No', section: 'Build')
if build_clapper
foreach name : clapper_possible_functionalities
summary(name, clapper_available_functionalities.contains(name) ? 'Yes' : 'No', section: 'Functionalities')
endforeach
foreach name : clapper_possible_features
summary(name, clapper_available_features.contains(name) ? 'Yes' : 'No', section: 'Features')
endforeach

View File

@@ -35,6 +35,13 @@ option('doc',
description: 'Build documentation'
)
# Functionalities
option('enhancers-loader',
type: 'feature',
value: 'enabled',
description: 'Ability to load libpeas based plugins that enhance capabilities'
)
# Features
option('discoverer',
type: 'feature',

View File

@@ -4,12 +4,12 @@
"runtime-version": "master",
"sdk": "org.gnome.Sdk",
"sdk-extensions": [
"org.freedesktop.Sdk.Extension.rust-nightly",
"org.freedesktop.Sdk.Extension.llvm16"
"org.freedesktop.Sdk.Extension.rust-stable",
"org.freedesktop.Sdk.Extension.llvm18"
],
"add-extensions": {
"org.freedesktop.Platform.ffmpeg-full": {
"version": "23.08",
"version": "24.08",
"directory": "lib/ffmpeg",
"add-ld-path": ".",
"no-autodownload": false,
@@ -29,13 +29,19 @@
"--filesystem=xdg-run/pipewire-0:ro",
"--filesystem=xdg-videos",
"--filesystem=xdg-run/gvfsd",
"--own-name=org.mpris.MediaPlayer2.Clapper",
"--own-name=org.mpris.MediaPlayer2.Clapper.*",
"--talk-name=org.gtk.vfs.*",
"--env=GST_PLUGIN_SYSTEM_PATH=/app/lib/gstreamer-1.0"
],
"build-options": {
"append-path": "/usr/lib/sdk/rust-nightly/bin:/usr/lib/sdk/llvm16/bin",
"prepend-ld-library-path": "/usr/lib/sdk/llvm16/lib"
"append-path": "/usr/lib/sdk/rust-stable/bin:/usr/lib/sdk/llvm18/bin",
"prepend-ld-library-path": "/usr/lib/sdk/llvm18/lib",
"env": {
"CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER": "clang",
"CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS": "-C link-arg=-fuse-ld=/usr/lib/sdk/rust-stable/bin/mold",
"CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER": "clang",
"CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUSTFLAGS": "-C link-arg=-fuse-ld=/usr/lib/sdk/rust-stable/bin/mold"
}
},
"modules": [
"flathub/shared-modules/gudev/gudev.json",
@@ -50,7 +56,8 @@
"testing/dav1d.json",
"testing/gstreamer.json",
"testing/gst-plugins-rs.json",
"testing/gtuber.json",
"testing/yt-dlp.json",
"testing/libpeas.json",
{
"name": "clapper",
"buildsystem": "meson",
@@ -60,7 +67,8 @@
"path": "../../."
}
]
}
},
"testing/clapper-enhancers.json"
],
"cleanup-commands": [
"mkdir -p /app/lib/ffmpeg",

View File

@@ -1,11 +1,11 @@
{
"app-id": "com.github.rafostar.Clapper",
"runtime": "org.gnome.Platform",
"runtime-version": "45",
"runtime-version": "47",
"sdk": "org.gnome.Sdk",
"add-extensions": {
"org.freedesktop.Platform.ffmpeg-full": {
"version": "23.08",
"version": "24.08",
"directory": "lib/ffmpeg",
"add-ld-path": ".",
"no-autodownload": false,
@@ -25,7 +25,7 @@
"--filesystem=xdg-run/pipewire-0:ro",
"--filesystem=xdg-videos",
"--filesystem=xdg-run/gvfsd",
"--own-name=org.mpris.MediaPlayer2.Clapper",
"--own-name=org.mpris.MediaPlayer2.Clapper.*",
"--talk-name=org.gtk.vfs.*",
"--env=GST_PLUGIN_SYSTEM_PATH=/app/lib/gstreamer-1.0"
],
@@ -40,21 +40,20 @@
"flathub/lib/libass.json",
"flathub/lib/uchardet.json",
"flathub/lib/libmicrodns.json",
"flathub/gstreamer-1.0/gstreamer.json",
"testing/gtuber.json",
"testing/gstreamer_stable.json",
"testing/yt-dlp.json",
"testing/libpeas.json",
{
"name": "clapper",
"buildsystem": "meson",
"config-opts": [
"-Dc_args=\"-DHAVE_GST_PATCHES=1\""
],
"sources": [
{
"type": "dir",
"path": "../../."
}
]
}
},
"testing/clapper-enhancers.json"
],
"cleanup-commands": [
"mkdir -p /app/lib/ffmpeg",

View File

@@ -0,0 +1,14 @@
{
"name": "clapper-enhancers",
"buildsystem": "meson",
"config-opts": [
"-Dauto_features=enabled"
],
"sources": [
{
"type": "git",
"url": "https://github.com/Rafostar/clapper-enhancers.git",
"branch": "main"
}
]
}

View File

@@ -0,0 +1,99 @@
{
"name": "gstreamer",
"buildsystem": "meson",
"config-opts": [
"--buildtype=release",
"--wrap-mode=nodownload",
"-Dbase=enabled",
"-Dgood=enabled",
"-Dbad=enabled",
"-Dugly=enabled",
"-Dlibav=enabled",
"-Dvaapi=enabled",
"-Dsharp=disabled",
"-Drs=disabled",
"-Dpython=disabled",
"-Ddevtools=disabled",
"-Dges=disabled",
"-Drtsp_server=disabled",
"-Dgst-examples=disabled",
"-Dqt5=disabled",
"-Dtests=disabled",
"-Dexamples=disabled",
"-Dintrospection=enabled",
"-Ddoc=disabled",
"-Dgtk_doc=disabled",
"-Dgpl=enabled",
"-Dgstreamer:benchmarks=disabled",
"-Dgstreamer:gobject-cast-checks=disabled",
"-Dgstreamer:glib-asserts=disabled",
"-Dgstreamer:glib-checks=disabled",
"-Dgstreamer:extra-checks=disabled",
"-Dgst-plugins-base:gobject-cast-checks=disabled",
"-Dgst-plugins-base:glib-asserts=disabled",
"-Dgst-plugins-base:glib-checks=disabled",
"-Dgst-plugins-base:gl_api=opengl,gles2",
"-Dgst-plugins-base:gl_platform=egl,glx",
"-Dgst-plugins-good:gobject-cast-checks=disabled",
"-Dgst-plugins-good:glib-asserts=disabled",
"-Dgst-plugins-good:glib-checks=disabled",
"-Dgst-plugins-good:gtk3=disabled",
"-Dgst-plugins-bad:gobject-cast-checks=disabled",
"-Dgst-plugins-bad:glib-asserts=disabled",
"-Dgst-plugins-bad:glib-checks=disabled",
"-Dgst-plugins-bad:extra-checks=disabled",
"-Dgst-plugins-bad:vulkan=disabled",
"-Dgst-plugins-bad:webrtc=disabled",
"-Dgst-plugins-bad:wasapi=disabled",
"-Dgst-plugins-bad:wasapi2=disabled",
"-Dgst-plugins-bad:winks=disabled",
"-Dgst-plugins-bad:winscreencap=disabled",
"-Dgst-plugins-bad:assrender=enabled",
"-Dgst-plugins-bad:nvcodec=enabled",
"-Dgst-plugins-bad:v4l2codecs=enabled",
"-Dgst-plugins-bad:va=enabled",
"-Dgst-plugins-ugly:gobject-cast-checks=disabled",
"-Dgst-plugins-ugly:glib-asserts=disabled",
"-Dgst-plugins-ugly:glib-checks=disabled",
"-Dgst-plugins-ugly:mpeg2dec=enabled"
],
"sources": [
{
"type": "git",
"url": "https://gitlab.freedesktop.org/gstreamer/gstreamer.git",
"tag": "1.24.9",
"commit": "b309f90bfde36e6d175b70bfa0c941f2829dd6a5",
"disable-submodules": true
},
{
"type": "patch",
"path": "../flathub/gstreamer-1.0/gst-libav-stop-caching-codecs.patch"
},
{
"type": "patch",
"path": "../flathub/gstreamer-1.0/gst-plugins-base-autodetect-subtitle-text-encoding.patch"
},
{
"type": "patch",
"path": "../flathub/gstreamer-1.0/gst-plugins-good-matroska-fix-attachments-detection.patch"
},
{
"type": "patch",
"path": "../flathub/gstreamer-1.0/gst-plugins-good-dashdemux2-play-last-subfragment.patch"
},
{
"type": "patch",
"path": "../flathub/gstreamer-1.0/gst-plugins-bad-dashdemux-sidx-range-download.patch"
},
{
"type": "patch",
"path": "../flathub/gstreamer-1.0/gst-plugins-bad-dashdemux-improve-initial-representation-selection.patch"
}
]
}

View File

@@ -1,20 +0,0 @@
{
"name": "gtuber",
"buildsystem": "meson",
"config-opts": [
"-Dintrospection=disabled",
"-Dvapi=disabled",
"-Dgst-gtuber=enabled"
],
"cleanup": [
"/include",
"/lib/pkgconfig"
],
"sources": [
{
"type": "git",
"url": "https://github.com/Rafostar/gtuber.git",
"branch": "main"
}
]
}

View File

@@ -0,0 +1,21 @@
{
"name": "libpeas",
"buildsystem": "meson",
"config-opts": [
"--wrap-mode=nodownload",
"-Dgjs=false",
"-Dlua51=false",
"-Dintrospection=false"
],
"sources": [
{
"type": "archive",
"url": "https://download.gnome.org/sources/libpeas/2.0/libpeas-2.0.5.tar.xz",
"sha256": "376f2f73d731b54e13ddbab1d91b6382cf6a980524def44df62add15489de6dd",
"x-checker-data": {
"type": "gnome",
"name": "libpeas"
}
}
]
}

View File

@@ -0,0 +1,19 @@
{
"name": "yt-dlp",
"buildsystem": "simple",
"build-commands": [
"pip3 install -v --root-user-action=ignore --no-deps --prefix=/app *.whl"
],
"sources": [
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/bb/68/548f9819b41d53561d4f3d39588111cf39993c066b6e5300b4ae118eb2e6/yt_dlp-2024.10.22-py3-none-any.whl",
"sha256": "ba166602ebe22a220e4dc1ead45bf00eb469ed812b22f4fb8bb54734f9b02084",
"x-checker-data": {
"type": "pypi",
"name": "yt-dlp",
"packagetype": "bdist_wheel"
}
}
]
}

View File

@@ -19,6 +19,7 @@
#include <glib/gi18n.h>
#include <gst/gst.h>
#include <clapper/clapper.h>
#include <adwaita.h>
#include "clapper-app-about-window.h"
@@ -39,6 +40,9 @@ clapper_app_about_window_new (GtkApplication *gtk_app)
gtk_window_set_modal (GTK_WINDOW (about), TRUE);
gtk_window_set_transient_for (GTK_WINDOW (about), window);
/* Also show development versions */
adw_about_window_set_version (about, CLAPPER_VERSION_S);
/* TRANSLATORS: Put your name(s) here for credits or leave untranslated */
adw_about_window_set_translator_credits (about, _("translator-credits"));

View File

@@ -18,6 +18,7 @@
#include "config.h"
#include <math.h>
#include <glib/gi18n.h>
#include <gst/gst.h>
#include <clapper-gtk/clapper-gtk.h>
@@ -26,6 +27,7 @@
#include "clapper-app-file-dialog.h"
#include "clapper-app-uri-dialog.h"
#include "clapper-app-info-window.h"
#include "clapper-app-pipeline-window.h"
#include "clapper-app-preferences-window.h"
#include "clapper-app-about-window.h"
#include "clapper-app-utils.h"
@@ -44,6 +46,9 @@ struct _ClapperAppApplication
gboolean need_init_state;
};
#define parent_class clapper_app_application_parent_class
G_DEFINE_TYPE (ClapperAppApplication, clapper_app_application, GTK_TYPE_APPLICATION);
struct ClapperPluginFeatureData
{
const gchar *name;
@@ -63,8 +68,147 @@ typedef struct
const gchar *accels[3];
} ClapperAppShortcut;
#define parent_class clapper_app_application_parent_class
G_DEFINE_TYPE (ClapperAppApplication, clapper_app_application, GTK_TYPE_APPLICATION);
static gboolean
clapper_app_options_get (const gchar *key, const gchar *format, GVariantDict *options,
GObject *src_object, GSettings *settings, gpointer output)
{
if (options && g_variant_dict_lookup (options, key, format, output)) {
return TRUE;
} else if (src_object) {
g_object_get (src_object, key, output, NULL);
return TRUE;
} else if (settings) {
g_settings_get (settings, key, format, output);
return TRUE;
}
return FALSE;
}
static gboolean
clapper_app_options_get_extra (const gchar *key, GVariantDict *options,
const gchar *extra_value, GSettings *settings, gchar **output)
{
if (options && g_variant_dict_lookup (options, key, "s", output)) {
return TRUE;
} else if (extra_value) {
*output = g_strdup (extra_value);
return TRUE;
} else if (settings) {
*output = g_settings_get_string (settings, key);
/* Ensure non-empty string */
if (*output) {
if (strlen (*output) > 0)
return TRUE;
else
g_clear_pointer (output, g_free);
}
}
return FALSE;
}
/*
* Apply options to @dest_window. Option providers will be used in args order.
* If any arg is %NULL it will not be used. For example, passing %NULL as
* @settings will avoid restoring values to @dest_window from GSettings.
*/
static void
clapper_app_apply_options_to_window (ClapperAppWindow *dest_window, GVariantDict *options,
ClapperAppWindow *src_window, GSettings *settings)
{
ClapperPlayer *dest_player;
ClapperAppWindowExtraOptions *src_extra_opts = NULL, *dest_extra_opts = NULL;
GObject *src_player_obj = NULL;
GObject *src_queue_obj = NULL;
gchar *option_str;
gdouble option_dbl;
gint option_int;
gboolean option_bool;
GST_DEBUG ("Applying options to window: %p", dest_window);
dest_player = clapper_app_window_get_player (dest_window);
dest_extra_opts = clapper_app_window_get_extra_options (dest_window);
if (src_window) {
src_player_obj = (GObject *) clapper_app_window_get_player (src_window);
src_queue_obj = (GObject *) clapper_player_get_queue (CLAPPER_PLAYER_CAST (src_player_obj));
src_extra_opts = clapper_app_window_get_extra_options (src_window);
}
/* Apply player values, clamp them to be within allowed range */
if (clapper_app_options_get ("volume", "d", options, src_player_obj, settings, &option_dbl))
clapper_player_set_volume (dest_player, PERCENTAGE_ROUND (CLAMP (option_dbl, 0, 2.0)));
if (clapper_app_options_get ("mute", "b", NULL, src_player_obj, settings, &option_bool))
clapper_player_set_mute (dest_player, option_bool);
if (clapper_app_options_get ("speed", "d", options, src_player_obj, settings, &option_dbl))
clapper_player_set_speed (dest_player, PERCENTAGE_ROUND (CLAMP (option_dbl, 0.05, 2.0)));
if (clapper_app_options_get ("adaptive-start-bitrate", "i", options, src_player_obj, settings, &option_int))
clapper_player_set_adaptive_start_bitrate (dest_player, option_int);
if (clapper_app_options_get ("progression-mode", "i", options, src_queue_obj, settings, &option_int))
clapper_queue_set_progression_mode (clapper_player_get_queue (dest_player), CLAMP (option_int, 0, 4));
if (clapper_app_options_get ("subtitles-enabled", "b", NULL, src_player_obj, settings, &option_bool))
clapper_player_set_subtitles_enabled (dest_player, option_bool);
if (clapper_app_options_get_extra ("video-filter", options,
(src_extra_opts) ? src_extra_opts->video_filter : NULL, NULL, &option_str)) {
clapper_player_set_video_filter (dest_player, clapper_app_utils_make_element (option_str));
g_free (dest_extra_opts->video_filter);
dest_extra_opts->video_filter = option_str;
}
if (clapper_app_options_get_extra ("audio-filter", options,
(src_extra_opts) ? src_extra_opts->audio_filter : NULL, NULL, &option_str)) {
clapper_player_set_audio_filter (dest_player, clapper_app_utils_make_element (option_str));
g_free (dest_extra_opts->audio_filter);
dest_extra_opts->audio_filter = option_str;
}
if (clapper_app_options_get_extra ("video-sink", options,
(src_extra_opts) ? src_extra_opts->video_sink : NULL, NULL, &option_str)) {
clapper_player_set_video_sink (dest_player, clapper_app_utils_make_element (option_str));
g_free (dest_extra_opts->video_sink);
dest_extra_opts->video_sink = option_str;
}
if (clapper_app_options_get_extra ("audio-sink", options,
(src_extra_opts) ? src_extra_opts->audio_sink : NULL, NULL, &option_str)) {
clapper_player_set_audio_sink (dest_player, clapper_app_utils_make_element (option_str));
g_free (dest_extra_opts->audio_sink);
dest_extra_opts->audio_sink = option_str;
}
/* Apply window options */
if ((options && g_variant_dict_contains (options, "fullscreen"))
|| (settings && g_settings_get_boolean (settings, "fullscreened")))
gtk_window_fullscreen (GTK_WINDOW (dest_window));
else if (settings && g_settings_get_boolean (settings, "maximized"))
gtk_window_maximize (GTK_WINDOW (dest_window));
GST_DEBUG ("Options applied");
}
static inline void
_store_settings_from_window (ClapperAppApplication *self, ClapperAppWindow *app_window)
{
ClapperPlayer *player = clapper_app_window_get_player (app_window);
ClapperQueue *queue = clapper_player_get_queue (player);
GtkWindow *window = GTK_WINDOW (app_window);
GST_DEBUG ("Storing current configuration to GSettings");
g_settings_set_double (self->settings, "volume", clapper_player_get_volume (player));
g_settings_set_boolean (self->settings, "mute", clapper_player_get_mute (player));
g_settings_set_double (self->settings, "speed", clapper_player_get_speed (player));
g_settings_set_int (self->settings, "adaptive-start-bitrate",
CLAMP (clapper_player_get_adaptive_bandwidth (player) * 0.8, 0, G_MAXINT));
g_settings_set_boolean (self->settings, "subtitles-enabled", clapper_player_get_subtitles_enabled (player));
g_settings_set_int (self->settings, "progression-mode", clapper_queue_get_progression_mode (queue));
g_settings_set_boolean (self->settings, "maximized", gtk_window_is_maximized (window));
g_settings_set_boolean (self->settings, "fullscreened", gtk_window_is_fullscreen (window));
GST_DEBUG ("Configuration stored");
}
static inline void
_set_initial_plugin_feature_ranks (void)
@@ -190,6 +334,26 @@ add_uri (GSimpleAction *action, GVariant *param, gpointer user_data)
clapper_app_uri_dialog_open_uri (gtk_app);
}
static void
new_window (GSimpleAction *action, GVariant *param, gpointer user_data)
{
GtkApplication *gtk_app = GTK_APPLICATION (user_data);
GtkWidget *stack = gtk_window_get_child (gtk_application_get_active_window (gtk_app));
const gchar *child_name = gtk_stack_get_visible_child_name (GTK_STACK (stack));
/* Do not allow to open new windows during initial state,
* there already is a free one to use */
if (g_strcmp0 (child_name, "initial_state") != 0) {
ClapperAppWindow *src_window, *dest_window;
src_window = CLAPPER_APP_WINDOW_CAST (gtk_application_get_active_window (gtk_app));
dest_window = CLAPPER_APP_WINDOW_CAST (clapper_app_window_new (gtk_app));
clapper_app_apply_options_to_window (dest_window, NULL, src_window, NULL);
gtk_window_present (GTK_WINDOW (dest_window));
}
}
static void
show_preferences (GSimpleAction *action, GVariant *param, gpointer user_data)
{
@@ -215,6 +379,32 @@ show_info (GSimpleAction *action, GVariant *param, gpointer user_data)
gtk_window_present (GTK_WINDOW (info_window));
}
static void
show_pipeline (GSimpleAction *action, GVariant *param, gpointer user_data)
{
GtkApplication *gtk_app = GTK_APPLICATION (user_data);
GtkWidget *pipeline_window;
GtkWindow *window;
ClapperPlayer *player;
window = gtk_application_get_active_window (gtk_app);
/* Already open */
if (CLAPPER_APP_IS_PIPELINE_WINDOW (window))
return;
while (window && !CLAPPER_APP_IS_WINDOW (window))
window = gtk_window_get_transient_for (window);
if (G_UNLIKELY (window == NULL))
return;
player = clapper_app_window_get_player (CLAPPER_APP_WINDOW (window));
pipeline_window = clapper_app_pipeline_window_new (gtk_app, player);
gtk_window_present (GTK_WINDOW (pipeline_window));
}
static void
show_about (GSimpleAction *action, GVariant *param, gpointer user_data)
{
@@ -225,55 +415,12 @@ show_about (GSimpleAction *action, GVariant *param, gpointer user_data)
gtk_window_present (GTK_WINDOW (about_window));
}
static inline void
_restore_settings_to_window (ClapperAppApplication *self, ClapperAppWindow *app_window)
{
ClapperPlayer *player = clapper_app_window_get_player (app_window);
ClapperQueue *queue = clapper_player_get_queue (player);
GST_DEBUG ("Restoring saved GSettings values to: %" GST_PTR_FORMAT, app_window);
clapper_player_set_volume (player, PERCENTAGE_ROUND (g_settings_get_double (self->settings, "volume")));
clapper_player_set_mute (player, g_settings_get_boolean (self->settings, "mute"));
clapper_player_set_speed (player, PERCENTAGE_ROUND (g_settings_get_double (self->settings, "speed")));
clapper_player_set_subtitles_enabled (player, g_settings_get_boolean (self->settings, "subtitles-enabled"));
clapper_queue_set_progression_mode (queue, g_settings_get_int (self->settings, "progression-mode"));
if (g_settings_get_boolean (self->settings, "fullscreened"))
gtk_window_fullscreen (GTK_WINDOW (app_window));
else if (g_settings_get_boolean (self->settings, "maximized"))
gtk_window_maximize (GTK_WINDOW (app_window));
GST_DEBUG ("Configuration restored");
}
static inline void
_store_settings_from_window (ClapperAppApplication *self, ClapperAppWindow *app_window)
{
ClapperPlayer *player = clapper_app_window_get_player (app_window);
ClapperQueue *queue = clapper_player_get_queue (player);
GtkWindow *window = GTK_WINDOW (app_window);
GST_DEBUG ("Storing current configuration to GSettings");
g_settings_set_double (self->settings, "volume", clapper_player_get_volume (player));
g_settings_set_boolean (self->settings, "mute", clapper_player_get_mute (player));
g_settings_set_double (self->settings, "speed", clapper_player_get_speed (player));
g_settings_set_boolean (self->settings, "subtitles-enabled", clapper_player_get_subtitles_enabled (player));
g_settings_set_int (self->settings, "progression-mode", clapper_queue_get_progression_mode (queue));
g_settings_set_boolean (self->settings, "maximized", gtk_window_is_maximized (window));
g_settings_set_boolean (self->settings, "fullscreened", gtk_window_is_fullscreen (window));
GST_DEBUG ("Configuration stored");
}
GApplication *
clapper_app_application_new (void)
{
return g_object_new (CLAPPER_APP_TYPE_APPLICATION,
"application-id", CLAPPER_APP_ID,
"flags", G_APPLICATION_HANDLES_OPEN,
"flags", G_APPLICATION_HANDLES_OPEN | G_APPLICATION_HANDLES_COMMAND_LINE,
NULL);
}
@@ -306,14 +453,18 @@ static void
clapper_app_application_activate (GApplication *app)
{
ClapperAppApplication *self = CLAPPER_APP_APPLICATION_CAST (app);
GtkApplication *gtk_app = GTK_APPLICATION (app);
GtkWindow *window;
GST_INFO ("Activate");
G_APPLICATION_CLASS (parent_class)->activate (app);
if (!(window = gtk_application_get_active_window (GTK_APPLICATION (app)))) {
window = GTK_WINDOW (clapper_app_window_new (GTK_APPLICATION (app)));
_restore_settings_to_window (self, CLAPPER_APP_WINDOW_CAST (window));
/* When activated through DBus command line does not run,
* so create our first window here instead */
if (!(window = gtk_application_get_active_window (gtk_app))) {
window = GTK_WINDOW (clapper_app_window_new (gtk_app));
clapper_app_apply_options_to_window (CLAPPER_APP_WINDOW_CAST (window),
NULL, NULL, self->settings);
}
if (self->need_init_state) {
@@ -331,6 +482,8 @@ clapper_app_application_local_command_line (GApplication *app,
gchar **argv = *arguments;
guint i;
GST_INFO ("Handling local command line");
/* NOTE: argv is never NULL, so no need to check */
for (i = 0; argv[i]; ++i) {
@@ -344,6 +497,43 @@ clapper_app_application_local_command_line (GApplication *app,
return G_APPLICATION_CLASS (parent_class)->local_command_line (app, arguments, exit_status);
}
static gint
clapper_app_application_command_line (GApplication *app, GApplicationCommandLine *cmd_line)
{
ClapperAppApplication *self = CLAPPER_APP_APPLICATION_CAST (app);
ClapperAppWindow *src_window = NULL, *dest_window = NULL;
GtkApplication *gtk_app = GTK_APPLICATION (app);
GVariantDict *options;
GFile **files = NULL;
GSettings *settings;
gint n_files = 0;
GST_INFO ("Handling command line");
options = g_application_command_line_get_options_dict (cmd_line);
dest_window = CLAPPER_APP_WINDOW_CAST (gtk_application_get_active_window (gtk_app));
/* Restore settings only once by making them %NULL when run again */
settings = (!dest_window) ? self->settings : NULL;
if (!dest_window || g_variant_dict_contains (options, "new-window")) {
src_window = dest_window;
dest_window = CLAPPER_APP_WINDOW_CAST (clapper_app_window_new (gtk_app));
}
clapper_app_apply_options_to_window (dest_window, options, src_window, settings);
if (clapper_app_utils_files_from_command_line (cmd_line, &files, &n_files)) {
g_application_open (app, files, n_files,
(g_variant_dict_contains (options, "enqueue")) ? "add-only" : "");
clapper_app_utils_files_free (files);
} else {
g_application_activate (app);
}
return EXIT_SUCCESS;
}
static gboolean
_is_claps_file (GFile *file)
{
@@ -512,20 +702,40 @@ clapper_app_application_constructed (GObject *object)
GApplication *app = G_APPLICATION (self);
guint i;
static const GActionEntry app_entries[] = {
const GOptionEntry app_options[] = {
{ "new-window", 'n', 0, G_OPTION_ARG_NONE, NULL, _("Create a new window"), NULL },
{ "enqueue", 0, 0, G_OPTION_ARG_NONE, NULL, _("Add media to queue in primary application instance"), NULL },
{ "volume", 0, 0, G_OPTION_ARG_DOUBLE, NULL, _("Audio volume to set (0 - 2.0 range)"), NULL },
{ "speed", 0, 0, G_OPTION_ARG_DOUBLE, NULL, _("Playback speed to set (0.05 - 2.0 range)"), NULL },
{ "adaptive-start-bitrate", 0, 0, G_OPTION_ARG_INT, NULL, _("Initial bitrate for adaptive streaming"), NULL },
{ "progression-mode", 0, 0, G_OPTION_ARG_INT, NULL, _("Initial queue progression mode (0=none, 1=consecutive, 2=repeat-item, 3=carousel, 4=shuffle)"), NULL },
{ "fullscreen", 'f', 0, G_OPTION_ARG_NONE, NULL, _("Set window to be fullscreen"), NULL },
{ "video-filter", 0, 0, G_OPTION_ARG_STRING, NULL, _("Video filter to use (\"none\" to disable)"), NULL },
{ "audio-filter", 0, 0, G_OPTION_ARG_STRING, NULL, _("Audio filter to use (\"none\" to disable)"), NULL },
{ "video-sink", 0, 0, G_OPTION_ARG_STRING, NULL, _("Video sink to use"), NULL },
{ "audio-sink", 0, 0, G_OPTION_ARG_STRING, NULL, _("Audio sink to use"), NULL },
{ NULL }
};
static const GActionEntry app_actions[] = {
{ "add-files", add_files, NULL, NULL, NULL },
{ "add-uri", add_uri, NULL, NULL, NULL },
{ "new-window", new_window, NULL, NULL, NULL },
{ "info", show_info, NULL, NULL, NULL },
{ "pipeline", show_pipeline, NULL, NULL, NULL },
{ "preferences", show_preferences, NULL, NULL, NULL },
{ "about", show_about, NULL, NULL, NULL },
};
static const ClapperAppShortcut app_shortcuts[] = {
{ "app.add-files", { "<Control>o", NULL, NULL }},
{ "app.add-uri", { "<Control>u", NULL, NULL }},
{ "app.new-window", { "<Control>n", NULL, NULL }},
{ "app.info", { "<Control>i", NULL, NULL }},
{ "app.pipeline", { "<Control><Shift>p", NULL, NULL }},
{ "app.preferences", { "<Control>comma", NULL, NULL }},
{ "app.about", { "F1", NULL, NULL }},
{ "win.toggle-fullscreen", { "F11", "f", NULL }},
{ "win.unfullscreen", { "Escape", NULL, NULL }},
{ "win.auto-resize", { "<Super>r", NULL, NULL }},
{ "win.show-help-overlay", { "<Control>question", NULL, NULL }},
{ "window.close", { "<Control>q", "q", NULL }},
};
@@ -542,11 +752,14 @@ clapper_app_application_constructed (GObject *object)
plugin_feature_ranks_settings_changed_cb (self->settings, NULL, NULL);
g_action_map_add_action_entries (G_ACTION_MAP (app),
app_entries, G_N_ELEMENTS (app_entries), app);
app_actions, G_N_ELEMENTS (app_actions), app);
for (i = 0; i < G_N_ELEMENTS (app_shortcuts); ++i)
gtk_application_set_accels_for_action (GTK_APPLICATION (app), app_shortcuts[i].action, app_shortcuts[i].accels);
g_application_set_option_context_parameter_string (app, "[URI1|FILE1] [URI2|FILE2] …");
g_application_add_main_option_entries (app, app_options);
g_application_add_option_group (app, gst_init_get_option_group ());
G_OBJECT_CLASS (parent_class)->constructed (object);
@@ -581,5 +794,6 @@ clapper_app_application_class_init (ClapperAppApplicationClass *klass)
application_class->activate = clapper_app_application_activate;
application_class->local_command_line = clapper_app_application_local_command_line;
application_class->command_line = clapper_app_application_command_line;
application_class->open = clapper_app_application_open;
}

View File

@@ -80,8 +80,15 @@ _dialog_add_mime_types (GtkFileDialog *dialog, const gchar *filter_name,
GtkFileFilter *filter = gtk_file_filter_new ();
guint i;
for (i = 0; mime_types[i]; ++i)
/* XXX: Windows does not support mime-types file
* filters, so use file extensions instead */
for (i = 0; mime_types[i]; ++i) {
#ifndef G_OS_WIN32
gtk_file_filter_add_mime_type (filter, mime_types[i]);
#else
gtk_file_filter_add_suffix (filter, mime_types[i]);
#endif
}
gtk_file_filter_set_name (filter, filter_name);
g_list_store_append (filters, filter);
@@ -99,7 +106,11 @@ clapper_app_file_dialog_open_files (GtkApplication *gtk_app)
GtkFileDialog *dialog = gtk_file_dialog_new ();
_dialog_add_mime_types (dialog, "Media Files",
#ifndef G_OS_WIN32
clapper_app_utils_get_mime_types ());
#else
clapper_app_utils_get_extensions ());
#endif
gtk_file_dialog_set_modal (dialog, TRUE);
gtk_file_dialog_set_title (dialog, "Add Files");
@@ -118,7 +129,11 @@ clapper_app_file_dialog_open_subtitles (GtkApplication *gtk_app, ClapperMediaIte
GtkFileDialog *dialog = gtk_file_dialog_new ();
_dialog_add_mime_types (dialog, "Subtitles",
#ifndef G_OS_WIN32
clapper_app_utils_get_subtitles_mime_types ());
#else
clapper_app_utils_get_subtitles_extensions ());
#endif
gtk_file_dialog_set_modal (dialog, TRUE);
gtk_file_dialog_set_title (dialog, "Open Subtitles");

View File

@@ -0,0 +1,830 @@
/* Clapper Application
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <gst/gst.h>
#include <graphviz/cgraph.h>
#include <graphviz/gvc.h>
#include <librsvg/rsvg.h>
#include <math.h>
#include "clapper-app-pipeline-viewer.h"
#define GST_CAT_DEFAULT clapper_app_pipeline_viewer_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperAppPipelineViewer
{
GtkWidget parent;
GtkAdjustment *hadjustment;
GtkAdjustment *vadjustment;
GdkTexture *preview_texture;
GdkTexture *texture;
RsvgHandle *handle;
gdouble intrinsic_w;
gdouble intrinsic_h;
graphene_rect_t viewport;
gdouble zoom;
gboolean zooming;
gdouble pointer_x;
gdouble pointer_y;
gdouble drag_adj_x;
gdouble drag_adj_y;
gint allocated_width;
gint allocated_height;
ClapperPlayer *player;
GCancellable *cancellable;
gboolean running;
gboolean pending_preview;
gboolean pending_refresh;
guint refresh_id;
};
enum
{
PROP_0,
PROP_HADJUSTMENT,
PROP_VADJUSTMENT,
PROP_HSCROLL_POLICY,
PROP_VSCROLL_POLICY,
PROP_LAST
};
static void
_scrollable_iface_init (GtkScrollableInterface *iface)
{
}
#define parent_class clapper_app_pipeline_viewer_parent_class
G_DEFINE_TYPE_WITH_CODE (ClapperAppPipelineViewer, clapper_app_pipeline_viewer, GTK_TYPE_WIDGET,
G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, _scrollable_iface_init));
static void clapper_app_pipeline_viewer_preview (ClapperAppPipelineViewer *self);
static void clapper_app_pipeline_viewer_refresh (ClapperAppPipelineViewer *self);
typedef struct
{
ClapperPlayer *player;
RsvgHandle *handle;
gdouble intrinsic_w;
gdouble intrinsic_h;
graphene_rect_t viewport;
gdouble zoom;
gint scale_factor;
} ClapperAppPipelineViewerData;
static ClapperAppPipelineViewerData *
_thread_data_create (ClapperAppPipelineViewer *self)
{
ClapperAppPipelineViewerData *data;
data = g_new (ClapperAppPipelineViewerData, 1);
data->player = gst_object_ref (self->player);
data->handle = (self->handle) ? g_object_ref (self->handle) : NULL;
data->intrinsic_w = self->intrinsic_w;
data->intrinsic_h = self->intrinsic_h;
graphene_rect_init_from_rect (&data->viewport, &self->viewport);
data->zoom = self->zoom;
data->scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (self));
GST_TRACE ("Created render data: %p", data);
return data;
}
static void
_thread_data_free (ClapperAppPipelineViewerData *data)
{
GST_TRACE ("Freeing render data: %p", data);
gst_object_unref (data->player);
g_clear_object (&data->handle);
g_free (data);
}
static inline void
_set_cancelled_error (GError **error)
{
g_set_error (error, G_IO_ERROR,
G_IO_ERROR_CANCELLED, "Cancelled");
}
static inline void
_cancel_cancellable (ClapperAppPipelineViewer *self)
{
g_cancellable_cancel (self->cancellable);
g_object_unref (self->cancellable);
self->cancellable = g_cancellable_new ();
}
static void
_invalidate_viewport (ClapperAppPipelineViewer *self)
{
g_clear_object (&self->texture);
_cancel_cancellable (self);
gtk_widget_queue_resize (GTK_WIDGET (self));
}
static void
_refresh_viewport (ClapperAppPipelineViewer *self)
{
self->viewport.origin.x = gtk_adjustment_get_value (self->hadjustment);
self->viewport.origin.y = gtk_adjustment_get_value (self->vadjustment);
self->viewport.size.width = gtk_widget_get_width ((GtkWidget *) self);
self->viewport.size.height = gtk_widget_get_height ((GtkWidget *) self);
clapper_app_pipeline_viewer_refresh (self);
gtk_widget_queue_resize (GTK_WIDGET (self));
self->refresh_id = 0;
}
static void
_set_refresh_viewport_timeout (ClapperAppPipelineViewer *self)
{
/* Wait a bit while adjustment still moves, then refresh */
g_clear_handle_id (&self->refresh_id, g_source_remove);
self->refresh_id = g_timeout_add_once (200, (GSourceOnceFunc) _refresh_viewport, self);
}
static RsvgHandle *
_load_pipeline_graph (ClapperAppPipelineViewer *self, ClapperPlayer *player,
GstDebugGraphDetails details, GCancellable *cancellable, GError **error)
{
RsvgHandle *handle = NULL;
gchar *dot_data, *img_data = NULL;
guint size = 0;
dot_data = clapper_player_make_pipeline_graph (player, details);
if (!g_cancellable_is_cancelled (cancellable)) {
Agraph_t *graph;
GVC_t *gvc;
graph = agmemread (dot_data);
gvc = gvContext ();
gvLayout (gvc, graph, "dot");
gvRenderData (gvc, graph, "svg", &img_data, &size);
agclose (graph);
gvFreeContext (gvc);
} else if (*error == NULL) {
_set_cancelled_error (error);
}
g_free (dot_data);
if (img_data) {
if (!g_cancellable_is_cancelled (cancellable))
handle = rsvg_handle_new_from_data ((const guint8 *) img_data, size, error);
else if (*error == NULL)
_set_cancelled_error (error);
g_free (img_data);
}
return handle;
}
static inline GdkTexture *
_create_texture_from_surface (cairo_surface_t *surface)
{
GdkTexture *texture;
GBytes *bytes;
bytes = g_bytes_new_with_free_func (cairo_image_surface_get_data (surface),
cairo_image_surface_get_height (surface) * cairo_image_surface_get_stride (surface),
(GDestroyNotify) cairo_surface_destroy,
cairo_surface_reference (surface));
texture = gdk_memory_texture_new (
cairo_image_surface_get_width (surface),
cairo_image_surface_get_height (surface),
GDK_MEMORY_DEFAULT,
bytes,
cairo_image_surface_get_stride (surface));
g_bytes_unref (bytes);
return texture;
}
static GdkTexture *
_render_texture (RsvgHandle *handle, const graphene_rect_t *viewport, gint render_w, gint render_h,
gint scale_factor, GCancellable *cancellable, GError **error)
{
GdkTexture *texture = NULL;
cairo_surface_t *surface;
cairo_t *cr;
gint x, y, w, h;
x = floor (viewport->origin.x * scale_factor);
y = floor (viewport->origin.y * scale_factor);
w = ceil (viewport->size.width * scale_factor);
h = ceil (viewport->size.height * scale_factor);
GST_DEBUG ("Creating surface, bb: (%i,%i,%i,%i)", x, y, w, h);
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h);
cr = cairo_create (surface);
if (!g_cancellable_is_cancelled (cancellable)) {
if (rsvg_handle_render_document (handle, cr, &(RsvgRectangle) {-x, -y, render_w, render_h}, error))
texture = _create_texture_from_surface (surface);
} else if (error == NULL) {
_set_cancelled_error (error);
}
cairo_destroy (cr);
cairo_surface_destroy (surface);
return texture;
}
static void
_preview_in_thread (GTask *task, ClapperAppPipelineViewer *self,
ClapperAppPipelineViewerData *data, GCancellable *cancellable)
{
GdkTexture *texture = NULL;
GError *error = NULL;
g_clear_object (&data->handle);
if ((data->handle = _load_pipeline_graph (self,
data->player, GST_DEBUG_GRAPH_SHOW_ALL, cancellable, &error))) {
rsvg_handle_set_dpi (data->handle, 90);
rsvg_handle_get_intrinsic_size_in_pixels (data->handle,
&data->intrinsic_w, &data->intrinsic_h);
texture = _render_texture (data->handle,
&GRAPHENE_RECT_INIT (0, 0, data->intrinsic_w, data->intrinsic_h),
ceil (data->intrinsic_w), ceil (data->intrinsic_h),
1, cancellable, &error);
}
if (texture)
g_task_return_pointer (task, texture, (GDestroyNotify) g_object_unref);
else
g_task_return_error (task, error);
}
static void
_refresh_in_thread (GTask *task, ClapperAppPipelineViewer *self,
ClapperAppPipelineViewerData *data, GCancellable *cancellable)
{
GdkTexture *texture;
GError *error = NULL;
gint render_w, render_h;
render_w = ceil (data->zoom * data->intrinsic_w * data->scale_factor);
render_h = ceil (data->zoom * data->intrinsic_h * data->scale_factor);
texture = _render_texture (data->handle, &data->viewport, render_w, render_h,
data->scale_factor, cancellable, &error);
if (texture)
g_task_return_pointer (task, texture, (GDestroyNotify) g_object_unref);
else
g_task_return_error (task, error);
}
static inline void
_finish_texture_task (ClapperAppPipelineViewer *self, GTask *task, GdkTexture **texture)
{
GError *error = NULL;
gboolean cancelled = FALSE;
g_clear_object (texture);
*texture = (GdkTexture *) g_task_propagate_pointer (task, &error);
if (error) {
if (error->domain != G_IO_ERROR || error->code != G_IO_ERROR_CANCELLED) {
GST_ERROR ("Error: %s", (error->message)
? error->message : "Could not render pipeline graph");
} else {
GST_DEBUG ("Refresh cancelled");
cancelled = TRUE;
}
g_error_free (error);
}
/* Resize will also trigger redraw. We need to always call resize,
* because regenerated image has slightly different dimensions each time. */
if (!cancelled)
gtk_widget_queue_resize (GTK_WIDGET (self));
self->running = FALSE;
if (self->pending_preview) {
clapper_app_pipeline_viewer_preview (self);
self->pending_preview = FALSE;
} else if (self->pending_refresh) {
clapper_app_pipeline_viewer_refresh (self);
self->pending_refresh = FALSE;
}
}
static void
clapper_app_pipeline_viewer_preview_cb (ClapperAppPipelineViewer *self,
GAsyncResult *res, gpointer user_data G_GNUC_UNUSED)
{
GTask *task = G_TASK (res);
ClapperAppPipelineViewerData *data;
data = (ClapperAppPipelineViewerData *) g_task_get_task_data (task);
g_clear_object (&self->handle);
self->handle = g_object_ref (data->handle);
self->intrinsic_w = data->intrinsic_w;
self->intrinsic_h = data->intrinsic_h;
_finish_texture_task (self, task, &self->preview_texture);
}
static void
clapper_app_pipeline_viewer_refresh_cb (ClapperAppPipelineViewer *self,
GAsyncResult *res, gpointer user_data G_GNUC_UNUSED)
{
_finish_texture_task (self, G_TASK (res), &self->texture);
}
static void
clapper_app_pipeline_viewer_preview (ClapperAppPipelineViewer *self)
{
GTask *task;
_cancel_cancellable (self);
if (self->running) {
self->pending_preview = TRUE;
return;
}
self->running = TRUE;
task = g_task_new (self, self->cancellable,
(GAsyncReadyCallback) clapper_app_pipeline_viewer_preview_cb, NULL);
g_task_set_task_data (task, _thread_data_create (self),
(GDestroyNotify) _thread_data_free);
g_task_run_in_thread (task, (GTaskThreadFunc) _preview_in_thread);
g_object_unref (task);
}
static void
clapper_app_pipeline_viewer_refresh (ClapperAppPipelineViewer *self)
{
GTask *task;
g_clear_object (&self->texture);
_cancel_cancellable (self);
if (self->running) {
self->pending_refresh = TRUE;
return;
}
self->running = TRUE;
task = g_task_new (self, self->cancellable,
(GAsyncReadyCallback) clapper_app_pipeline_viewer_refresh_cb, NULL);
g_task_set_task_data (task, _thread_data_create (self),
(GDestroyNotify) _thread_data_free);
g_task_run_in_thread (task, (GTaskThreadFunc) _refresh_in_thread);
g_object_unref (task);
}
static void
motion_cb (GtkEventControllerMotion *motion,
gdouble x, gdouble y, ClapperAppPipelineViewer *self)
{
self->pointer_x = x;
self->pointer_y = y;
}
static gboolean
scroll_cb (GtkEventControllerScroll *scroll,
gdouble dx, gdouble dy, ClapperAppPipelineViewer *self)
{
gdouble scale_factor, calc_scale, multiplier;
gdouble event_x, event_y, x = 0, y = 0;
scale_factor = (dy > 0) ? 0.9 : (dy < 0) ? 1.1 : 0;
if (scale_factor == 0)
return TRUE;
calc_scale = CLAMP (self->zoom * scale_factor, 0.1, 10.0);
if (G_APPROX_VALUE (calc_scale, self->zoom, FLT_EPSILON))
return TRUE;
GST_LOG_OBJECT (self, "Zoom to: %.2lf", calc_scale);
multiplier = calc_scale / self->zoom;
event_x = self->pointer_x - gtk_adjustment_get_value (self->hadjustment) * multiplier;
event_y = self->pointer_y - gtk_adjustment_get_value (self->vadjustment) * multiplier;
x = self->pointer_x * multiplier - event_x;
y = self->pointer_y * multiplier - event_y;
/* Do not act on adjustment changes here */
self->zooming = TRUE;
self->zoom = calc_scale;
_invalidate_viewport (self);
gtk_adjustment_set_upper (self->hadjustment, G_MAXDOUBLE);
gtk_adjustment_set_upper (self->vadjustment, G_MAXDOUBLE);
gtk_adjustment_set_value (self->hadjustment, x);
gtk_adjustment_set_value (self->vadjustment, y);
_set_refresh_viewport_timeout (self);
self->zooming = FALSE;
return TRUE;
}
static void
drag_begin_cb (GtkGestureDrag *drag, gdouble start_x, gdouble start_y,
ClapperAppPipelineViewer *self)
{
GdkCursor *cursor;
GST_DEBUG_OBJECT (self, "Drag begin");
cursor = gdk_cursor_new_from_name ("all-scroll", NULL);
gtk_widget_set_cursor (GTK_WIDGET (self), cursor);
g_object_unref (cursor);
self->drag_adj_x = gtk_adjustment_get_value (self->hadjustment);
self->drag_adj_y = gtk_adjustment_get_value (self->vadjustment);
}
static void
drag_update_cb (GtkGestureDrag *drag, gdouble offset_x, gdouble offset_y,
ClapperAppPipelineViewer *self)
{
gtk_adjustment_set_value (self->hadjustment, self->drag_adj_x - offset_x);
gtk_adjustment_set_value (self->vadjustment, self->drag_adj_y - offset_y);
}
static void
drag_end_cb (GtkGestureDrag *drag, gdouble offset_x, gdouble offset_y,
ClapperAppPipelineViewer *self)
{
GdkCursor *cursor;
GST_DEBUG_OBJECT (self, "Drag end");
cursor = gdk_cursor_new_from_name ("default", NULL);
gtk_widget_set_cursor (GTK_WIDGET (self), cursor);
g_object_unref (cursor);
}
static void
_adjustment_value_changed_cb (GtkAdjustment *adjustment, ClapperAppPipelineViewer *self)
{
if (self->zooming)
return;
_invalidate_viewport (self);
_set_refresh_viewport_timeout (self);
gtk_widget_queue_allocate ((GtkWidget *) self);
}
static void
_on_widget_size_changed (ClapperAppPipelineViewer *self)
{
if (!self->preview_texture)
return;
_invalidate_viewport (self);
_set_refresh_viewport_timeout (self);
}
static inline void
_set_adjustment (ClapperAppPipelineViewer *self,
GtkAdjustment **dest_adjustment, GtkAdjustment *adjustment)
{
if (*dest_adjustment)
g_signal_handlers_disconnect_by_func (*dest_adjustment, _adjustment_value_changed_cb, self);
if (!adjustment)
adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
g_clear_object (dest_adjustment);
*dest_adjustment = g_object_ref_sink (adjustment);
g_signal_connect (*dest_adjustment, "value-changed", G_CALLBACK (_adjustment_value_changed_cb), self);
}
static inline void
_set_adjustment_values (ClapperAppPipelineViewer *self, GtkAdjustment *adjustment,
gboolean is_rtl, gint viewport_size, gint upper)
{
gdouble value = gtk_adjustment_get_value (adjustment);
/* We clamp to the left in RTL mode */
if (adjustment == self->hadjustment && is_rtl) {
gdouble dist = gtk_adjustment_get_upper (adjustment) - value - gtk_adjustment_get_page_size (adjustment);
value = upper - dist - viewport_size;
}
gtk_adjustment_configure (adjustment, value, 0, upper,
viewport_size * 0.1, viewport_size * 0.9, viewport_size);
}
void
clapper_app_pipeline_viewer_set_player (ClapperAppPipelineViewer *self, ClapperPlayer *player)
{
gst_object_replace ((GstObject **) &self->player, GST_OBJECT_CAST (player));
clapper_app_pipeline_viewer_preview (self);
}
static void
clapper_app_pipeline_viewer_size_allocate (GtkWidget *widget,
gint width, gint height, gint baseline)
{
ClapperAppPipelineViewer *self = CLAPPER_APP_PIPELINE_VIEWER_CAST (widget);
gint sizes[2] = { width, height };
gboolean visible, is_rtl;
visible = gtk_widget_get_visible (widget);
is_rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
/* Update both at once, then notify */
g_object_freeze_notify (G_OBJECT (self->hadjustment));
g_object_freeze_notify (G_OBJECT (self->vadjustment));
if (visible) {
gint min = 0, nat = 0;
if (width != self->allocated_width || height != self->allocated_height)
_on_widget_size_changed (self);
gtk_widget_measure (widget, GTK_ORIENTATION_VERTICAL, -1, &min, &nat, NULL, NULL);
sizes[1] = MAX (sizes[1], nat);
gtk_widget_measure (widget, GTK_ORIENTATION_HORIZONTAL, sizes[1], &min, &nat, NULL, NULL);
sizes[0] = MAX (sizes[0], nat);
}
_set_adjustment_values (self, self->hadjustment, is_rtl, width, sizes[0]);
_set_adjustment_values (self, self->vadjustment, is_rtl, height, sizes[1]);
self->allocated_width = width;
self->allocated_height = height;
g_object_thaw_notify (G_OBJECT (self->hadjustment));
g_object_thaw_notify (G_OBJECT (self->vadjustment));
}
static void
clapper_app_pipeline_viewer_measure (GtkWidget *widget, GtkOrientation orientation,
gint for_size, gint *minimum, gint *natural,
gint *minimum_baseline, gint *natural_baseline)
{
ClapperAppPipelineViewer *self = CLAPPER_APP_PIPELINE_VIEWER_CAST (widget);
if (self->preview_texture) {
gdouble size;
size = (orientation == GTK_ORIENTATION_HORIZONTAL)
? self->intrinsic_w
: self->intrinsic_h;
*minimum = *natural = ceil (self->zoom * size);
} else {
GTK_WIDGET_CLASS (parent_class)->measure (widget, orientation,
for_size, minimum, natural, minimum_baseline, natural_baseline);
}
}
static GtkSizeRequestMode
clapper_app_pipeline_viewer_get_request_mode (GtkWidget *widget)
{
return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
}
static void
clapper_app_pipeline_viewer_snapshot (GtkWidget *widget, GtkSnapshot *snapshot)
{
ClapperAppPipelineViewer *self = CLAPPER_APP_PIPELINE_VIEWER_CAST (widget);
gint w, h, offset_x, offset_y;
gint widget_w, widget_h;
if (!self->preview_texture)
return;
GST_ERROR ("SNAPSHOT");
w = ceil (self->zoom * self->intrinsic_w);
h = ceil (self->zoom * self->intrinsic_h);
widget_w = gtk_widget_get_width (widget);
widget_h = gtk_widget_get_height (widget);
if (widget_w > w)
offset_x = floor ((gdouble) (widget_w - w) / 2);
else
offset_x = -floor (gtk_adjustment_get_value (self->hadjustment));
if (widget_h > h)
offset_y = floor ((gdouble) (widget_h - h) / 2);
else
offset_y = -floor (gtk_adjustment_get_value (self->vadjustment));
gtk_snapshot_push_clip (snapshot, &GRAPHENE_RECT_INIT (0, 0, widget_w, widget_h));
gtk_snapshot_save (snapshot);
gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (offset_x, offset_y));
if (self->texture)
gtk_snapshot_append_texture (snapshot, self->texture, &self->viewport);
else
gtk_snapshot_append_texture (snapshot, self->preview_texture, &GRAPHENE_RECT_INIT (0, 0, w, h));
gtk_snapshot_restore (snapshot);
gtk_snapshot_pop (snapshot);
}
static void
clapper_app_pipeline_viewer_unrealize (GtkWidget *widget)
{
ClapperAppPipelineViewer *self = CLAPPER_APP_PIPELINE_VIEWER_CAST (widget);
GST_TRACE_OBJECT (self, "Unrealize");
g_clear_handle_id (&self->refresh_id, g_source_remove);
_cancel_cancellable (self);
GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
}
static void
clapper_app_pipeline_viewer_init (ClapperAppPipelineViewer *self)
{
GtkEventController *controller;
_set_adjustment (self, &self->hadjustment, NULL);
_set_adjustment (self, &self->vadjustment, NULL);
graphene_rect_init (&self->viewport, 0, 0, 1, 1);
self->zoom = 0.5;
self->cancellable = g_cancellable_new ();
controller = gtk_event_controller_motion_new ();
g_signal_connect (controller, "motion", G_CALLBACK (motion_cb), self);
gtk_widget_add_controller (GTK_WIDGET (self), controller);
controller = gtk_event_controller_scroll_new (GTK_EVENT_CONTROLLER_SCROLL_VERTICAL);
gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE);
g_signal_connect (controller, "scroll", G_CALLBACK (scroll_cb), self);
gtk_widget_add_controller (GTK_WIDGET (self), controller);
controller = GTK_EVENT_CONTROLLER (gtk_gesture_drag_new ());
gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (controller), FALSE);
g_signal_connect (controller, "drag-begin", G_CALLBACK (drag_begin_cb), self);
g_signal_connect (controller, "drag-update", G_CALLBACK (drag_update_cb), self);
g_signal_connect (controller, "drag-end", G_CALLBACK (drag_end_cb), self);
gtk_widget_add_controller (GTK_WIDGET (self), controller);
}
static void
clapper_app_pipeline_viewer_dispose (GObject *object)
{
ClapperAppPipelineViewer *self = CLAPPER_APP_PIPELINE_VIEWER_CAST (object);
if (self->hadjustment) {
g_signal_handlers_disconnect_by_func (self->hadjustment, _adjustment_value_changed_cb, self);
g_clear_object (&self->hadjustment);
}
if (self->vadjustment) {
g_signal_handlers_disconnect_by_func (self->vadjustment, _adjustment_value_changed_cb, self);
g_clear_object (&self->vadjustment);
}
g_clear_object (&self->preview_texture);
g_clear_object (&self->texture);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
clapper_app_pipeline_viewer_finalize (GObject *object)
{
ClapperAppPipelineViewer *self = CLAPPER_APP_PIPELINE_VIEWER_CAST (object);
GST_TRACE_OBJECT (self, "Finalize");
g_clear_object (&self->cancellable);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
clapper_app_pipeline_viewer_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
ClapperAppPipelineViewer *self = CLAPPER_APP_PIPELINE_VIEWER_CAST (object);
switch (prop_id) {
case PROP_HADJUSTMENT:
g_value_set_object (value, self->hadjustment);
break;
case PROP_VADJUSTMENT:
g_value_set_object (value, self->vadjustment);
break;
case PROP_HSCROLL_POLICY:
case PROP_VSCROLL_POLICY:
g_value_set_enum (value, GTK_SCROLL_NATURAL);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_app_pipeline_viewer_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
ClapperAppPipelineViewer *self = CLAPPER_APP_PIPELINE_VIEWER_CAST (object);
switch (prop_id) {
case PROP_HADJUSTMENT:
_set_adjustment (self, &self->hadjustment, g_value_get_object (value));
break;
case PROP_VADJUSTMENT:
_set_adjustment (self, &self->vadjustment, g_value_get_object (value));
break;
case PROP_HSCROLL_POLICY:
case PROP_VSCROLL_POLICY:
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_app_pipeline_viewer_class_init (ClapperAppPipelineViewerClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperapppipelineviewer", 0,
"Clapper App Pipeline Viewer");
gobject_class->get_property = clapper_app_pipeline_viewer_get_property;
gobject_class->set_property = clapper_app_pipeline_viewer_set_property;
gobject_class->dispose = clapper_app_pipeline_viewer_dispose;
gobject_class->finalize = clapper_app_pipeline_viewer_finalize;
g_object_class_override_property (gobject_class, PROP_HADJUSTMENT, "hadjustment");
g_object_class_override_property (gobject_class, PROP_VADJUSTMENT, "vadjustment");
g_object_class_override_property (gobject_class, PROP_HSCROLL_POLICY, "hscroll-policy");
g_object_class_override_property (gobject_class, PROP_VSCROLL_POLICY, "vscroll-policy");
widget_class->size_allocate = clapper_app_pipeline_viewer_size_allocate;
widget_class->measure = clapper_app_pipeline_viewer_measure;
widget_class->get_request_mode = clapper_app_pipeline_viewer_get_request_mode;
widget_class->snapshot = clapper_app_pipeline_viewer_snapshot;
widget_class->unrealize = clapper_app_pipeline_viewer_unrealize;
gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_IMG);
}

View File

@@ -0,0 +1,41 @@
/* Clapper Application
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <glib.h>
#include <glib-object.h>
#include <gio/gio.h>
#include <gtk/gtk.h>
#include <clapper/clapper.h>
G_BEGIN_DECLS
#define CLAPPER_APP_TYPE_PIPELINE_VIEWER (clapper_app_pipeline_viewer_get_type())
#define CLAPPER_APP_PIPELINE_VIEWER_CAST(obj) ((ClapperAppPipelineViewer *)(obj))
G_DECLARE_FINAL_TYPE (ClapperAppPipelineViewer, clapper_app_pipeline_viewer, CLAPPER_APP, PIPELINE_VIEWER, GtkWidget)
void clapper_app_pipeline_viewer_set_player (ClapperAppPipelineViewer *pipeline_viewer, ClapperPlayer *player);
gboolean clapper_app_pipeline_viewer_focus (ClapperAppPipelineViewer *pipeline_viewer, const graphene_rect_t *viewport, gdouble zoom);
void clapper_app_pipeline_viewer_invalidate_viewport (ClapperAppPipelineViewer *pipeline_viewer);
gdouble clapper_app_pipeline_viewer_get_zoom (ClapperAppPipelineViewer *pipeline_viewer);
G_END_DECLS

View File

@@ -0,0 +1,103 @@
/* Clapper Application
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "clapper-app-pipeline-window.h"
#include "clapper-app-pipeline-viewer.h"
#define GST_CAT_DEFAULT clapper_app_pipeline_window_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperAppPipelineWindow
{
AdwWindow parent;
ClapperAppPipelineViewer *pipeline_viewer;
};
#define parent_class clapper_app_pipeline_window_parent_class
G_DEFINE_TYPE (ClapperAppPipelineWindow, clapper_app_pipeline_window, ADW_TYPE_WINDOW);
static gboolean
close_cb (GtkWidget *widget, GVariant *args G_GNUC_UNUSED,
gpointer user_data G_GNUC_UNUSED)
{
gtk_window_close (GTK_WINDOW (widget));
return TRUE;
}
GtkWidget *
clapper_app_pipeline_window_new (GtkApplication *gtk_app, ClapperPlayer *player)
{
ClapperAppPipelineWindow *window;
window = g_object_new (CLAPPER_APP_TYPE_PIPELINE_WINDOW,
"application", gtk_app,
"transient-for", gtk_application_get_active_window (gtk_app),
NULL);
clapper_app_pipeline_viewer_set_player (window->pipeline_viewer, player);
return GTK_WIDGET (window);
}
static void
clapper_app_pipeline_window_init (ClapperAppPipelineWindow *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
}
static void
clapper_app_pipeline_window_dispose (GObject *object)
{
gtk_widget_dispose_template (GTK_WIDGET (object), CLAPPER_APP_TYPE_PIPELINE_WINDOW);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
clapper_app_pipeline_window_finalize (GObject *object)
{
ClapperAppPipelineWindow *self = CLAPPER_APP_PIPELINE_WINDOW_CAST (object);
GST_TRACE_OBJECT (self, "Finalize");
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
clapper_app_pipeline_window_class_init (ClapperAppPipelineWindowClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperapppipelinewindow", 0,
"Clapper App Pipeline Window");
gobject_class->dispose = clapper_app_pipeline_window_dispose;
gobject_class->finalize = clapper_app_pipeline_window_finalize;
gtk_widget_class_set_template_from_resource (widget_class,
CLAPPER_APP_RESOURCE_PREFIX "/ui/clapper-app-pipeline-window.ui");
gtk_widget_class_add_binding (widget_class, GDK_KEY_Escape, 0, close_cb, NULL);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPipelineWindow, pipeline_viewer);
//gtk_widget_class_bind_template_callback (widget_class, refresh_button_clicked_cb);
}

View File

@@ -0,0 +1,36 @@
/* Clapper Application
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <glib.h>
#include <glib-object.h>
#include <gtk/gtk.h>
#include <adwaita.h>
#include <clapper/clapper.h>
G_BEGIN_DECLS
#define CLAPPER_APP_TYPE_PIPELINE_WINDOW (clapper_app_pipeline_window_get_type())
#define CLAPPER_APP_PIPELINE_WINDOW_CAST(obj) ((ClapperAppPipelineWindow *)(obj))
G_DECLARE_FINAL_TYPE (ClapperAppPipelineWindow, clapper_app_pipeline_window, CLAPPER_APP, PIPELINE_WINDOW, AdwWindow)
G_GNUC_INTERNAL
GtkWidget * clapper_app_pipeline_window_new (GtkApplication *gtk_app, ClapperPlayer *player);
G_END_DECLS

View File

@@ -0,0 +1,42 @@
/* Clapper Application
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "clapper-app-types.h"
#include "clapper-app-headerbar.h"
#include "clapper-app-media-item-box.h"
#include "clapper-app-property-row.h"
#include "clapper-app-queue-list.h"
#include "clapper-app-queue-progression-model.h"
#include "clapper-app-window-state-buttons.h"
/*
* clapper_app_types_init:
*
* Ensure private types that appear in UI files in order for
* GtkBuilder to be able to find them when building templates.
*/
inline void
clapper_app_types_init (void)
{
g_type_ensure (CLAPPER_APP_TYPE_HEADERBAR);
g_type_ensure (CLAPPER_APP_TYPE_MEDIA_ITEM_BOX);
g_type_ensure (CLAPPER_APP_TYPE_PROPERTY_ROW);
g_type_ensure (CLAPPER_APP_TYPE_QUEUE_LIST);
g_type_ensure (CLAPPER_APP_TYPE_QUEUE_PROGRESSION_MODEL);
g_type_ensure (CLAPPER_APP_TYPE_WINDOW_STATE_BUTTONS);
}

View File

@@ -0,0 +1,27 @@
/* Clapper Application
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <glib.h>
G_BEGIN_DECLS
G_GNUC_INTERNAL
void clapper_app_types_init (void);
G_END_DECLS

View File

@@ -23,6 +23,30 @@
#include "clapper-app-utils.h"
#include "clapper-app-media-item-box.h"
/* Useful only on Windows */
#ifdef G_OS_WIN32
const gchar *const *
clapper_app_utils_get_extensions (void)
{
static const gchar *const all_extensions[] = {
"avi", "claps", "m2ts", "mkv", "mov",
"mp4", "webm", "wmv", NULL
};
return all_extensions;
}
const gchar *const *
clapper_app_utils_get_subtitles_extensions (void)
{
static const gchar *const subs_extensions[] = {
"srt", "vtt", NULL
};
return subs_extensions;
}
#endif
const gchar *const *
clapper_app_utils_get_mime_types (void)
{
@@ -197,6 +221,30 @@ clapper_app_utils_files_from_string (const gchar *string, GFile ***files, gint *
return success;
}
gboolean
clapper_app_utils_files_from_command_line (GApplicationCommandLine *cmd_line, GFile ***files, gint *n_files)
{
GSList *slist = NULL;
gchar **argv;
gint i, argc = 0;
gboolean success;
argv = g_application_command_line_get_arguments (cmd_line, &argc);
for (i = 1; i < argc; ++i)
slist = g_slist_append (slist, g_application_command_line_create_file_for_arg (cmd_line, argv[i]));
g_strfreev (argv);
if (!slist)
return FALSE;
success = clapper_app_utils_files_from_slist (slist, files, n_files);
g_slist_free_full (slist, g_object_unref);
return success;
}
static inline gboolean
_files_from_file (GFile *file, GFile ***files, gint *n_files)
{
@@ -336,3 +384,29 @@ parse_overrides:
g_free (stored_overrides);
}
GstElement *
clapper_app_utils_make_element (const gchar *string)
{
gchar *char_loc;
if (strcmp (string, "none") == 0)
return NULL;
char_loc = strchr (string, ' ');
if (char_loc) {
GstElement *element;
GError *error = NULL;
element = gst_parse_bin_from_description (string, TRUE, &error);
if (error) {
GST_ERROR ("Bin parse error: \"%s\", reason: %s", string, error->message);
g_error_free (error);
}
return element;
}
return gst_element_factory_make (string, NULL);
}

View File

@@ -26,6 +26,14 @@ G_BEGIN_DECLS
typedef void (* ClapperAppUtilsIterRanks) (const gchar *feature_name, GstRank rank, gboolean from_env, gpointer user_data);
#ifdef G_OS_WIN32
G_GNUC_INTERNAL
const gchar *const * clapper_app_utils_get_extensions (void);
G_GNUC_INTERNAL
const gchar *const * clapper_app_utils_get_subtitles_extensions (void);
#endif
G_GNUC_INTERNAL
const gchar *const * clapper_app_utils_get_mime_types (void);
@@ -50,6 +58,9 @@ gboolean clapper_app_utils_files_from_slist (GSList *file_list, GFile ***files,
G_GNUC_INTERNAL
gboolean clapper_app_utils_files_from_string (const gchar *string, GFile ***files, gint *n_files);
G_GNUC_INTERNAL
gboolean clapper_app_utils_files_from_command_line (GApplicationCommandLine *cmd_line, GFile ***files, gint *n_files);
G_GNUC_INTERNAL
gboolean clapper_app_utils_files_from_value (const GValue *value, GFile ***files, gint *n_files);
@@ -59,4 +70,7 @@ void clapper_app_utils_files_free (GFile **files);
G_GNUC_INTERNAL
void clapper_app_utils_iterate_plugin_feature_ranks (GSettings *settings, ClapperAppUtilsIterRanks callback, gpointer user_data);
G_GNUC_INTERNAL
GstElement * clapper_app_utils_make_element (const gchar *string);
G_END_DECLS

View File

@@ -28,6 +28,9 @@
#include "clapper-app-file-dialog.h"
#include "clapper-app-utils.h"
#define MIN_WINDOW_WIDTH 352
#define MIN_WINDOW_HEIGHT 198
#define DEFAULT_WINDOW_WIDTH 1024
#define DEFAULT_WINDOW_HEIGHT 576
@@ -40,6 +43,8 @@
#define PERCENTAGE_ROUND(a) (round ((gdouble) a / 0.01) * 0.01)
#define AXIS_WINS_OVER(a,b) ((a > 0 && a - 0.3 > b) || (a < 0 && a + 0.3 < b))
#define MIN_STEP_DELAY 12000
#define GST_CAT_DEFAULT clapper_app_window_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
@@ -59,6 +64,7 @@ struct _ClapperAppWindow
GSettings *settings;
guint seek_timeout;
guint resize_tick_id;
gboolean key_held;
gboolean scrolling;
@@ -74,6 +80,36 @@ struct _ClapperAppWindow
#define parent_class clapper_app_window_parent_class
G_DEFINE_TYPE (ClapperAppWindow, clapper_app_window, GTK_TYPE_APPLICATION_WINDOW)
typedef struct
{
gint dest_width, dest_height;
gint64 last_tick;
} ClapperAppWindowResizeData;
#if CLAPPER_HAVE_MPRIS
static guint16 instance_count = 0;
#endif
static inline GQuark
clapper_app_window_extra_options_get_quark (void)
{
return g_quark_from_static_string ("clapper-app-window-extra-options-quark");
}
static void
clapper_app_window_extra_options_free (ClapperAppWindowExtraOptions *extra_opts)
{
GST_TRACE ("Freeing window extra options: %p", extra_opts);
g_free (extra_opts->video_filter);
g_free (extra_opts->audio_filter);
g_free (extra_opts->video_sink);
g_free (extra_opts->audio_sink);
g_free (extra_opts);
}
static void
_media_item_title_changed_cb (ClapperMediaItem *item,
GParamSpec *pspec G_GNUC_UNUSED, ClapperAppWindow *self)
@@ -115,6 +151,15 @@ _queue_current_item_changed_cb (ClapperQueue *queue,
gst_clear_object (&current_item);
}
static void
_player_adaptive_bandwidth_changed_cb (ClapperPlayer *player,
GParamSpec *pspec G_GNUC_UNUSED, gpointer *user_data G_GNUC_UNUSED)
{
/* Do not take whole bandwidth */
clapper_player_set_adaptive_start_bitrate (player,
clapper_player_get_adaptive_bandwidth (player) * 0.8);
}
static gboolean
_get_seek_method_mapping (GValue *value,
GVariant *variant, gpointer user_data G_GNUC_UNUSED)
@@ -224,12 +269,16 @@ _open_subtitles_cb (ClapperGtkExtraMenuButton *button G_GNUC_UNUSED,
}
static void
right_click_pressed_cb (GtkGestureClick *click, gint n_press,
click_pressed_cb (GtkGestureClick *click, gint n_press,
gdouble x, gdouble y, ClapperAppWindow *self)
{
GdkCursor *cursor;
const gchar *cursor_name = NULL;
if (gtk_gesture_single_get_current_button (
GTK_GESTURE_SINGLE (click)) != GDK_BUTTON_SECONDARY)
return;
GST_LOG_OBJECT (self, "Right click pressed");
if ((cursor = gtk_widget_get_cursor (self->video)))
@@ -244,9 +293,152 @@ right_click_pressed_cb (GtkGestureClick *click, gint n_press,
}
}
static gboolean
_resize_tick (GtkWidget *widget, GdkFrameClock *frame_clock,
ClapperAppWindowResizeData *resize_data)
{
gint64 now = gdk_frame_clock_get_frame_time (frame_clock);
if (now - resize_data->last_tick >= MIN_STEP_DELAY) {
ClapperAppWindow *self = CLAPPER_APP_WINDOW_CAST (widget);
gint win_width, win_height;
GST_LOG_OBJECT (self, "Resize step, last: %" G_GINT64_FORMAT
", now: %" G_GINT64_FORMAT, resize_data->last_tick, now);
gtk_window_get_default_size (GTK_WINDOW (self), &win_width, &win_height);
if (win_width != resize_data->dest_width) {
gint width_diff = ABS (win_width - resize_data->dest_width);
gint step_size = (width_diff > 180) ? 120 : MAX (width_diff / 4, 1);
win_width += (win_width > resize_data->dest_width) ? -step_size : step_size;
}
if (win_height != resize_data->dest_height) {
gint height_diff = ABS (win_height - resize_data->dest_height);
gint step_size = (height_diff > 180) ? 120 : MAX (height_diff / 4, 1);
win_height += (win_height > resize_data->dest_height) ? -step_size : step_size;
}
gtk_window_set_default_size (GTK_WINDOW (self), win_width, win_height);
if (win_width == resize_data->dest_width
&& win_height == resize_data->dest_height) {
GST_DEBUG_OBJECT (self, "Window resize finish");
self->resize_tick_id = 0;
return G_SOURCE_REMOVE;
}
resize_data->last_tick = now;
}
return G_SOURCE_CONTINUE;
}
static void
right_click_released_cb (GtkGestureClick *click, gint n_press,
gdouble x, gdouble y, ClapperAppWindow *self)
_calculate_win_resize (gint win_w, gint win_h,
gint vid_w, gint vid_h, gint *dest_w, gint *dest_h)
{
gdouble win_aspect = (gdouble) win_w / win_h;
gdouble vid_aspect = (gdouble) vid_w / vid_h;
if (win_aspect < vid_aspect) {
while (!G_APPROX_VALUE (fmod (win_w, vid_aspect), 0, FLT_EPSILON))
win_w++;
win_h = round ((gdouble) win_w / vid_aspect);
if (win_h < MIN_WINDOW_HEIGHT) {
_calculate_win_resize (G_MAXINT, MIN_WINDOW_HEIGHT, vid_w, vid_h, dest_w, dest_h);
return;
}
} else {
while (!G_APPROX_VALUE (fmod (win_h * vid_aspect, 1.0), 0, FLT_EPSILON))
win_h++;
win_w = round ((gdouble) win_h * vid_aspect);
if (win_w < MIN_WINDOW_WIDTH) {
_calculate_win_resize (MIN_WINDOW_WIDTH, G_MAXINT, vid_w, vid_h, dest_w, dest_h);
return;
}
}
*dest_w = win_w;
*dest_h = win_h;
}
static void
_resize_window (ClapperAppWindow *self)
{
ClapperPlayer *player;
ClapperStreamList *vstreams;
ClapperVideoStream *vstream;
GdkToplevelState toplevel_state, disallowed;
if (self->resize_tick_id != 0)
return;
toplevel_state = gdk_toplevel_get_state (GDK_TOPLEVEL (
gtk_native_get_surface (GTK_NATIVE (self))));
disallowed = (GDK_TOPLEVEL_STATE_MINIMIZED
| GDK_TOPLEVEL_STATE_MAXIMIZED
| GDK_TOPLEVEL_STATE_FULLSCREEN
| GDK_TOPLEVEL_STATE_TILED);
if ((toplevel_state & disallowed) > 0) {
GST_DEBUG_OBJECT (self, "Cannot resize window in disallowed state");
return;
}
player = clapper_app_window_get_player (self);
vstreams = clapper_player_get_video_streams (player);
vstream = CLAPPER_VIDEO_STREAM_CAST (
clapper_stream_list_get_current_stream (vstreams));
if (vstream) {
gint video_width = clapper_video_stream_get_width (vstream);
gint video_height = clapper_video_stream_get_height (vstream);
if (G_LIKELY (video_width > 0 && video_height > 0)) {
gint win_width, win_height, dest_width, dest_height;
gtk_window_get_default_size (GTK_WINDOW (self), &win_width, &win_height);
_calculate_win_resize (win_width, win_height,
video_width, video_height, &dest_width, &dest_height);
/* Only begin resize when not already at perfect size */
if (dest_width != win_width || dest_height != win_height) {
ClapperAppWindowResizeData *resize_data;
resize_data = g_new0 (ClapperAppWindowResizeData, 1);
resize_data->dest_width = dest_width;
resize_data->dest_height = dest_height;
GST_DEBUG_OBJECT (self, "Window resize start, dest: %ix%i",
resize_data->dest_width, resize_data->dest_height);
self->resize_tick_id = gtk_widget_add_tick_callback (GTK_WIDGET (self),
(GtkTickCallback) _resize_tick, resize_data, g_free);
}
}
gst_object_unref (vstream);
}
}
static void
_handle_middle_click (ClapperAppWindow *self, GtkGestureClick *click)
{
_resize_window (self);
gtk_gesture_set_state (GTK_GESTURE (click), GTK_EVENT_SEQUENCE_CLAIMED);
}
static void
_handle_right_click (ClapperAppWindow *self, GtkGestureClick *click)
{
GdkSurface *surface;
GdkEventSequence *sequence;
@@ -267,6 +459,22 @@ right_click_released_cb (GtkGestureClick *click, gint n_press,
gtk_gesture_set_state (GTK_GESTURE (click), GTK_EVENT_SEQUENCE_CLAIMED);
}
static void
click_released_cb (GtkGestureClick *click, gint n_press,
gdouble x, gdouble y, ClapperAppWindow *self)
{
switch (gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (click))) {
case GDK_BUTTON_MIDDLE:
_handle_middle_click (self, click);
break;
case GDK_BUTTON_SECONDARY:
_handle_right_click (self, click);
break;
default:
break;
}
}
static void
drag_begin_cb (GtkGestureDrag *drag,
gdouble start_x, gdouble start_y, ClapperAppWindow *self)
@@ -866,6 +1074,23 @@ toggle_fullscreen (GSimpleAction *action, GVariant *param, gpointer user_data)
video_toggle_fullscreen_cb (CLAPPER_GTK_VIDEO_CAST (self->video), self);
}
static void
unfullscreen (GSimpleAction *action, GVariant *param, gpointer user_data)
{
GtkWindow *window = GTK_WINDOW (user_data);
if (gtk_window_is_fullscreen (window)) {
ClapperAppWindow *self = CLAPPER_APP_WINDOW_CAST (window);
video_toggle_fullscreen_cb (CLAPPER_GTK_VIDEO_CAST (self->video), self);
}
}
static void
auto_resize (GSimpleAction *action, GVariant *param, gpointer user_data)
{
_resize_window (CLAPPER_APP_WINDOW_CAST (user_data));
}
static void
show_help_overlay (GSimpleAction *action, GVariant *param, gpointer user_data)
{
@@ -903,6 +1128,13 @@ clapper_app_window_get_player (ClapperAppWindow *self)
return clapper_gtk_video_get_player (CLAPPER_GTK_VIDEO_CAST (self->video));
}
ClapperAppWindowExtraOptions *
clapper_app_window_get_extra_options (ClapperAppWindow *self)
{
return g_object_get_qdata ((GObject *) self,
clapper_app_window_extra_options_get_quark ());
}
void
clapper_app_window_ensure_no_initial_state (ClapperAppWindow *self)
{
@@ -964,10 +1196,21 @@ clapper_app_window_unrealize (GtkWidget *widget)
static void
clapper_app_window_init (ClapperAppWindow *self)
{
ClapperAppWindowExtraOptions *extra_opts;
GtkSettings *settings;
GtkWidget *dummy_titlebar;
gint distance = 0;
gtk_widget_set_size_request (GTK_WIDGET (self),
MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT);
extra_opts = g_new0 (ClapperAppWindowExtraOptions, 1);
GST_TRACE ("Created window extra options: %p", extra_opts);
g_object_set_qdata_full ((GObject *) self,
clapper_app_window_extra_options_get_quark (),
extra_opts, (GDestroyNotify) clapper_app_window_extra_options_free);
gtk_widget_init_template (GTK_WIDGET (self));
/* Make double tap easier to perform */
@@ -1001,20 +1244,26 @@ clapper_app_window_constructed (GObject *object)
static const GActionEntry win_entries[] = {
{ "toggle-fullscreen", toggle_fullscreen, NULL, NULL, NULL },
{ "unfullscreen", unfullscreen, NULL, NULL, NULL },
{ "auto-resize", auto_resize, NULL, NULL, NULL },
{ "show-help-overlay", show_help_overlay, NULL, NULL, NULL },
};
#if (CLAPPER_HAVE_MPRIS || CLAPPER_HAVE_SERVER || CLAPPER_HAVE_DISCOVERER)
ClapperFeature *feature = NULL;
#endif
#if CLAPPER_HAVE_MPRIS
gchar mpris_name[45];
g_snprintf (mpris_name, sizeof (mpris_name),
"org.mpris.MediaPlayer2.Clapper.instance%" G_GUINT16_FORMAT, instance_count++);
#endif
self->settings = g_settings_new (CLAPPER_APP_ID);
self->last_volume = PERCENTAGE_ROUND (g_settings_get_double (self->settings, "volume"));
#if CLAPPER_HAVE_MPRIS
feature = CLAPPER_FEATURE (clapper_mpris_new (
"org.mpris.MediaPlayer2.Clapper",
"Clapper", CLAPPER_APP_ID));
mpris_name, CLAPPER_APP_NAME, CLAPPER_APP_ID));
clapper_mpris_set_queue_controllable (CLAPPER_MPRIS (feature), TRUE);
clapper_player_add_feature (player, feature);
gst_object_unref (feature);
@@ -1040,10 +1289,12 @@ clapper_app_window_constructed (GObject *object)
clapper_player_set_autoplay (player, TRUE);
/* No need to also call this here, as item is selected
/* No need to also call these here, as they only change
* after application window is contructed */
g_signal_connect (queue, "notify::current-item",
G_CALLBACK (_queue_current_item_changed_cb), self);
g_signal_connect (player, "notify::adaptive-bandwidth",
G_CALLBACK (_player_adaptive_bandwidth_changed_cb), NULL);
g_settings_bind (self->settings, "audio-offset",
player, "audio-offset", G_SETTINGS_BIND_GET);
@@ -1081,6 +1332,11 @@ clapper_app_window_dispose (GObject *object)
{
ClapperAppWindow *self = CLAPPER_APP_WINDOW_CAST (object);
if (self->resize_tick_id != 0) {
gtk_widget_remove_tick_callback (GTK_WIDGET (self), self->resize_tick_id);
self->resize_tick_id = 0;
}
g_clear_handle_id (&self->seek_timeout, g_source_remove);
gtk_widget_dispose_template (GTK_WIDGET (object), CLAPPER_APP_TYPE_WINDOW);
@@ -1140,8 +1396,8 @@ clapper_app_window_class_init (ClapperAppWindowClass *klass)
gtk_widget_class_bind_template_callback (widget_class, key_pressed_cb);
gtk_widget_class_bind_template_callback (widget_class, key_released_cb);
gtk_widget_class_bind_template_callback (widget_class, right_click_pressed_cb);
gtk_widget_class_bind_template_callback (widget_class, right_click_released_cb);
gtk_widget_class_bind_template_callback (widget_class, click_pressed_cb);
gtk_widget_class_bind_template_callback (widget_class, click_released_cb);
gtk_widget_class_bind_template_callback (widget_class, drag_begin_cb);
gtk_widget_class_bind_template_callback (widget_class, drag_update_cb);

View File

@@ -27,6 +27,15 @@ G_BEGIN_DECLS
#define CLAPPER_APP_TYPE_WINDOW (clapper_app_window_get_type())
#define CLAPPER_APP_WINDOW_CAST(obj) ((ClapperAppWindow *)(obj))
typedef struct
{
gchar *video_filter;
gchar *audio_filter;
gchar *video_sink;
gchar *audio_sink;
} ClapperAppWindowExtraOptions;
G_DECLARE_FINAL_TYPE (ClapperAppWindow, clapper_app_window, CLAPPER_APP, WINDOW, GtkApplicationWindow)
G_GNUC_INTERNAL
@@ -38,6 +47,9 @@ GtkWidget * clapper_app_window_get_video (ClapperAppWindow *window);
G_GNUC_INTERNAL
ClapperPlayer * clapper_app_window_get_player (ClapperAppWindow *window);
G_GNUC_INTERNAL
ClapperAppWindowExtraOptions * clapper_app_window_get_extra_options (ClapperAppWindow *window);
G_GNUC_INTERNAL
void clapper_app_window_ensure_no_initial_state (ClapperAppWindow *window);

View File

@@ -7,6 +7,7 @@
<file preprocess="xml-stripblanks">ui/clapper-app-headerbar.ui</file>
<file preprocess="xml-stripblanks">ui/clapper-app-help-overlay.ui</file>
<file preprocess="xml-stripblanks">ui/clapper-app-info-window.ui</file>
<file preprocess="xml-stripblanks">ui/clapper-app-pipeline-window.ui</file>
<file preprocess="xml-stripblanks">ui/clapper-app-queue-list.ui</file>
<file preprocess="xml-stripblanks">ui/clapper-app-queue-list-item.ui</file>
<file preprocess="xml-stripblanks">ui/clapper-app-queue-progression-item.ui</file>

View File

@@ -1,8 +1,3 @@
window.app {
min-width: 352px;
min-height: 198px;
}
window .initialstate {
padding-left: 6px;
padding-right: 6px;
@@ -23,6 +18,10 @@ window.info .subcontent streamlist preferencesgroup {
margin-bottom: 8px;
}
window.pipeline scrolledwindow {
background-color: white;
}
window.preferences .subcontent {
margin: 16px;
}

View File

@@ -14,3 +14,8 @@ Type=Application
Keywords=Video;Movie;Film;Clip;Series;Player;Playlist;DVD;TV;Disc;Album;Music;GNOME;Clapper;
# Translators: Do NOT translate or transliterate this text (these are enum types)!
X-Purism-FormFactor=Workstation;Mobile;
Actions=new-window;
[Desktop Action new-window]
Name=New Window
Exec=clapper --new-window

View File

@@ -49,6 +49,10 @@
<default>1.0</default>
<summary>Stores last speed value to apply on startup</summary>
</key>
<key name="adaptive-start-bitrate" type="i">
<default>1600000</default>
<summary>Stores initial adaptive streaming bitrate to apply on startup</summary>
</key>
<key name="progression-mode" type="i">
<default>1</default>
<summary>Stores last queue progression mode used to apply on startup</summary>

View File

@@ -1,7 +1,3 @@
if not ['linux'].contains(host_machine.system())
subdir_done()
endif
appstream_util = find_program('appstream-util', required: false)
if appstream_util.found()
test('Validate appstream file',
@@ -29,22 +25,31 @@ endif
install_subdir('icons',
install_dir: join_paths(prefix, datadir)
)
install_subdir('mime',
install_dir: join_paths(prefix, datadir)
)
install_subdir('applications',
install_dir: join_paths(prefix, datadir)
)
install_subdir('metainfo',
install_dir: join_paths(prefix, datadir)
)
is_linux = ['linux'].contains(host_machine.system())
is_windows = ['windows'].contains(host_machine.system())
if is_linux
install_subdir('applications',
install_dir: join_paths(prefix, datadir)
)
subdir('dbus-1')
endif
if not is_windows
install_subdir('mime',
install_dir: join_paths(prefix, datadir)
)
endif
subdir('glib-2.0/schemas')
subdir('dbus-1')
gnome.post_install(
glib_compile_schemas: true,
gtk_update_icon_cache: true,
update_desktop_database: true,
update_mime_database: true,
update_desktop_database: is_linux,
update_mime_database: not is_windows,
)

View File

@@ -59,11 +59,6 @@
<category>Video</category>
</categories>
<releases>
<release version="0.6.1" date="2024-07-01">
<description>
<p>Bug fixes and translation updates</p>
</description>
</release>
<release version="0.6.0" date="2024-04-22">
<description>
<p>

View File

@@ -25,6 +25,7 @@
#include <clapper/clapper.h>
#include "clapper-app-application.h"
#include "clapper-app-types.h"
gint
main (gint argc, gchar **argv)
@@ -46,6 +47,8 @@ main (gint argc, gchar **argv)
gtk_init ();
adw_init ();
clapper_app_types_init ();
g_set_application_name ("Clapper");
application = clapper_app_application_new ();

View File

@@ -15,6 +15,10 @@ clapperapp_deps = [
libadwaita_dep,
glib_dep,
gobject_dep,
libm,
dependency('libcgraph', required: false),
dependency('libgvc', required: false),
dependency('librsvg-2.0', version: '>= 2.52', required: false),
]
foreach dep : clapperapp_deps
@@ -61,12 +65,15 @@ clapperapp_sources = [
'clapper-app-info-window.c',
'clapper-app-list-item-utils.c',
'clapper-app-media-item-box.c',
'clapper-app-pipeline-viewer.c',
'clapper-app-pipeline-window.c',
'clapper-app-preferences-window.c',
'clapper-app-property-row.c',
'clapper-app-queue-list.c',
'clapper-app-queue-progression-item.c',
'clapper-app-queue-progression-model.c',
'clapper-app-queue-selection.c',
'clapper-app-types.c',
'clapper-app-uri-dialog.c',
'clapper-app-utils.c',
'clapper-app-window.c',
@@ -87,5 +94,18 @@ executable(
c_args: clapperapp_c_args,
install: true,
install_dir: bindir,
win_subsystem: 'windows',
)
if ['windows'].contains(host_machine.system())
executable(
meson.project_name() + '-console',
clapperapp_sources,
dependencies: clapperapp_deps,
include_directories: clapperapp_conf_inc,
c_args: clapperapp_c_args,
install: true,
install_dir: bindir,
win_subsystem: 'console',
)
endif
build_clapperapp = true

View File

@@ -10,6 +10,7 @@ src/bin/clapper-app/ui/clapper-app-uri-dialog.ui
src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui
src/bin/clapper-app/clapper-app-about-window.c
src/bin/clapper-app/clapper-app-application.c
src/bin/clapper-app/clapper-app-info-window.c
src/bin/clapper-app/clapper-app-list-item-utils.c
src/bin/clapper-app/clapper-app-preferences-window.c

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: clapper\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-21 20:22+0200\n"
"PO-Revision-Date: 2024-04-21 20:51\n"
"PO-Revision-Date: 2024-08-12 08:36\n"
"Last-Translator: \n"
"Language-Team: Czech\n"
"Language: cs_CZ\n"
@@ -20,7 +20,7 @@ msgstr ""
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:14
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:14
msgid "Codec"
msgstr ""
msgstr "Kodek"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:24
msgid "Channels"
@@ -28,21 +28,21 @@ msgstr "Kanály"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:36
msgid "Sample Format"
msgstr ""
msgstr "Vzorkovací Formát"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:46
msgid "Sample Rate"
msgstr ""
msgstr "Vzorkovací Frekvence"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:58
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:51
msgid "Bitrate"
msgstr ""
msgstr "Datový Tok"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:70
#: src/bin/clapper-app/ui/clapper-app-subtitle-stream-list-item.ui:24
msgid "Language"
msgstr ""
msgstr "Jazyk"
#: src/bin/clapper-app/ui/clapper-app-headerbar.ui:183
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:80
@@ -52,7 +52,7 @@ msgstr "Předvolby"
#: src/bin/clapper-app/ui/clapper-app-headerbar.ui:187
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:84
msgid "Keyboard Shortcuts"
msgstr ""
msgstr "Klávesové Zkratky"
#. TRANSLATORS: Please do not translate application name
#: src/bin/clapper-app/ui/clapper-app-headerbar.ui:194
@@ -71,7 +71,7 @@ msgstr "Zobrazit klávesové zkratky"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:19
msgid "Open preferences"
msgstr ""
msgstr "Otevřít předvolby"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:25
msgid "Toggle fullscreen"
@@ -94,17 +94,17 @@ msgstr "Média"
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:55
#: src/bin/clapper-app/ui/clapper-app-queue-list.ui:178
msgid "Add Files…"
msgstr ""
msgstr "Přidat Soubory…"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:49
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:65
#: src/bin/clapper-app/ui/clapper-app-queue-list.ui:182
msgid "Add URI…"
msgstr ""
msgstr "Přidat URI…"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:57
msgid "Queue"
msgstr ""
msgstr "Fronta"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:61
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:68
@@ -118,7 +118,7 @@ msgstr "Předchozí položka"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:88
msgid "Change progression mode"
msgstr ""
msgstr "Změnit způsob postupování"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:96
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:76
@@ -131,7 +131,7 @@ msgstr "Přepnout přehrávání"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:100
msgid "Tap | Left click"
msgstr ""
msgstr "Klepnutím | Levým tlačítkem"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:107
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:115
@@ -141,12 +141,12 @@ msgstr "Přetočit vpřed"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:108
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:132
msgid "Double tap (right side) | Scroll right"
msgstr ""
msgstr "Dvojitě klepnout (pravá strana) | Posun vpravo"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:116
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:124
msgid "Double tap (left side) | Scroll left"
msgstr ""
msgstr "Dvojitě klepnout (levá strana) | Posun vlevo"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:123
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:131
@@ -159,7 +159,7 @@ msgstr "Zesílit"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:139
msgid "Scroll up"
msgstr ""
msgstr "Posun nahoru"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:145
msgid "Volume down"
@@ -167,7 +167,7 @@ msgstr "Zeslabit"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:146
msgid "Scroll down"
msgstr ""
msgstr "Posun dolů"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:152
msgid "Toggle mute"
@@ -175,11 +175,11 @@ msgstr "Vypnout/zapnout zvuk"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:158
msgid "Speed up"
msgstr ""
msgstr "Zrychlit"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:164
msgid "Speed down"
msgstr ""
msgstr "Zpomalit"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:171
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:178
@@ -193,7 +193,7 @@ msgstr "Předchozí kapitola"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:13
msgid "Info"
msgstr ""
msgstr "Informace"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:35
#: src/bin/clapper-app/ui/clapper-app-subtitle-stream-list-item.ui:14
@@ -202,20 +202,20 @@ msgstr "Název"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:49
msgid "Container Format"
msgstr ""
msgstr "Formát Kontejneru"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:63
msgid "Duration"
msgstr ""
msgstr "Doba Trvání"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:81
msgid "Streams"
msgstr ""
msgstr "Kanály"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:84
#: src/bin/clapper-app/clapper-app-list-item-utils.c:36
msgid "Video"
msgstr ""
msgstr "Video"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:107
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:80
@@ -231,42 +231,42 @@ msgstr "Titulky"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:155
msgid "Video Playback"
msgstr ""
msgstr "Přehrávání Videa"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:158
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:199
msgid "Decoder"
msgstr ""
msgstr "Dekodér"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:170
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:211
msgid "Filter"
msgstr ""
msgstr "Filtr"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:182
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:223
msgid "Sink"
msgstr ""
msgstr "Sink"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:196
msgid "Audio Playback"
msgstr ""
msgstr "Přehrávání Audia"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:247
msgid "Video Streams"
msgstr ""
msgstr "Video Kanály"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:290
msgid "Audio Streams"
msgstr ""
msgstr "Audio Kanály"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:333
msgid "Subtitle Streams"
msgstr ""
msgstr "Titulkové Kanály"
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:46
msgid "Start by adding media to playback queue"
msgstr ""
msgstr "Začněte přidáním média do přehrávací fronty"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:15
msgid "Seeking"
@@ -274,11 +274,11 @@ msgstr "Přetáčení"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:18
msgid "Method"
msgstr ""
msgstr "Způsob"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:19
msgid "A preferred method used for seeking"
msgstr ""
msgstr "Upřednostňovaný způsob použitý pro přetáčení"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:32
msgid "Value"
@@ -286,7 +286,7 @@ msgstr "Hodnota"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:33
msgid "Value used for seeking forward/backward"
msgstr ""
msgstr "Hodnota použitá pro přetáčení dopředu/dozadu"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:46
msgid "Unit"
@@ -294,7 +294,7 @@ msgstr "Jednotka"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:47
msgid "An unit of a seek forward/backward value"
msgstr ""
msgstr "Jednotka hodnoty pro přetáčení dopředu/dozadu"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:51
msgid "Second"
@@ -310,7 +310,7 @@ msgstr "Procenta"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:63
msgid "Features"
msgstr ""
msgstr "Funkce"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:66
msgid "Server"
@@ -323,15 +323,15 @@ msgstr "Vzdáleně ovládat přehrávač"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:83
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:103
msgid "Offset"
msgstr ""
msgstr "Posun"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:84
msgid "Synchronisation offset in seconds between the audio and video streams"
msgstr ""
msgstr "Synchronizační posun v sekundách mezi audio a video kanály"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:104
msgid "Synchronisation offset in seconds between the subtitle and video streams"
msgstr ""
msgstr "Synchronizační posun v sekundách mezi titulkovými a video kanály"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:118
msgid "Default font"
@@ -339,7 +339,7 @@ msgstr "Výchozí styl písma"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:119
msgid "Text font used for subtitles when media does not explicitly specify one"
msgstr ""
msgstr "Písmo používané pro titulky, když jej médium výslovně neupřesní"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:137
msgid "Tweaks"
@@ -355,48 +355,48 @@ msgstr "Změna výchozího pořadí zásuvných modulů GStreameru"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:164
msgid "Plugin Ranking"
msgstr ""
msgstr "Pořadí Pluginů"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:182
msgid "Available plugins"
msgstr ""
msgstr "Dostupné pluginy"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:183
msgid "Select a plugin and its feature to override rank for. When multiple elements have similiar capabilities, the one with highest value is preferred."
msgstr ""
msgstr "Vyberte plugin a jeho funkci, pro kterou chcete přepsat pořadí. Pokud má více prvků podobnou schopnost, je preferována ta s nejvyšší hodnotou."
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:186
msgid "Plugin"
msgstr ""
msgstr "Plugin"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:195
msgid "Plugin feature"
msgstr ""
msgstr "Funkce pluginy"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:213
msgid "Add override"
msgstr ""
msgstr "Přidat přepsání"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:229
msgid "Rank overrides"
msgstr ""
msgstr "Pořadí přepsání"
#: src/bin/clapper-app/ui/clapper-app-queue-list.ui:60
#: src/bin/clapper-app/ui/clapper-app-uri-dialog.ui:23
msgid "Add"
msgstr ""
msgstr "Přidat"
#: src/bin/clapper-app/ui/clapper-app-queue-list.ui:87
msgid "Queue progression"
msgstr ""
msgstr "Postup fronty"
#: src/bin/clapper-app/ui/clapper-app-uri-dialog.ui:6
msgid "Add URI"
msgstr ""
msgstr "Přidat URI"
#: src/bin/clapper-app/ui/clapper-app-uri-dialog.ui:7
msgid "Insert an URI to be added to playback queue"
msgstr ""
msgstr "Vložte URI, která bude přidána do fronty přehrávání"
#: src/bin/clapper-app/ui/clapper-app-uri-dialog.ui:18
msgid "Enter or drop URI here"
@@ -408,15 +408,15 @@ msgstr "Zrušit"
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:24
msgid "Resolution"
msgstr ""
msgstr "Rozlišení"
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:39
msgid "Framerate"
msgstr ""
msgstr "Snímky za sekundu"
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:63
msgid "Pixel Format"
msgstr ""
msgstr "Formát Pixelu"
#. TRANSLATORS: Put your name(s) here for credits or leave untranslated
#: src/bin/clapper-app/clapper-app-about-window.c:43
@@ -425,11 +425,11 @@ msgstr "Vojtěch Perník <translations@pervoj.cz>"
#: src/bin/clapper-app/clapper-app-info-window.c:86
msgid "Hardware"
msgstr ""
msgstr "Hardware"
#: src/bin/clapper-app/clapper-app-info-window.c:86
msgid "Software"
msgstr ""
msgstr "Software"
#: src/bin/clapper-app/clapper-app-preferences-window.c:441
msgid "Accurate"
@@ -445,25 +445,25 @@ msgstr "Rychlý"
#: src/bin/clapper-app/clapper-app-utils.c:66
msgid "No progression"
msgstr ""
msgstr "Žádný postup"
#: src/bin/clapper-app/clapper-app-utils.c:67
msgid "Consecutive"
msgstr ""
msgstr "Po sobě"
#: src/bin/clapper-app/clapper-app-utils.c:68
msgid "Repeat item"
msgstr ""
msgstr "Opakovat položku"
#: src/bin/clapper-app/clapper-app-utils.c:69
msgid "Carousel"
msgstr ""
msgstr "Opakovat frontu"
#: src/bin/clapper-app/clapper-app-utils.c:70
msgid "Shuffle"
msgstr ""
msgstr "Náhodně"
#: src/bin/clapper-app/clapper-app-window.c:828
msgid "Drop on title bar to play now or anywhere else to enqueue."
msgstr ""
msgstr "Přetáhněte na titulní lištu pro okamžité přehrání nebo kdekoliv jinde pro zařazení."

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: clapper\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-21 20:22+0200\n"
"PO-Revision-Date: 2024-04-21 20:51\n"
"PO-Revision-Date: 2024-09-07 11:10\n"
"Last-Translator: \n"
"Language-Team: Persian\n"
"Language: fa_IR\n"
@@ -20,7 +20,7 @@ msgstr ""
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:14
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:14
msgid "Codec"
msgstr ""
msgstr "رمزینه"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:24
msgid "Channels"
@@ -28,21 +28,21 @@ msgstr "کانال‌ها"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:36
msgid "Sample Format"
msgstr ""
msgstr "قالب نمونه"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:46
msgid "Sample Rate"
msgstr ""
msgstr "نرخ نمونه‌"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:58
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:51
msgid "Bitrate"
msgstr ""
msgstr "نرخ بیت"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:70
#: src/bin/clapper-app/ui/clapper-app-subtitle-stream-list-item.ui:24
msgid "Language"
msgstr ""
msgstr "زبان"
#: src/bin/clapper-app/ui/clapper-app-headerbar.ui:183
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:80
@@ -52,13 +52,13 @@ msgstr "ترجیحات"
#: src/bin/clapper-app/ui/clapper-app-headerbar.ui:187
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:84
msgid "Keyboard Shortcuts"
msgstr ""
msgstr "میان‌برهای صفحه‌کلید"
#. TRANSLATORS: Please do not translate application name
#: src/bin/clapper-app/ui/clapper-app-headerbar.ui:194
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:91
msgid "About Clapper"
msgstr "دربارهٔ کلَپِر"
msgstr "دربارهٔ کلپِر"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:10
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:11
@@ -71,7 +71,7 @@ msgstr "نمایش میان‌برها"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:19
msgid "Open preferences"
msgstr ""
msgstr "گشودن ترجیحات"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:25
msgid "Toggle fullscreen"
@@ -94,17 +94,17 @@ msgstr "رسانه"
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:55
#: src/bin/clapper-app/ui/clapper-app-queue-list.ui:178
msgid "Add Files…"
msgstr ""
msgstr "افزودن پرونده‌ها…"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:49
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:65
#: src/bin/clapper-app/ui/clapper-app-queue-list.ui:182
msgid "Add URI…"
msgstr ""
msgstr "افزودن نشانی…"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:57
msgid "Queue"
msgstr ""
msgstr "صف"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:61
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:68
@@ -118,7 +118,7 @@ msgstr "مورد قبل"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:88
msgid "Change progression mode"
msgstr ""
msgstr "تغییر خالت پیشرفت"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:96
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:76
@@ -131,7 +131,7 @@ msgstr "تغییر پخش"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:100
msgid "Tap | Left click"
msgstr ""
msgstr "ضربه | کلیک راست"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:107
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:115
@@ -141,12 +141,12 @@ msgstr "پیمایش به جلو"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:108
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:132
msgid "Double tap (right side) | Scroll right"
msgstr ""
msgstr "دوبار ضربه (سمت راست) | لغزش راست"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:116
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:124
msgid "Double tap (left side) | Scroll left"
msgstr ""
msgstr "دوبار ضربه (سمت چپ) | لغزش چپ"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:123
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:131
@@ -159,7 +159,7 @@ msgstr "افزایش صدا"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:139
msgid "Scroll up"
msgstr ""
msgstr "لغزش به بالا"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:145
msgid "Volume down"
@@ -167,7 +167,7 @@ msgstr "کاهش صدا"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:146
msgid "Scroll down"
msgstr ""
msgstr "لغزش به پایین"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:152
msgid "Toggle mute"
@@ -175,11 +175,11 @@ msgstr "تغییر وضعیت بیصدا"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:158
msgid "Speed up"
msgstr ""
msgstr "سرعت بخشیدن"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:164
msgid "Speed down"
msgstr ""
msgstr "کاهش سرعت"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:171
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:178
@@ -193,7 +193,7 @@ msgstr "بخش قبلی"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:13
msgid "Info"
msgstr ""
msgstr "اطّلاعات"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:35
#: src/bin/clapper-app/ui/clapper-app-subtitle-stream-list-item.ui:14
@@ -202,26 +202,26 @@ msgstr "عنوان"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:49
msgid "Container Format"
msgstr ""
msgstr "قالب بارگنج"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:63
msgid "Duration"
msgstr ""
msgstr "مدّت"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:81
msgid "Streams"
msgstr ""
msgstr "جریان‌ها"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:84
#: src/bin/clapper-app/clapper-app-list-item-utils.c:36
msgid "Video"
msgstr ""
msgstr "ویدیو"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:107
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:80
#: src/bin/clapper-app/clapper-app-list-item-utils.c:39
msgid "Audio"
msgstr "صوتی"
msgstr "صدا"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:130
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:100
@@ -231,42 +231,42 @@ msgstr "زیرنویس‌ها"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:155
msgid "Video Playback"
msgstr ""
msgstr "پخش ویدیو"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:158
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:199
msgid "Decoder"
msgstr ""
msgstr "رمزگشا"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:170
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:211
msgid "Filter"
msgstr ""
msgstr "پالایه"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:182
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:223
msgid "Sink"
msgstr ""
msgstr "سینک"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:196
msgid "Audio Playback"
msgstr ""
msgstr "پخش صدا"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:247
msgid "Video Streams"
msgstr ""
msgstr "جریان‌های ویدیویی"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:290
msgid "Audio Streams"
msgstr ""
msgstr "جریان‌های صوتی"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:333
msgid "Subtitle Streams"
msgstr ""
msgstr "جریان‌های زیرنویس"
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:46
msgid "Start by adding media to playback queue"
msgstr ""
msgstr "آغزا با افزودن رسانه به صف پخش"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:15
msgid "Seeking"
@@ -274,11 +274,11 @@ msgstr "پوییدن"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:18
msgid "Method"
msgstr ""
msgstr "روش"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:19
msgid "A preferred method used for seeking"
msgstr ""
msgstr "روشی ترجیحی برای جویش"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:32
msgid "Value"
@@ -286,7 +286,7 @@ msgstr "مقدار"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:33
msgid "Value used for seeking forward/backward"
msgstr ""
msgstr "یکای استفاده شده برای جویش به پیش و پ"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:46
msgid "Unit"
@@ -294,7 +294,7 @@ msgstr "واحد"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:47
msgid "An unit of a seek forward/backward value"
msgstr ""
msgstr "یکای مقدار جویش به پیش و پس"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:51
msgid "Second"
@@ -310,97 +310,97 @@ msgstr "درصد"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:63
msgid "Features"
msgstr ""
msgstr "ویژگی‌ها"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:66
msgid "Server"
msgstr "سرور"
msgstr "کارساز"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:67
msgid "Control player remotely"
msgstr "کنترل پخش‌کننده از راه دور"
msgstr "واپایش پخش‌کننده از راه دور"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:83
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:103
msgid "Offset"
msgstr ""
msgstr "انحراف از مبدأ"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:84
msgid "Synchronisation offset in seconds between the audio and video streams"
msgstr ""
msgstr "همگام‌سازی انحراف از مبدأ به ثانیه بین جریان‌های ویدیو و صدا"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:104
msgid "Synchronisation offset in seconds between the subtitle and video streams"
msgstr ""
msgstr "همگام‌سازی انحراف از مبدأ به ثانیه بین جریان‌های ویدیو و زیرنویس"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:118
msgid "Default font"
msgstr "قلم پیش فرض"
msgstr "قلم پیش‌گزیده"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:119
msgid "Text font used for subtitles when media does not explicitly specify one"
msgstr ""
msgstr "قلم متن استفاده شده برای زیرنویس هنگامی که رسانه قلمی خاص را مشخّص نکرده"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:137
msgid "Tweaks"
msgstr "دستكاري"
msgstr "سیخونک‌ها"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:144
msgid "Plugin ranking"
msgstr "پیکربندی پلاگین"
msgstr "رتبه دهی افزایه"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:145
msgid "Alter default ranks of GStreamer plugins"
msgstr "تغییر پیکربندی پیش فرض پلاگین های GStreamer"
msgstr "تغییر رتبه دهی پیش‌گزیده برای افزایه‌های جی‌استریمر"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:164
msgid "Plugin Ranking"
msgstr ""
msgstr "رتبه دهی افزایه"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:182
msgid "Available plugins"
msgstr ""
msgstr "افزایه‌های موجود"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:183
msgid "Select a plugin and its feature to override rank for. When multiple elements have similiar capabilities, the one with highest value is preferred."
msgstr ""
msgstr "گزینش افزایه و ویژگیش برای پایمالی رتبه دهیش. هنگامی که چندین عنصر قابلیت‌ّای مشابهی دارند، آنی که بیش‌ترین رتبه را دارد ترجیح داده می‌شود."
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:186
msgid "Plugin"
msgstr ""
msgstr "افزایه"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:195
msgid "Plugin feature"
msgstr ""
msgstr "ویژگی افزایه"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:213
msgid "Add override"
msgstr ""
msgstr "افزودن پایمالی"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:229
msgid "Rank overrides"
msgstr ""
msgstr "پایمالی‌های رتبه دهی"
#: src/bin/clapper-app/ui/clapper-app-queue-list.ui:60
#: src/bin/clapper-app/ui/clapper-app-uri-dialog.ui:23
msgid "Add"
msgstr ""
msgstr "افزودن"
#: src/bin/clapper-app/ui/clapper-app-queue-list.ui:87
msgid "Queue progression"
msgstr ""
msgstr "پیشرفت صف"
#: src/bin/clapper-app/ui/clapper-app-uri-dialog.ui:6
msgid "Add URI"
msgstr ""
msgstr "افزودن نشانی"
#: src/bin/clapper-app/ui/clapper-app-uri-dialog.ui:7
msgid "Insert an URI to be added to playback queue"
msgstr ""
msgstr "درج نشانی برای افزودن به صف پخش"
#: src/bin/clapper-app/ui/clapper-app-uri-dialog.ui:18
msgid "Enter or drop URI here"
msgstr "رها کردن یا واردکردن URI"
msgstr "ورود یا انداختن ناشنی"
#: src/bin/clapper-app/ui/clapper-app-uri-dialog.ui:22
msgid "Cancel"
@@ -408,32 +408,33 @@ msgstr "لغو"
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:24
msgid "Resolution"
msgstr ""
msgstr "تفکیک‌پذیری"
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:39
msgid "Framerate"
msgstr ""
msgstr "نرخ قاب"
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:63
msgid "Pixel Format"
msgstr ""
msgstr "قالب پیکسلی"
#. TRANSLATORS: Put your name(s) here for credits or leave untranslated
#: src/bin/clapper-app/clapper-app-about-window.c:43
msgid "translator-credits"
msgstr "سجاد موسوی نژاد <ssmns@outlook.com>"
msgstr "سجاد موسوی نژاد <ssmns@outlook.com>\n"
"دانیال بهزادی <dani.behzi@ubuntu.com>"
#: src/bin/clapper-app/clapper-app-info-window.c:86
msgid "Hardware"
msgstr ""
msgstr "سخت‌افزاری"
#: src/bin/clapper-app/clapper-app-info-window.c:86
msgid "Software"
msgstr ""
msgstr "نرم‌افزاری"
#: src/bin/clapper-app/clapper-app-preferences-window.c:441
msgid "Accurate"
msgstr "دقت"
msgstr "دقیق"
#: src/bin/clapper-app/clapper-app-preferences-window.c:443
msgid "Normal"
@@ -445,25 +446,25 @@ msgstr "تند"
#: src/bin/clapper-app/clapper-app-utils.c:66
msgid "No progression"
msgstr ""
msgstr "بدون پیشرفت"
#: src/bin/clapper-app/clapper-app-utils.c:67
msgid "Consecutive"
msgstr ""
msgstr "متوالی"
#: src/bin/clapper-app/clapper-app-utils.c:68
msgid "Repeat item"
msgstr ""
msgstr "تکرار مورد"
#: src/bin/clapper-app/clapper-app-utils.c:69
msgid "Carousel"
msgstr ""
msgstr "چرخ فلک"
#: src/bin/clapper-app/clapper-app-utils.c:70
msgid "Shuffle"
msgstr ""
msgstr "بر زدن"
#: src/bin/clapper-app/clapper-app-window.c:828
msgid "Drop on title bar to play now or anywhere else to enqueue."
msgstr ""
msgstr "انداختن روی نوار عنوان برای پخش اکنون یا هر جای دیگری برای صف کردن."

View File

@@ -0,0 +1,469 @@
msgid ""
msgstr ""
"Project-Id-Version: clapper\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-21 20:22+0200\n"
"PO-Revision-Date: 2024-07-09 18:54\n"
"Last-Translator: \n"
"Language-Team: Hindi\n"
"Language: hi_IN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: clapper\n"
"X-Crowdin-Project-ID: 473374\n"
"X-Crowdin-Language: hi\n"
"X-Crowdin-File: /master/src/bin/clapper-app/po/clapper-app.pot\n"
"X-Crowdin-File-ID: 48\n"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:14
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:14
msgid "Codec"
msgstr "कोडेक"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:24
msgid "Channels"
msgstr "चैनल"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:36
msgid "Sample Format"
msgstr "सैंपल प्रारूप"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:46
msgid "Sample Rate"
msgstr "सैंपल दर"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:58
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:51
msgid "Bitrate"
msgstr "बिटरेट"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:70
#: src/bin/clapper-app/ui/clapper-app-subtitle-stream-list-item.ui:24
msgid "Language"
msgstr "भाषा"
#: src/bin/clapper-app/ui/clapper-app-headerbar.ui:183
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:80
msgid "Preferences"
msgstr "प्राथमिकताएं"
#: src/bin/clapper-app/ui/clapper-app-headerbar.ui:187
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:84
msgid "Keyboard Shortcuts"
msgstr "कीबोर्ड शॉर्टकट"
#. TRANSLATORS: Please do not translate application name
#: src/bin/clapper-app/ui/clapper-app-headerbar.ui:194
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:91
msgid "About Clapper"
msgstr "क्लैपर के बारे में"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:10
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:11
msgid "General"
msgstr "सामान्य"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:13
msgid "Show shortcuts"
msgstr "शॉर्टकट दिखाएं"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:19
msgid "Open preferences"
msgstr "प्राथमिकताएं खोलें"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:25
msgid "Toggle fullscreen"
msgstr "पूर्णस्क्रीन टॉगल करें"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:26
msgid "Double tap | Double click"
msgstr "डबल टैप | डबल क्लिक"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:32
msgid "Quit"
msgstr "बंद करें"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:40
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:32
msgid "Media"
msgstr "मीडिया"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:43
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:55
#: src/bin/clapper-app/ui/clapper-app-queue-list.ui:178
msgid "Add Files…"
msgstr "फाइलें जोड़ें…"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:49
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:65
#: src/bin/clapper-app/ui/clapper-app-queue-list.ui:182
msgid "Add URI…"
msgstr "यूआरआई जोड़ें…"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:57
msgid "Queue"
msgstr "कतार"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:61
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:68
msgid "Next item"
msgstr "अगला मद"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:75
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:82
msgid "Previous item"
msgstr "पिछला मद"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:88
msgid "Change progression mode"
msgstr "प्रगति मोड बदलें"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:96
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:76
msgid "Playback"
msgstr "प्लेबैक"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:99
msgid "Toggle play"
msgstr "चजाना टॉगल करें"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:100
msgid "Tap | Left click"
msgstr "टैप | बायां क्लिक"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:107
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:115
msgid "Seek forward"
msgstr "आगे तलाशें"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:108
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:132
msgid "Double tap (right side) | Scroll right"
msgstr "डबल टैप (दायीं ओर) | दाएं स्क्रॉल करें"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:116
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:124
msgid "Double tap (left side) | Scroll left"
msgstr "डबल टैप (बायीं ओर) | बाएं स्क्रॉल करें"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:123
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:131
msgid "Seek backward"
msgstr "पीछे तलाशें"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:138
msgid "Volume up"
msgstr "आवाज बढ़ाएं"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:139
msgid "Scroll up"
msgstr "ऊपर स्क्रॉल"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:145
msgid "Volume down"
msgstr "आवाज घटाएं"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:146
msgid "Scroll down"
msgstr "नीचे स्क्रॉल"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:152
msgid "Toggle mute"
msgstr "मूक टॉगल करें"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:158
msgid "Speed up"
msgstr "गति बढ़ाएं"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:164
msgid "Speed down"
msgstr "गति घटाएं"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:171
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:178
msgid "Next chapter"
msgstr "अगला अध्याय"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:185
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:192
msgid "Previous chapter"
msgstr "पिछला अध्याय"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:13
msgid "Info"
msgstr "जानकारी"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:35
#: src/bin/clapper-app/ui/clapper-app-subtitle-stream-list-item.ui:14
msgid "Title"
msgstr "शीर्षक"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:49
msgid "Container Format"
msgstr "कंटेनर प्रारूप"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:63
msgid "Duration"
msgstr "अवधि"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:81
msgid "Streams"
msgstr "स्ट्रीम"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:84
#: src/bin/clapper-app/clapper-app-list-item-utils.c:36
msgid "Video"
msgstr "वीडियो"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:107
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:80
#: src/bin/clapper-app/clapper-app-list-item-utils.c:39
msgid "Audio"
msgstr "ऑडियो"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:130
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:100
#: src/bin/clapper-app/clapper-app-list-item-utils.c:42
msgid "Subtitles"
msgstr "उपशीर्षक"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:155
msgid "Video Playback"
msgstr "वीडियो प्लेबैक"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:158
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:199
msgid "Decoder"
msgstr "डिकोडर"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:170
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:211
msgid "Filter"
msgstr "फिल्टर"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:182
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:223
msgid "Sink"
msgstr "सिंक"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:196
msgid "Audio Playback"
msgstr "ऑडियो प्लेबैक"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:247
msgid "Video Streams"
msgstr "वीडियो स्ट्रीम"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:290
msgid "Audio Streams"
msgstr "ऑडियो स्ट्रीम"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:333
msgid "Subtitle Streams"
msgstr "उपशीर्षक स्ट्रीम"
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:46
msgid "Start by adding media to playback queue"
msgstr "प्लेबैक कतार में मीडिया जोड़कर प्रारंभ करें"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:15
msgid "Seeking"
msgstr "तलाश"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:18
msgid "Method"
msgstr "विधि"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:19
msgid "A preferred method used for seeking"
msgstr "तलाशने की वरीय विधि"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:32
msgid "Value"
msgstr "मान"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:33
msgid "Value used for seeking forward/backward"
msgstr "आगे/पीछे तलाशने हेतु मान"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:46
msgid "Unit"
msgstr "इकाई"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:47
msgid "An unit of a seek forward/backward value"
msgstr "आगे/पीछे तलाशने हेतु मान की इकाई"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:51
msgid "Second"
msgstr "सेकंड"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:52
msgid "Minute"
msgstr "मिनट"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:53
msgid "Percentage"
msgstr "प्रतिशत"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:63
msgid "Features"
msgstr "विशेषताएं"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:66
msgid "Server"
msgstr "सर्वर"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:67
msgid "Control player remotely"
msgstr "प्लेयर को रिमोट से नियंत्रित करें"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:83
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:103
msgid "Offset"
msgstr "ऑफसेट"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:84
msgid "Synchronisation offset in seconds between the audio and video streams"
msgstr "ऑडियो और वीडियो स्ट्रीम के बीच सेकंड में समन्वयन ऑफसेट"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:104
msgid "Synchronisation offset in seconds between the subtitle and video streams"
msgstr "उपशीर्षक और वीडियो स्ट्रीम के बीच सेकंड में समन्वयन ऑफसेट"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:118
msgid "Default font"
msgstr "तयशुदा फॉन्ट"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:119
msgid "Text font used for subtitles when media does not explicitly specify one"
msgstr "उपशीर्षकों हेतु प्रयुक्त टेक्स्ट फॉन्ट जब मीडिया स्पष्ट रूप से निर्दिष्ट नहीं करता है"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:137
msgid "Tweaks"
msgstr "ट्विक्स"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:144
msgid "Plugin ranking"
msgstr "प्लगइन रैंकिंग"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:145
msgid "Alter default ranks of GStreamer plugins"
msgstr "GStreamer प्लगइन्स की तयशुदा रैंक बदलें"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:164
msgid "Plugin Ranking"
msgstr "प्लगइन रैंकिंग"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:182
msgid "Available plugins"
msgstr "उपलब्ध प्लगइन"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:183
msgid "Select a plugin and its feature to override rank for. When multiple elements have similiar capabilities, the one with highest value is preferred."
msgstr "रैंक को ओवरराइड करने के लिए प्लगइन और उसकी खासियत चुनें। जब कई तत्वों में समान क्षमताएं होती हैं, तो सबसे अधिक मान वाले को प्राथमिकता दी जाती है।"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:186
msgid "Plugin"
msgstr "प्लगइन"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:195
msgid "Plugin feature"
msgstr "प्लगइन सुविधा"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:213
msgid "Add override"
msgstr "ओवरराइड जोड़ें"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:229
msgid "Rank overrides"
msgstr "रैंक ओवरराइड"
#: src/bin/clapper-app/ui/clapper-app-queue-list.ui:60
#: src/bin/clapper-app/ui/clapper-app-uri-dialog.ui:23
msgid "Add"
msgstr "जोड़ें"
#: src/bin/clapper-app/ui/clapper-app-queue-list.ui:87
msgid "Queue progression"
msgstr "कतार प्रगति"
#: src/bin/clapper-app/ui/clapper-app-uri-dialog.ui:6
msgid "Add URI"
msgstr "यूआरआई जोड़ें"
#: src/bin/clapper-app/ui/clapper-app-uri-dialog.ui:7
msgid "Insert an URI to be added to playback queue"
msgstr "प्लेबैक कतार में जोड़ने के लिए यूआरआई डालें"
#: src/bin/clapper-app/ui/clapper-app-uri-dialog.ui:18
msgid "Enter or drop URI here"
msgstr "यहां यूआरआई दर्ज करें या छोड़ें"
#: src/bin/clapper-app/ui/clapper-app-uri-dialog.ui:22
msgid "Cancel"
msgstr "रद्द करें"
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:24
msgid "Resolution"
msgstr "रिजोल्यूशन"
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:39
msgid "Framerate"
msgstr "फ्रेमरेट"
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:63
msgid "Pixel Format"
msgstr "पिक्सल प्रारूप"
#. TRANSLATORS: Put your name(s) here for credits or leave untranslated
#: src/bin/clapper-app/clapper-app-about-window.c:43
msgid "translator-credits"
msgstr "Scrambled777 <weblate.scrambled777@simplelogin.com>"
#: src/bin/clapper-app/clapper-app-info-window.c:86
msgid "Hardware"
msgstr "हार्डवेयर"
#: src/bin/clapper-app/clapper-app-info-window.c:86
msgid "Software"
msgstr "सॉफ्टवेयर"
#: src/bin/clapper-app/clapper-app-preferences-window.c:441
msgid "Accurate"
msgstr "सटीक"
#: src/bin/clapper-app/clapper-app-preferences-window.c:443
msgid "Normal"
msgstr "साधारण"
#: src/bin/clapper-app/clapper-app-preferences-window.c:445
msgid "Fast"
msgstr "तेज"
#: src/bin/clapper-app/clapper-app-utils.c:66
msgid "No progression"
msgstr "प्रगति नहीं"
#: src/bin/clapper-app/clapper-app-utils.c:67
msgid "Consecutive"
msgstr "लगातार"
#: src/bin/clapper-app/clapper-app-utils.c:68
msgid "Repeat item"
msgstr "मद दोहराएं"
#: src/bin/clapper-app/clapper-app-utils.c:69
msgid "Carousel"
msgstr "कैरोसेल"
#: src/bin/clapper-app/clapper-app-utils.c:70
msgid "Shuffle"
msgstr "शफल"
#: src/bin/clapper-app/clapper-app-window.c:828
msgid "Drop on title bar to play now or anywhere else to enqueue."
msgstr "अभी चलाने के लिए शीर्षक पट्टी पर छोड़ें या कतार में लगाने के लिए कहीं और छोड़ें।"

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: clapper\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-21 20:22+0200\n"
"PO-Revision-Date: 2024-04-21 20:51\n"
"PO-Revision-Date: 2024-10-05 12:47\n"
"Last-Translator: \n"
"Language-Team: Norwegian\n"
"Language: no_NO\n"
@@ -20,450 +20,450 @@ msgstr ""
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:14
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:14
msgid "Codec"
msgstr ""
msgstr "Codec"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:24
msgid "Channels"
msgstr ""
msgstr "Kanaler"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:36
msgid "Sample Format"
msgstr ""
msgstr "Samplingsformat"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:46
msgid "Sample Rate"
msgstr ""
msgstr "Samplingsfrekvens"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:58
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:51
msgid "Bitrate"
msgstr ""
msgstr "Bitrate"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:70
#: src/bin/clapper-app/ui/clapper-app-subtitle-stream-list-item.ui:24
msgid "Language"
msgstr ""
msgstr "Språk"
#: src/bin/clapper-app/ui/clapper-app-headerbar.ui:183
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:80
msgid "Preferences"
msgstr ""
msgstr "Brukervalg"
#: src/bin/clapper-app/ui/clapper-app-headerbar.ui:187
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:84
msgid "Keyboard Shortcuts"
msgstr ""
msgstr "Tastatursnarveier"
#. TRANSLATORS: Please do not translate application name
#: src/bin/clapper-app/ui/clapper-app-headerbar.ui:194
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:91
msgid "About Clapper"
msgstr ""
msgstr "Om Clapper"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:10
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:11
msgid "General"
msgstr ""
msgstr "Generelt"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:13
msgid "Show shortcuts"
msgstr ""
msgstr "Vis tastatursnarveier"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:19
msgid "Open preferences"
msgstr ""
msgstr "Åpne brukervalg"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:25
msgid "Toggle fullscreen"
msgstr ""
msgstr "Slå på/av fullskjermsmodus"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:26
msgid "Double tap | Double click"
msgstr ""
msgstr "Dobbelttrykk | Dobbeltklikk"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:32
msgid "Quit"
msgstr ""
msgstr "Avslutt"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:40
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:32
msgid "Media"
msgstr ""
msgstr "Medium"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:43
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:55
#: src/bin/clapper-app/ui/clapper-app-queue-list.ui:178
msgid "Add Files…"
msgstr ""
msgstr "Legg til filer…"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:49
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:65
#: src/bin/clapper-app/ui/clapper-app-queue-list.ui:182
msgid "Add URI…"
msgstr ""
msgstr "Legg til URI…"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:57
msgid "Queue"
msgstr ""
msgstr ""
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:61
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:68
msgid "Next item"
msgstr ""
msgstr "Neste element"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:75
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:82
msgid "Previous item"
msgstr ""
msgstr "Forrige element"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:88
msgid "Change progression mode"
msgstr ""
msgstr "Endre progresjonsmodus"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:96
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:76
msgid "Playback"
msgstr ""
msgstr "Avspilling"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:99
msgid "Toggle play"
msgstr ""
msgstr "Spill av eller pause"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:100
msgid "Tap | Left click"
msgstr ""
msgstr "Trykk | Venstreklikk"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:107
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:115
msgid "Seek forward"
msgstr ""
msgstr "Søk fremover"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:108
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:132
msgid "Double tap (right side) | Scroll right"
msgstr ""
msgstr "Dobbelttrykk (på høyre side) | Rull til høyre"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:116
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:124
msgid "Double tap (left side) | Scroll left"
msgstr ""
msgstr "Dobbelttrykk (på venstre side) | Rull til venstre"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:123
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:131
msgid "Seek backward"
msgstr ""
msgstr "Søk bakover"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:138
msgid "Volume up"
msgstr ""
msgstr "Øk volym"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:139
msgid "Scroll up"
msgstr ""
msgstr "Rull opp"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:145
msgid "Volume down"
msgstr ""
msgstr "Senk volym"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:146
msgid "Scroll down"
msgstr ""
msgstr "Rull ned"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:152
msgid "Toggle mute"
msgstr ""
msgstr "Demp/avdemp"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:158
msgid "Speed up"
msgstr ""
msgstr "Øk farten"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:164
msgid "Speed down"
msgstr ""
msgstr "Senk farten"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:171
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:178
msgid "Next chapter"
msgstr ""
msgstr "Neste kapittel"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:185
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:192
msgid "Previous chapter"
msgstr ""
msgstr "Forrige kapittel"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:13
msgid "Info"
msgstr ""
msgstr "Informasjon"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:35
#: src/bin/clapper-app/ui/clapper-app-subtitle-stream-list-item.ui:14
msgid "Title"
msgstr ""
msgstr "Tittel"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:49
msgid "Container Format"
msgstr ""
msgstr "Konteinerformat"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:63
msgid "Duration"
msgstr ""
msgstr "Lengde"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:81
msgid "Streams"
msgstr ""
msgstr "Strømmer"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:84
#: src/bin/clapper-app/clapper-app-list-item-utils.c:36
msgid "Video"
msgstr ""
msgstr "Video"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:107
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:80
#: src/bin/clapper-app/clapper-app-list-item-utils.c:39
msgid "Audio"
msgstr ""
msgstr "Lyd"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:130
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:100
#: src/bin/clapper-app/clapper-app-list-item-utils.c:42
msgid "Subtitles"
msgstr ""
msgstr "Undertekstning"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:155
msgid "Video Playback"
msgstr ""
msgstr "Videoavspilling"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:158
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:199
msgid "Decoder"
msgstr ""
msgstr "Dekoder"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:170
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:211
msgid "Filter"
msgstr ""
msgstr "Filter"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:182
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:223
msgid "Sink"
msgstr ""
msgstr "Vask"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:196
msgid "Audio Playback"
msgstr ""
msgstr "Lydavspilling"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:247
msgid "Video Streams"
msgstr ""
msgstr "Videostrømmer"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:290
msgid "Audio Streams"
msgstr ""
msgstr "Lydstrømmer"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:333
msgid "Subtitle Streams"
msgstr ""
msgstr "Undertekstingsstrømmer"
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:46
msgid "Start by adding media to playback queue"
msgstr ""
msgstr "Start ved å legge til medier i avspillingskøen"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:15
msgid "Seeking"
msgstr ""
msgstr "Søkning"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:18
msgid "Method"
msgstr ""
msgstr "Metode"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:19
msgid "A preferred method used for seeking"
msgstr ""
msgstr "En foretrukket metode brukt for å søke"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:32
msgid "Value"
msgstr ""
msgstr "Verdi"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:33
msgid "Value used for seeking forward/backward"
msgstr ""
msgstr "Verdi brukt for å søke fremover/bakover"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:46
msgid "Unit"
msgstr ""
msgstr "Enhet"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:47
msgid "An unit of a seek forward/backward value"
msgstr ""
msgstr "En enhet av en søkningsverdi"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:51
msgid "Second"
msgstr ""
msgstr "Sekund"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:52
msgid "Minute"
msgstr ""
msgstr "Minutt"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:53
msgid "Percentage"
msgstr ""
msgstr "Prosentdel"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:63
msgid "Features"
msgstr ""
msgstr "Funksjoner"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:66
msgid "Server"
msgstr ""
msgstr "Server"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:67
msgid "Control player remotely"
msgstr ""
msgstr "Fjernstyring av spilleren"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:83
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:103
msgid "Offset"
msgstr ""
msgstr "Forskyvning"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:84
msgid "Synchronisation offset in seconds between the audio and video streams"
msgstr ""
msgstr "Synkroniseringsforskyvning i sekunder mellom lyd- og videostrømmene"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:104
msgid "Synchronisation offset in seconds between the subtitle and video streams"
msgstr ""
msgstr "Synkroniseringsforskyvning i sekunder mellom untertekst- og videostrømmene"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:118
msgid "Default font"
msgstr ""
msgstr "Standard skrifttype"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:119
msgid "Text font used for subtitles when media does not explicitly specify one"
msgstr ""
msgstr "Skrifttype som brukes for undertekster når mediet ikke angir en skrifttype"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:137
msgid "Tweaks"
msgstr ""
msgstr "Justeringer"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:144
msgid "Plugin ranking"
msgstr ""
msgstr "Tilleggsrangering"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:145
msgid "Alter default ranks of GStreamer plugins"
msgstr ""
msgstr "Endre den standarde rangeringen av GStreamer-tillegg"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:164
msgid "Plugin Ranking"
msgstr ""
msgstr "Tilleggsrangering"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:182
msgid "Available plugins"
msgstr ""
msgstr "Tilgjengelige tillegg"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:183
msgid "Select a plugin and its feature to override rank for. When multiple elements have similiar capabilities, the one with highest value is preferred."
msgstr ""
msgstr "Velg et tillegg og funskjonen dets for å overstyre rangen. Når flere elementer har lignende egenskaper, skal det med den høyeste verdien brukes."
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:186
msgid "Plugin"
msgstr ""
msgstr "Tillegg"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:195
msgid "Plugin feature"
msgstr ""
msgstr "Tilleggsfunksjon"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:213
msgid "Add override"
msgstr ""
msgstr "Legg till overstyring"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:229
msgid "Rank overrides"
msgstr ""
msgstr "Rangoverstyringer"
#: src/bin/clapper-app/ui/clapper-app-queue-list.ui:60
#: src/bin/clapper-app/ui/clapper-app-uri-dialog.ui:23
msgid "Add"
msgstr ""
msgstr "Legg til"
#: src/bin/clapper-app/ui/clapper-app-queue-list.ui:87
msgid "Queue progression"
msgstr ""
msgstr "Køprogresjon"
#: src/bin/clapper-app/ui/clapper-app-uri-dialog.ui:6
msgid "Add URI"
msgstr ""
msgstr "Legg til URI"
#: src/bin/clapper-app/ui/clapper-app-uri-dialog.ui:7
msgid "Insert an URI to be added to playback queue"
msgstr ""
msgstr "Tast inn en URI for å legge den til i avspillingskøen"
#: src/bin/clapper-app/ui/clapper-app-uri-dialog.ui:18
msgid "Enter or drop URI here"
msgstr ""
msgstr "Tast inn eller slipp URI her"
#: src/bin/clapper-app/ui/clapper-app-uri-dialog.ui:22
msgid "Cancel"
msgstr ""
msgstr "Avbryt"
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:24
msgid "Resolution"
msgstr ""
msgstr "Oppløsning"
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:39
msgid "Framerate"
msgstr ""
msgstr "Bildefrekvens"
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:63
msgid "Pixel Format"
msgstr ""
msgstr "Pikselformat"
#. TRANSLATORS: Put your name(s) here for credits or leave untranslated
#: src/bin/clapper-app/clapper-app-about-window.c:43
msgid "translator-credits"
msgstr ""
msgstr "Sunniva Løvstad <clapper@turtle.garden>"
#: src/bin/clapper-app/clapper-app-info-window.c:86
msgid "Hardware"
msgstr ""
msgstr "Maskinvare"
#: src/bin/clapper-app/clapper-app-info-window.c:86
msgid "Software"
msgstr ""
msgstr "Programvare"
#: src/bin/clapper-app/clapper-app-preferences-window.c:441
msgid "Accurate"
msgstr ""
msgstr "Nøyaktig"
#: src/bin/clapper-app/clapper-app-preferences-window.c:443
msgid "Normal"
msgstr ""
msgstr "Normal"
#: src/bin/clapper-app/clapper-app-preferences-window.c:445
msgid "Fast"
msgstr ""
msgstr "Rask"
#: src/bin/clapper-app/clapper-app-utils.c:66
msgid "No progression"
msgstr ""
msgstr "Ingen progresjon"
#: src/bin/clapper-app/clapper-app-utils.c:67
msgid "Consecutive"
msgstr ""
msgstr "Konsekutiv"
#: src/bin/clapper-app/clapper-app-utils.c:68
msgid "Repeat item"
msgstr ""
msgstr "Gjenta element"
#: src/bin/clapper-app/clapper-app-utils.c:69
msgid "Carousel"
msgstr ""
msgstr "Gjenta kø"
#: src/bin/clapper-app/clapper-app-utils.c:70
msgid "Shuffle"
msgstr ""
msgstr "Omstokking"
#: src/bin/clapper-app/clapper-app-window.c:828
msgid "Drop on title bar to play now or anywhere else to enqueue."
msgstr ""
msgstr "Slipp på tittellinjen for å spille av nå, eller hvor som helst annet for å legg til i køen."

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: clapper\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-21 20:22+0200\n"
"PO-Revision-Date: 2024-04-21 20:51\n"
"PO-Revision-Date: 2024-08-11 15:57\n"
"Last-Translator: \n"
"Language-Team: Russian\n"
"Language: ru_RU\n"
@@ -20,7 +20,7 @@ msgstr ""
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:14
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:14
msgid "Codec"
msgstr ""
msgstr "Кодек"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:24
msgid "Channels"
@@ -28,21 +28,21 @@ msgstr "Каналы"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:36
msgid "Sample Format"
msgstr ""
msgstr "Формат семпла"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:46
msgid "Sample Rate"
msgstr ""
msgstr "Частота дискретизации"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:58
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:51
msgid "Bitrate"
msgstr ""
msgstr "Битрейт"
#: src/bin/clapper-app/ui/clapper-app-audio-stream-list-item.ui:70
#: src/bin/clapper-app/ui/clapper-app-subtitle-stream-list-item.ui:24
msgid "Language"
msgstr ""
msgstr "Язык"
#: src/bin/clapper-app/ui/clapper-app-headerbar.ui:183
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:80
@@ -52,7 +52,7 @@ msgstr "Параметры"
#: src/bin/clapper-app/ui/clapper-app-headerbar.ui:187
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:84
msgid "Keyboard Shortcuts"
msgstr ""
msgstr "Сочетания клавиш"
#. TRANSLATORS: Please do not translate application name
#: src/bin/clapper-app/ui/clapper-app-headerbar.ui:194
@@ -71,7 +71,7 @@ msgstr "Комбинации клавиш"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:19
msgid "Open preferences"
msgstr ""
msgstr "Открыть настройки"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:25
msgid "Toggle fullscreen"
@@ -94,17 +94,17 @@ msgstr "Медиа"
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:55
#: src/bin/clapper-app/ui/clapper-app-queue-list.ui:178
msgid "Add Files…"
msgstr ""
msgstr "Добавить файлы…"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:49
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:65
#: src/bin/clapper-app/ui/clapper-app-queue-list.ui:182
msgid "Add URI…"
msgstr ""
msgstr "Открыть по ссылке…"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:57
msgid "Queue"
msgstr ""
msgstr "Дорожка"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:61
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:68
@@ -118,7 +118,7 @@ msgstr "Предыдущий элемент"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:88
msgid "Change progression mode"
msgstr ""
msgstr "Изменить режим повтора"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:96
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:76
@@ -131,7 +131,7 @@ msgstr "Управление воспроизведением"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:100
msgid "Tap | Left click"
msgstr ""
msgstr "Тап | Левая кнопка мыши"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:107
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:115
@@ -141,12 +141,12 @@ msgstr "Перемотка вперед"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:108
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:132
msgid "Double tap (right side) | Scroll right"
msgstr ""
msgstr "Двойной тап (правая сторона) | Скролл вправо"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:116
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:124
msgid "Double tap (left side) | Scroll left"
msgstr ""
msgstr "Двойной тап (левая сторона) | Скролл влево"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:123
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:131
@@ -159,7 +159,7 @@ msgstr "Увеличить громкость"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:139
msgid "Scroll up"
msgstr ""
msgstr "Скролл вверх"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:145
msgid "Volume down"
@@ -167,7 +167,7 @@ msgstr "Уменьшить громкость"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:146
msgid "Scroll down"
msgstr ""
msgstr "Скролл вниз"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:152
msgid "Toggle mute"
@@ -175,11 +175,11 @@ msgstr "Отключить звук"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:158
msgid "Speed up"
msgstr ""
msgstr "Ускорить"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:164
msgid "Speed down"
msgstr ""
msgstr "Замедлить"
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:171
#: src/bin/clapper-app/ui/clapper-app-help-overlay.ui:178
@@ -193,7 +193,7 @@ msgstr "Предыдущая глава"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:13
msgid "Info"
msgstr ""
msgstr "Информация"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:35
#: src/bin/clapper-app/ui/clapper-app-subtitle-stream-list-item.ui:14
@@ -202,20 +202,20 @@ msgstr "Заголовок"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:49
msgid "Container Format"
msgstr ""
msgstr "Формат контейнера"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:63
msgid "Duration"
msgstr ""
msgstr "Длительность"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:81
msgid "Streams"
msgstr ""
msgstr "Потоки"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:84
#: src/bin/clapper-app/clapper-app-list-item-utils.c:36
msgid "Video"
msgstr ""
msgstr "Видео"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:107
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:80
@@ -231,42 +231,42 @@ msgstr "Субтитры"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:155
msgid "Video Playback"
msgstr ""
msgstr "Воспроизведение видео"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:158
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:199
msgid "Decoder"
msgstr ""
msgstr "Декодер"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:170
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:211
msgid "Filter"
msgstr ""
msgstr "Фильтр"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:182
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:223
msgid "Sink"
msgstr ""
msgstr "Приемник"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:196
msgid "Audio Playback"
msgstr ""
msgstr "Воспроизведение звука"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:247
msgid "Video Streams"
msgstr ""
msgstr "Видеопотоки"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:290
msgid "Audio Streams"
msgstr ""
msgstr "Аудиопотоки"
#: src/bin/clapper-app/ui/clapper-app-info-window.ui:333
msgid "Subtitle Streams"
msgstr ""
msgstr "Потоки субтитров"
#: src/bin/clapper-app/ui/clapper-app-initial-state.ui:46
msgid "Start by adding media to playback queue"
msgstr ""
msgstr "Начните с добавления медиа в очередь воспроизведения"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:15
msgid "Seeking"
@@ -274,11 +274,11 @@ msgstr "Перемотка"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:18
msgid "Method"
msgstr ""
msgstr "Метод"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:19
msgid "A preferred method used for seeking"
msgstr ""
msgstr "Предпочтительный метод, используемый для поиска"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:32
msgid "Value"
@@ -286,7 +286,7 @@ msgstr "Значение"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:33
msgid "Value used for seeking forward/backward"
msgstr ""
msgstr "Значение, используемое для поиска вперед/назад"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:46
msgid "Unit"
@@ -294,7 +294,7 @@ msgstr "Ед. изм"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:47
msgid "An unit of a seek forward/backward value"
msgstr ""
msgstr "Единица поиска вперёд/назад"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:51
msgid "Second"
@@ -310,7 +310,7 @@ msgstr "Проценты"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:63
msgid "Features"
msgstr ""
msgstr "Дополнительные настройки"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:66
msgid "Server"
@@ -323,15 +323,15 @@ msgstr "Удаленное управление плеером"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:83
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:103
msgid "Offset"
msgstr ""
msgstr "Задержка"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:84
msgid "Synchronisation offset in seconds between the audio and video streams"
msgstr ""
msgstr "Задержка синхронизации в секундах между аудио- и видеопотоками"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:104
msgid "Synchronisation offset in seconds between the subtitle and video streams"
msgstr ""
msgstr "Задержка синхронизации в секундах между потоком субтитров и видеопотоком"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:118
msgid "Default font"
@@ -339,7 +339,7 @@ msgstr "Шрифт по умолчанию"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:119
msgid "Text font used for subtitles when media does not explicitly specify one"
msgstr ""
msgstr "Шрифт, используемый для субтитров, если не указан какой именно"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:137
msgid "Tweaks"
@@ -355,48 +355,48 @@ msgstr "Изменить ранги плагинов GStreamer по умолча
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:164
msgid "Plugin Ranking"
msgstr ""
msgstr "Ранжирование плагинов"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:182
msgid "Available plugins"
msgstr ""
msgstr "Доступные плагины"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:183
msgid "Select a plugin and its feature to override rank for. When multiple elements have similiar capabilities, the one with highest value is preferred."
msgstr ""
msgstr "Выберите плагин и его функцию, чтобы переопределить ранжирование. Когда несколько элементов имеют схожие возможности, предпочтение имеет один с наибольшим значением."
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:186
msgid "Plugin"
msgstr ""
msgstr "Плагин"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:195
msgid "Plugin feature"
msgstr ""
msgstr "Настройка плагина"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:213
msgid "Add override"
msgstr ""
msgstr "Добавить переопределение"
#: src/bin/clapper-app/ui/clapper-app-preferences-window.ui:229
msgid "Rank overrides"
msgstr ""
msgstr "Переопределение ранжирования"
#: src/bin/clapper-app/ui/clapper-app-queue-list.ui:60
#: src/bin/clapper-app/ui/clapper-app-uri-dialog.ui:23
msgid "Add"
msgstr ""
msgstr "Добавить"
#: src/bin/clapper-app/ui/clapper-app-queue-list.ui:87
msgid "Queue progression"
msgstr ""
msgstr "Прогресс очереди"
#: src/bin/clapper-app/ui/clapper-app-uri-dialog.ui:6
msgid "Add URI"
msgstr ""
msgstr "Открыть по ссылке"
#: src/bin/clapper-app/ui/clapper-app-uri-dialog.ui:7
msgid "Insert an URI to be added to playback queue"
msgstr ""
msgstr "Вставьте URI, который будет добавлен в очередь воспроизведения"
#: src/bin/clapper-app/ui/clapper-app-uri-dialog.ui:18
msgid "Enter or drop URI here"
@@ -408,15 +408,15 @@ msgstr "Отмена"
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:24
msgid "Resolution"
msgstr ""
msgstr "Разрешение"
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:39
msgid "Framerate"
msgstr ""
msgstr "Частота кадров"
#: src/bin/clapper-app/ui/clapper-app-video-stream-list-item.ui:63
msgid "Pixel Format"
msgstr ""
msgstr "Формат пикселей"
#. TRANSLATORS: Put your name(s) here for credits or leave untranslated
#: src/bin/clapper-app/clapper-app-about-window.c:43
@@ -425,11 +425,11 @@ msgstr "Игорь Дятлов"
#: src/bin/clapper-app/clapper-app-info-window.c:86
msgid "Hardware"
msgstr ""
msgstr "Аппаратное"
#: src/bin/clapper-app/clapper-app-info-window.c:86
msgid "Software"
msgstr ""
msgstr "Программное"
#: src/bin/clapper-app/clapper-app-preferences-window.c:441
msgid "Accurate"
@@ -445,25 +445,25 @@ msgstr "Быстрый"
#: src/bin/clapper-app/clapper-app-utils.c:66
msgid "No progression"
msgstr ""
msgstr "Нет прогрессии"
#: src/bin/clapper-app/clapper-app-utils.c:67
msgid "Consecutive"
msgstr ""
msgstr "Последовательно"
#: src/bin/clapper-app/clapper-app-utils.c:68
msgid "Repeat item"
msgstr ""
msgstr "Повторить элемент"
#: src/bin/clapper-app/clapper-app-utils.c:69
msgid "Carousel"
msgstr ""
msgstr "Повтор очереди"
#: src/bin/clapper-app/clapper-app-utils.c:70
msgid "Shuffle"
msgstr ""
msgstr "Случайно"
#: src/bin/clapper-app/clapper-app-window.c:828
msgid "Drop on title bar to play now or anywhere else to enqueue."
msgstr ""
msgstr "Перетащите на строку заголовка, чтобы воспроизвести сейчас или где-нибудь ещё, чтобы попасть в очередь."

View File

@@ -178,6 +178,12 @@
</child>
</template>
<menu id="app_menu">
<section>
<item>
<attribute name="label" translatable="yes">New Window</attribute>
<attribute name="action">app.new-window</attribute>
</item>
</section>
<section>
<item>
<attribute name="label" translatable="yes">Preferences</attribute>

View File

@@ -10,8 +10,8 @@
<property name="title" translatable="yes">General</property>
<child>
<object class="GtkShortcutsShortcut">
<property name="title" translatable="yes">Show shortcuts</property>
<property name="accelerator">&lt;Ctrl&gt;question</property>
<property name="title" translatable="yes">New window</property>
<property name="accelerator">&lt;Ctrl&gt;n</property>
</object>
</child>
<child>
@@ -20,6 +20,12 @@
<property name="accelerator">&lt;Ctrl&gt;comma</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="title" translatable="yes">Show shortcuts</property>
<property name="accelerator">&lt;Ctrl&gt;question</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="title" translatable="yes">Toggle fullscreen</property>
@@ -27,6 +33,19 @@
<property name="accelerator">F11 f</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="title" translatable="yes">Unfullscreen</property>
<property name="accelerator">Escape</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="title" translatable="yes">Auto window resize</property>
<property name="subtitle" translatable="yes">Middle click</property>
<property name="accelerator">&lt;Super&gt;r</property>
</object>
</child>
<child>
<object class="GtkShortcutsShortcut">
<property name="title" translatable="yes">Quit</property>

View File

@@ -232,6 +232,16 @@
</child>
</object>
</child>
<child>
<object class="GtkButton">
<property name="halign">center</property>
<property name="label" translatable="yes">Show Pipeline</property>
<property name="action-name">app.pipeline</property>
<style>
<class name="pill"/>
</style>
</object>
</child>
</object>
</child>
</object>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface domain="clapper-app">
<template class="ClapperAppPipelineWindow" parent="AdwWindow">
<property name="title" translatable="yes">Pipeline</property>
<property name="width-request">280</property>
<property name="height-request">280</property>
<property name="default-width">1400</property>
<property name="default-height">720</property>
<property name="modal">true</property>
<property name="content">
<object class="AdwToolbarView">
<child type="top">
<object class="AdwHeaderBar"/>
</child>
<property name="content">
<object class="GtkScrolledWindow">
<child>
<object class="ClapperAppPipelineViewer" id="pipeline_viewer"/>
</child>
</object>
</property>
</object>
</property>
<style>
<class name="pipeline"/>
</style>
</template>
</interface>

View File

@@ -47,9 +47,9 @@
</child>
<child>
<object class="GtkGestureClick">
<property name="button">3</property>
<signal name="pressed" handler="right_click_pressed_cb"/>
<signal name="released" handler="right_click_released_cb"/>
<property name="button">0</property>
<signal name="pressed" handler="click_pressed_cb"/>
<signal name="released" handler="click_released_cb"/>
</object>
</child>
<child>
@@ -68,8 +68,5 @@
<signal name="drop" handler="drop_cb"/>
</object>
</child>
<style>
<class name="app"/>
</style>
</template>
</interface>

View File

@@ -27,6 +27,7 @@
#include <glib-object.h>
#include <gtk/gtk.h>
#include <clapper-gtk/clapper-gtk-visibility.h>
#include <clapper-gtk/clapper-gtk-container.h>
G_BEGIN_DECLS
@@ -34,18 +35,25 @@ G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_BILLBOARD (clapper_gtk_billboard_get_type())
#define CLAPPER_GTK_BILLBOARD_CAST(obj) ((ClapperGtkBillboard *)(obj))
CLAPPER_GTK_API
G_DECLARE_FINAL_TYPE (ClapperGtkBillboard, clapper_gtk_billboard, CLAPPER_GTK, BILLBOARD, ClapperGtkContainer)
CLAPPER_GTK_API
GtkWidget * clapper_gtk_billboard_new (void);
CLAPPER_GTK_API
void clapper_gtk_billboard_post_message (ClapperGtkBillboard *billboard, const gchar *icon_name, const gchar *message);
CLAPPER_GTK_API
void clapper_gtk_billboard_pin_message (ClapperGtkBillboard *billboard, const gchar *icon_name, const gchar *message);
CLAPPER_GTK_API
void clapper_gtk_billboard_unpin_pinned_message (ClapperGtkBillboard *billboard);
CLAPPER_GTK_API
void clapper_gtk_billboard_announce_volume (ClapperGtkBillboard *billboard);
CLAPPER_GTK_API
void clapper_gtk_billboard_announce_speed (ClapperGtkBillboard *billboard);
G_END_DECLS

View File

@@ -27,11 +27,14 @@
#include <glib-object.h>
#include <gtk/gtk.h>
#include <clapper-gtk/clapper-gtk-visibility.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_CONTAINER (clapper_gtk_container_get_type())
#define CLAPPER_GTK_CONTAINER_CAST(obj) ((ClapperGtkContainer *)(obj))
CLAPPER_GTK_API
G_DECLARE_DERIVABLE_TYPE (ClapperGtkContainer, clapper_gtk_container, CLAPPER_GTK, CONTAINER, GtkWidget)
struct _ClapperGtkContainerClass
@@ -42,26 +45,37 @@ struct _ClapperGtkContainerClass
gpointer padding[4];
};
CLAPPER_GTK_API
GtkWidget * clapper_gtk_container_new (void);
CLAPPER_GTK_API
void clapper_gtk_container_set_child (ClapperGtkContainer *container, GtkWidget *child);
CLAPPER_GTK_API
GtkWidget * clapper_gtk_container_get_child (ClapperGtkContainer *container);
CLAPPER_GTK_API
void clapper_gtk_container_set_width_target (ClapperGtkContainer *container, gint width);
CLAPPER_GTK_API
gint clapper_gtk_container_get_width_target (ClapperGtkContainer *container);
CLAPPER_GTK_API
void clapper_gtk_container_set_height_target (ClapperGtkContainer *container, gint height);
CLAPPER_GTK_API
gint clapper_gtk_container_get_height_target (ClapperGtkContainer *container);
CLAPPER_GTK_API
void clapper_gtk_container_set_adaptive_width (ClapperGtkContainer *container, gint width);
CLAPPER_GTK_API
gint clapper_gtk_container_get_adaptive_width (ClapperGtkContainer *container);
CLAPPER_GTK_API
void clapper_gtk_container_set_adaptive_height (ClapperGtkContainer *container, gint height);
CLAPPER_GTK_API
gint clapper_gtk_container_get_adaptive_height (ClapperGtkContainer *container);
G_END_DECLS

View File

@@ -27,25 +27,35 @@
#include <glib-object.h>
#include <gtk/gtk.h>
#include <clapper-gtk/clapper-gtk-visibility.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_EXTRA_MENU_BUTTON (clapper_gtk_extra_menu_button_get_type())
#define CLAPPER_GTK_EXTRA_MENU_BUTTON_CAST(obj) ((ClapperGtkExtraMenuButton *)(obj))
CLAPPER_GTK_API
G_DECLARE_FINAL_TYPE (ClapperGtkExtraMenuButton, clapper_gtk_extra_menu_button, CLAPPER_GTK, EXTRA_MENU_BUTTON, GtkWidget)
CLAPPER_GTK_API
GtkWidget * clapper_gtk_extra_menu_button_new (void);
CLAPPER_GTK_API
void clapper_gtk_extra_menu_button_set_volume_visible (ClapperGtkExtraMenuButton *button, gboolean visible);
CLAPPER_GTK_API
gboolean clapper_gtk_extra_menu_button_get_volume_visible (ClapperGtkExtraMenuButton *button);
CLAPPER_GTK_API
void clapper_gtk_extra_menu_button_set_speed_visible (ClapperGtkExtraMenuButton *button, gboolean visible);
CLAPPER_GTK_API
gboolean clapper_gtk_extra_menu_button_get_speed_visible (ClapperGtkExtraMenuButton *button);
CLAPPER_GTK_API
void clapper_gtk_extra_menu_button_set_can_open_subtitles (ClapperGtkExtraMenuButton *button, gboolean allowed);
CLAPPER_GTK_API
gboolean clapper_gtk_extra_menu_button_get_can_open_subtitles (ClapperGtkExtraMenuButton *button);
G_END_DECLS

View File

@@ -27,6 +27,7 @@
#include <glib-object.h>
#include <gtk/gtk.h>
#include <clapper-gtk/clapper-gtk-visibility.h>
#include <clapper-gtk/clapper-gtk-enums.h>
#include <clapper-gtk/clapper-gtk-container.h>
@@ -35,6 +36,7 @@ G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_LEAD_CONTAINER (clapper_gtk_lead_container_get_type())
#define CLAPPER_GTK_LEAD_CONTAINER_CAST(obj) ((ClapperGtkLeadContainer *)(obj))
CLAPPER_GTK_API
G_DECLARE_DERIVABLE_TYPE (ClapperGtkLeadContainer, clapper_gtk_lead_container, CLAPPER_GTK, LEAD_CONTAINER, ClapperGtkContainer)
struct _ClapperGtkLeadContainerClass
@@ -45,14 +47,19 @@ struct _ClapperGtkLeadContainerClass
gpointer padding[4];
};
CLAPPER_GTK_API
GtkWidget * clapper_gtk_lead_container_new (void);
CLAPPER_GTK_API
void clapper_gtk_lead_container_set_leading (ClapperGtkLeadContainer *lead_container, gboolean leading);
CLAPPER_GTK_API
gboolean clapper_gtk_lead_container_get_leading (ClapperGtkLeadContainer *lead_container);
CLAPPER_GTK_API
void clapper_gtk_lead_container_set_blocked_actions (ClapperGtkLeadContainer *lead_container, ClapperGtkVideoActionMask actions);
CLAPPER_GTK_API
ClapperGtkVideoActionMask clapper_gtk_lead_container_get_blocked_actions (ClapperGtkLeadContainer *lead_container);
G_END_DECLS

View File

@@ -27,13 +27,17 @@
#include <glib-object.h>
#include <gtk/gtk.h>
#include <clapper-gtk/clapper-gtk-visibility.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_NEXT_ITEM_BUTTON (clapper_gtk_next_item_button_get_type())
#define CLAPPER_GTK_NEXT_ITEM_BUTTON_CAST(obj) ((ClapperGtkNextItemButton *)(obj))
CLAPPER_GTK_API
G_DECLARE_FINAL_TYPE (ClapperGtkNextItemButton, clapper_gtk_next_item_button, CLAPPER_GTK, NEXT_ITEM_BUTTON, GtkButton)
CLAPPER_GTK_API
GtkWidget * clapper_gtk_next_item_button_new (void);
G_END_DECLS

View File

@@ -27,13 +27,17 @@
#include <glib-object.h>
#include <gtk/gtk.h>
#include <clapper-gtk/clapper-gtk-visibility.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_PREVIOUS_ITEM_BUTTON (clapper_gtk_previous_item_button_get_type())
#define CLAPPER_GTK_PREVIOUS_ITEM_BUTTON_CAST(obj) ((ClapperGtkPreviousItemButton *)(obj))
CLAPPER_GTK_API
G_DECLARE_FINAL_TYPE (ClapperGtkPreviousItemButton, clapper_gtk_previous_item_button, CLAPPER_GTK, PREVIOUS_ITEM_BUTTON, GtkButton)
CLAPPER_GTK_API
GtkWidget * clapper_gtk_previous_item_button_new (void);
G_END_DECLS

View File

@@ -28,21 +28,29 @@
#include <gtk/gtk.h>
#include <clapper/clapper.h>
#include <clapper-gtk/clapper-gtk-visibility.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_SEEK_BAR (clapper_gtk_seek_bar_get_type())
#define CLAPPER_GTK_SEEK_BAR_CAST(obj) ((ClapperGtkSeekBar *)(obj))
CLAPPER_GTK_API
G_DECLARE_FINAL_TYPE (ClapperGtkSeekBar, clapper_gtk_seek_bar, CLAPPER_GTK, SEEK_BAR, GtkWidget)
CLAPPER_GTK_API
GtkWidget * clapper_gtk_seek_bar_new (void);
CLAPPER_GTK_API
void clapper_gtk_seek_bar_set_reveal_labels (ClapperGtkSeekBar *seek_bar, gboolean reveal);
CLAPPER_GTK_API
gboolean clapper_gtk_seek_bar_get_reveal_labels (ClapperGtkSeekBar *seek_bar);
CLAPPER_GTK_API
void clapper_gtk_seek_bar_set_seek_method (ClapperGtkSeekBar *seek_bar, ClapperPlayerSeekMethod method);
CLAPPER_GTK_API
ClapperPlayerSeekMethod clapper_gtk_seek_bar_get_seek_method (ClapperGtkSeekBar *seek_bar);
G_END_DECLS

View File

@@ -26,6 +26,8 @@
#include <glib.h>
#include <glib-object.h>
#include <gtk/gtk.h>
#include <clapper-gtk/clapper-gtk-visibility.h>
#include <clapper-gtk/clapper-gtk-container.h>
#include <clapper-gtk/clapper-gtk-extra-menu-button.h>
@@ -34,18 +36,25 @@ G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_SIMPLE_CONTROLS (clapper_gtk_simple_controls_get_type())
#define CLAPPER_GTK_SIMPLE_CONTROLS_CAST(obj) ((ClapperGtkSimpleControls *)(obj))
CLAPPER_GTK_API
G_DECLARE_FINAL_TYPE (ClapperGtkSimpleControls, clapper_gtk_simple_controls, CLAPPER_GTK, SIMPLE_CONTROLS, ClapperGtkContainer)
CLAPPER_GTK_API
GtkWidget * clapper_gtk_simple_controls_new (void);
CLAPPER_GTK_API
void clapper_gtk_simple_controls_set_fullscreenable (ClapperGtkSimpleControls *controls, gboolean fullscreenable);
CLAPPER_GTK_API
gboolean clapper_gtk_simple_controls_get_fullscreenable (ClapperGtkSimpleControls *controls);
CLAPPER_GTK_API
void clapper_gtk_simple_controls_set_seek_method (ClapperGtkSimpleControls *controls, ClapperPlayerSeekMethod method);
CLAPPER_GTK_API
ClapperPlayerSeekMethod clapper_gtk_simple_controls_get_seek_method (ClapperGtkSimpleControls *controls);
CLAPPER_GTK_API
ClapperGtkExtraMenuButton * clapper_gtk_simple_controls_get_extra_menu_button (ClapperGtkSimpleControls *controls);
G_END_DECLS

View File

@@ -26,6 +26,8 @@
#include <glib.h>
#include <glib-object.h>
#include <gtk/gtk.h>
#include <clapper-gtk/clapper-gtk-visibility.h>
#include <clapper-gtk/clapper-gtk-lead-container.h>
G_BEGIN_DECLS
@@ -33,14 +35,19 @@ G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_TITLE_HEADER (clapper_gtk_title_header_get_type())
#define CLAPPER_GTK_TITLE_HEADER_CAST(obj) ((ClapperGtkTitleHeader *)(obj))
CLAPPER_GTK_API
G_DECLARE_FINAL_TYPE (ClapperGtkTitleHeader, clapper_gtk_title_header, CLAPPER_GTK, TITLE_HEADER, ClapperGtkLeadContainer)
CLAPPER_GTK_API
GtkWidget * clapper_gtk_title_header_new (void);
CLAPPER_GTK_API
const gchar * clapper_gtk_title_header_get_current_title (ClapperGtkTitleHeader *header);
CLAPPER_GTK_API
void clapper_gtk_title_header_set_fallback_to_uri (ClapperGtkTitleHeader *header, gboolean enabled);
CLAPPER_GTK_API
gboolean clapper_gtk_title_header_get_fallback_to_uri (ClapperGtkTitleHeader *header);
G_END_DECLS

View File

@@ -28,23 +28,32 @@
#include <gtk/gtk.h>
#include <clapper/clapper.h>
#include <clapper-gtk/clapper-gtk-visibility.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_TITLE_LABEL (clapper_gtk_title_label_get_type())
#define CLAPPER_GTK_TITLE_LABEL_CAST(obj) ((ClapperGtkTitleLabel *)(obj))
CLAPPER_GTK_API
G_DECLARE_FINAL_TYPE (ClapperGtkTitleLabel, clapper_gtk_title_label, CLAPPER_GTK, TITLE_LABEL, GtkWidget)
CLAPPER_GTK_API
GtkWidget * clapper_gtk_title_label_new (void);
CLAPPER_GTK_API
void clapper_gtk_title_label_set_media_item (ClapperGtkTitleLabel *label, ClapperMediaItem *item);
CLAPPER_GTK_API
ClapperMediaItem * clapper_gtk_title_label_get_media_item (ClapperGtkTitleLabel *label);
CLAPPER_GTK_API
const gchar * clapper_gtk_title_label_get_current_title (ClapperGtkTitleLabel *label);
CLAPPER_GTK_API
void clapper_gtk_title_label_set_fallback_to_uri (ClapperGtkTitleLabel *label, gboolean enabled);
CLAPPER_GTK_API
gboolean clapper_gtk_title_label_get_fallback_to_uri (ClapperGtkTitleLabel *label);
G_END_DECLS

View File

@@ -27,13 +27,17 @@
#include <glib-object.h>
#include <gtk/gtk.h>
#include <clapper-gtk/clapper-gtk-visibility.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_TOGGLE_FULLSCREEN_BUTTON (clapper_gtk_toggle_fullscreen_button_get_type())
#define CLAPPER_GTK_TOGGLE_FULLSCREEN_BUTTON_CAST(obj) ((ClapperGtkToggleFullscreenButton *)(obj))
CLAPPER_GTK_API
G_DECLARE_FINAL_TYPE (ClapperGtkToggleFullscreenButton, clapper_gtk_toggle_fullscreen_button, CLAPPER_GTK, TOGGLE_FULLSCREEN_BUTTON, GtkButton)
CLAPPER_GTK_API
GtkWidget * clapper_gtk_toggle_fullscreen_button_new (void);
G_END_DECLS

View File

@@ -27,13 +27,17 @@
#include <glib-object.h>
#include <gtk/gtk.h>
#include <clapper-gtk/clapper-gtk-visibility.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_TOGGLE_PLAY_BUTTON (clapper_gtk_toggle_play_button_get_type())
#define CLAPPER_GTK_TOGGLE_PLAY_BUTTON_CAST(obj) ((ClapperGtkTogglePlayButton *)(obj))
CLAPPER_GTK_API
G_DECLARE_FINAL_TYPE (ClapperGtkTogglePlayButton, clapper_gtk_toggle_play_button, CLAPPER_GTK, TOGGLE_PLAY_BUTTON, GtkButton)
CLAPPER_GTK_API
GtkWidget * clapper_gtk_toggle_play_button_new (void);
G_END_DECLS

View File

@@ -27,8 +27,11 @@
#include <gtk/gtk.h>
#include <clapper/clapper.h>
#include <clapper-gtk/clapper-gtk-visibility.h>
G_BEGIN_DECLS
CLAPPER_GTK_API
ClapperPlayer * clapper_gtk_get_player_from_ancestor (GtkWidget *widget);
G_END_DECLS

View File

@@ -1065,7 +1065,7 @@ _player_missing_plugin_cb (ClapperPlayer *player, const gchar *name,
{
/* Some media files have custom/proprietary metadata,
* it should be safe to simply ignore these */
if (g_str_has_prefix (name, "meta/"))
if (strstr (name, "meta/") != NULL)
return;
/* XXX: Playbin2 seems to not emit state change here,
@@ -1583,7 +1583,7 @@ clapper_gtk_video_class_init (ClapperGtkVideoClass *klass)
*/
param_specs[PROP_FADE_DELAY] = g_param_spec_uint ("fade-delay",
NULL, NULL, 1, G_MAXUINT, DEFAULT_FADE_DELAY,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperGtkVideo:touch-fade-delay:
@@ -1593,7 +1593,7 @@ clapper_gtk_video_class_init (ClapperGtkVideoClass *klass)
*/
param_specs[PROP_TOUCH_FADE_DELAY] = g_param_spec_uint ("touch-fade-delay",
NULL, NULL, 1, G_MAXUINT, DEFAULT_TOUCH_FADE_DELAY,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperGtkVideo:auto-inhibit:

View File

@@ -28,33 +28,47 @@
#include <gtk/gtk.h>
#include <clapper/clapper.h>
#include <clapper-gtk/clapper-gtk-visibility.h>
G_BEGIN_DECLS
#define CLAPPER_GTK_TYPE_VIDEO (clapper_gtk_video_get_type())
#define CLAPPER_GTK_VIDEO_CAST(obj) ((ClapperGtkVideo *)(obj))
CLAPPER_GTK_API
G_DECLARE_FINAL_TYPE (ClapperGtkVideo, clapper_gtk_video, CLAPPER_GTK, VIDEO, GtkWidget)
CLAPPER_GTK_API
GtkWidget * clapper_gtk_video_new (void);
CLAPPER_GTK_API
void clapper_gtk_video_add_overlay (ClapperGtkVideo *video, GtkWidget *widget);
CLAPPER_GTK_API
void clapper_gtk_video_add_fading_overlay (ClapperGtkVideo *video, GtkWidget *widget);
CLAPPER_GTK_API
ClapperPlayer * clapper_gtk_video_get_player (ClapperGtkVideo *video);
CLAPPER_GTK_API
void clapper_gtk_video_set_fade_delay (ClapperGtkVideo *video, guint delay);
CLAPPER_GTK_API
guint clapper_gtk_video_get_fade_delay (ClapperGtkVideo *video);
CLAPPER_GTK_API
void clapper_gtk_video_set_touch_fade_delay (ClapperGtkVideo *video, guint delay);
CLAPPER_GTK_API
guint clapper_gtk_video_get_touch_fade_delay (ClapperGtkVideo *video);
CLAPPER_GTK_API
void clapper_gtk_video_set_auto_inhibit (ClapperGtkVideo *video, gboolean inhibit);
CLAPPER_GTK_API
gboolean clapper_gtk_video_get_auto_inhibit (ClapperGtkVideo *video);
CLAPPER_GTK_API
gboolean clapper_gtk_video_get_inhibited (ClapperGtkVideo *video);
G_END_DECLS

View File

@@ -39,6 +39,19 @@ configure_file(
configuration: config_h,
)
visibility_conf = configuration_data()
visibility_conf.set(
'CLAPPER_API',
'CLAPPER_GTK',
)
clappergtk_visibility_header = configure_file(
input: '../shared/clapper-api-visibility.h.in',
output: 'clapper-gtk-visibility.h',
configuration: visibility_conf,
)
version_conf = configuration_data()
version_conf.set(
@@ -93,6 +106,7 @@ clappergtk_headers = [
'clapper-gtk-utils.h',
'clapper-gtk-video.h',
clappergtk_version_header,
clappergtk_visibility_header,
]
clappergtk_sources = [
'clapper-gtk-billboard.c',
@@ -122,11 +136,17 @@ clappergtk_c_args = [
'-DGST_USE_UNSTABLE_API',
]
if get_option('default_library') == 'static'
clappergtk_c_args += ['-DCLAPPER_GTK_STATIC_COMPILATION']
endif
clappergtk_headers_dir = join_paths(includedir, clapper_api_name, 'clapper-gtk')
clappergtk_enums = gnome.mkenums_simple(
'clapper-gtk-enum-types',
sources: clappergtk_headers,
decorator: 'CLAPPER_GTK_API',
header_prefix: '#include <clapper-gtk/clapper-gtk-visibility.h>',
identifier_prefix: 'ClapperGtk',
symbol_prefix: 'clapper_gtk',
install_header: true,
@@ -215,6 +235,7 @@ clappergtk_dep = declare_dependency(
include_directories: clappergtk_conf_inc,
dependencies: clappergtk_deps,
sources: [
clappergtk_visibility_header,
clappergtk_version_header,
clappergtk_enums[1],
],

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: clapper\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-12 19:52+0200\n"
"PO-Revision-Date: 2024-04-12 18:47\n"
"PO-Revision-Date: 2024-08-12 08:36\n"
"Last-Translator: \n"
"Language-Team: Czech\n"
"Language: cs_CZ\n"
@@ -20,7 +20,7 @@ msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
msgstr "Video"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
@@ -34,33 +34,33 @@ msgstr "Titulky"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Show Subtitles"
msgstr ""
msgstr "Zobrazit Titulky"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
msgstr "Otevřít…"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
msgstr "Ztlumit"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
msgstr "Resetovat"
#: src/lib/clapper-gtk/clapper-gtk-status.c:79
msgid "Unplayable Content"
msgstr ""
msgstr "Nepřehratelný Obsah"
#. TRANSLATORS: Please do not try to translate "GStreamer" (it is a library name).
#: src/lib/clapper-gtk/clapper-gtk-status.c:89
#, c-format
msgid "Your GStreamer installation is missing a plugin: %s"
msgstr ""
msgstr "Vaší instalaci GStreamer chybí plugin: %s"
#: src/lib/clapper-gtk/clapper-gtk-status.c:90
msgid "Missing Plugin"
msgstr ""
msgstr "Chybějící Plugin"
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
@@ -73,9 +73,9 @@ msgstr "Kanály"
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
msgstr "Žádná média"
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""
msgstr "Neznámý název"

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: clapper\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-12 19:52+0200\n"
"PO-Revision-Date: 2024-04-12 18:48\n"
"PO-Revision-Date: 2024-09-07 11:10\n"
"Last-Translator: \n"
"Language-Team: Persian\n"
"Language: fa_IR\n"
@@ -20,12 +20,12 @@ msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
msgstr "ویدیو"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr "صوتی"
msgstr "صدا"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
@@ -34,38 +34,38 @@ msgstr "زیرنویس‌ها"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Show Subtitles"
msgstr ""
msgstr "نمایش زیرنویس"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
msgstr "گشودن…"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
msgstr "خموش"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
msgstr "بازنشانی"
#: src/lib/clapper-gtk/clapper-gtk-status.c:79
msgid "Unplayable Content"
msgstr ""
msgstr "محتوای غیر قابل پخش"
#. TRANSLATORS: Please do not try to translate "GStreamer" (it is a library name).
#: src/lib/clapper-gtk/clapper-gtk-status.c:89
#, c-format
msgid "Your GStreamer installation is missing a plugin: %s"
msgstr ""
msgstr "نصب جی‌استریمرتان افزایه‌ای را ندارد: %s"
#: src/lib/clapper-gtk/clapper-gtk-status.c:90
msgid "Missing Plugin"
msgstr ""
msgstr "افزایه ناموجود"
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr "نامشخص"
msgstr "تشخیص داده نشده"
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
@@ -73,9 +73,9 @@ msgstr "کانال‌ها"
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
msgstr "بدون رسانه"
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""
msgstr "عنوان ناشناس"

View File

@@ -0,0 +1,81 @@
msgid ""
msgstr ""
"Project-Id-Version: clapper\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-12 19:52+0200\n"
"PO-Revision-Date: 2024-07-09 18:54\n"
"Last-Translator: \n"
"Language-Team: Hindi\n"
"Language: hi_IN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Crowdin-Project: clapper\n"
"X-Crowdin-Project-ID: 473374\n"
"X-Crowdin-Language: hi\n"
"X-Crowdin-File: /master/src/lib/clapper-gtk/po/clapper-gtk.pot\n"
"X-Crowdin-File-ID: 46\n"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr "वीडियो"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr "ऑडियो"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr "उपशीर्षक"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Show Subtitles"
msgstr "उपशीर्षक दिखाएं"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr "खोलें…"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr "मूक"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr "रीसेट"
#: src/lib/clapper-gtk/clapper-gtk-status.c:79
msgid "Unplayable Content"
msgstr "चलाने योग्य सामग्री नहीं"
#. TRANSLATORS: Please do not try to translate "GStreamer" (it is a library name).
#: src/lib/clapper-gtk/clapper-gtk-status.c:89
#, c-format
msgid "Your GStreamer installation is missing a plugin: %s"
msgstr "आपके GStreamer स्थापना में एक प्लगइन गायब है: %s"
#: src/lib/clapper-gtk/clapper-gtk-status.c:90
msgid "Missing Plugin"
msgstr "गायब प्लगइन"
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr "अनिर्धारित"
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr "चैनल"
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr "मीडिया नहीं"
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr "अज्ञात शीर्षक"

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: clapper\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-12 19:52+0200\n"
"PO-Revision-Date: 2024-04-12 18:47\n"
"PO-Revision-Date: 2024-10-05 12:47\n"
"Last-Translator: \n"
"Language-Team: Norwegian\n"
"Language: no_NO\n"
@@ -20,62 +20,62 @@ msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
msgstr "Video"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
msgid "Audio"
msgstr ""
msgstr "Lyd"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:25
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:229
msgid "Subtitles"
msgstr ""
msgstr "Undertekstning"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Show Subtitles"
msgstr ""
msgstr "Vis undertekster"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
msgstr "Åpne…"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
msgstr "Demp"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
msgstr "Tilbakestill"
#: src/lib/clapper-gtk/clapper-gtk-status.c:79
msgid "Unplayable Content"
msgstr ""
msgstr "Uavspillelig innhold"
#. TRANSLATORS: Please do not try to translate "GStreamer" (it is a library name).
#: src/lib/clapper-gtk/clapper-gtk-status.c:89
#, c-format
msgid "Your GStreamer installation is missing a plugin: %s"
msgstr ""
msgstr "GStreamer-innstallasjon din mangler et tillegg: %s"
#: src/lib/clapper-gtk/clapper-gtk-status.c:90
msgid "Missing Plugin"
msgstr ""
msgstr "Manglende tillegg"
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
msgid "Undetermined"
msgstr ""
msgstr "Ubestemt"
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
msgid "Channels"
msgstr ""
msgstr "Kanaler"
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
msgstr "Ingen medier"
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""
msgstr "Ukjent tittel"

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: clapper\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-04-12 19:52+0200\n"
"PO-Revision-Date: 2024-04-12 18:47\n"
"PO-Revision-Date: 2024-08-03 08:27\n"
"Last-Translator: \n"
"Language-Team: Russian\n"
"Language: ru_RU\n"
@@ -20,7 +20,7 @@ msgstr ""
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:9
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:227
msgid "Video"
msgstr ""
msgstr "Видео"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:17
#: src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c:228
@@ -34,33 +34,33 @@ msgstr "Субтитры"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:28
msgid "Show Subtitles"
msgstr ""
msgstr "Показать субтитры"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:32
msgid "Open…"
msgstr ""
msgstr "Открыть…"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:75
msgid "Mute"
msgstr ""
msgstr "Без звука"
#: src/lib/clapper-gtk/ui/clapper-gtk-extra-menu-button.ui:119
msgid "Reset"
msgstr ""
msgstr "Сброс"
#: src/lib/clapper-gtk/clapper-gtk-status.c:79
msgid "Unplayable Content"
msgstr ""
msgstr "Невоспроизводимый контент"
#. TRANSLATORS: Please do not try to translate "GStreamer" (it is a library name).
#: src/lib/clapper-gtk/clapper-gtk-status.c:89
#, c-format
msgid "Your GStreamer installation is missing a plugin: %s"
msgstr ""
msgstr "В вашей установке GStreamer отсутствует модуль: %s"
#: src/lib/clapper-gtk/clapper-gtk-status.c:90
msgid "Missing Plugin"
msgstr ""
msgstr "Отсутствующий плагин"
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:82
#: src/lib/clapper-gtk/clapper-gtk-stream-check-button.c:126
@@ -73,9 +73,9 @@ msgstr "Каналы"
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:91
msgid "No media"
msgstr ""
msgstr "Нет медиафайлов"
#: src/lib/clapper-gtk/clapper-gtk-title-label.c:103
msgid "Unknown title"
msgstr ""
msgstr "Неизвестное название"

View File

@@ -47,6 +47,8 @@ void clapper_app_bus_post_refresh_timeline (ClapperAppBus *app_bus, GstObject *s
void clapper_app_bus_post_simple_signal (ClapperAppBus *app_bus, GstObject *src, guint signal_id);
void clapper_app_bus_post_object_desc_signal (ClapperAppBus *app_bus, GstObject *src, guint signal_id, GstObject *object, const gchar *desc);
void clapper_app_bus_post_desc_with_details_signal (ClapperAppBus *app_bus, GstObject *src, guint signal_id, const gchar *desc, const gchar *details);
void clapper_app_bus_post_error_signal (ClapperAppBus *app_bus, GstObject *src, guint signal_id, GError *error, const gchar *debug_info);

View File

@@ -43,6 +43,7 @@ enum
CLAPPER_APP_BUS_STRUCTURE_REFRESH_STREAMS,
CLAPPER_APP_BUS_STRUCTURE_REFRESH_TIMELINE,
CLAPPER_APP_BUS_STRUCTURE_SIMPLE_SIGNAL,
CLAPPER_APP_BUS_STRUCTURE_OBJECT_DESC_SIGNAL,
CLAPPER_APP_BUS_STRUCTURE_DESC_WITH_DETAILS_SIGNAL,
CLAPPER_APP_BUS_STRUCTURE_ERROR_SIGNAL
};
@@ -53,6 +54,7 @@ static ClapperBusQuark _structure_quarks[] = {
{"refresh-streams", 0},
{"refresh-timeline", 0},
{"simple-signal", 0},
{"object-desc-signal", 0},
{"desc-with-details-signal", 0},
{"error-signal", 0},
{NULL, 0}
@@ -63,6 +65,7 @@ enum
CLAPPER_APP_BUS_FIELD_UNKNOWN = 0,
CLAPPER_APP_BUS_FIELD_PSPEC,
CLAPPER_APP_BUS_FIELD_SIGNAL_ID,
CLAPPER_APP_BUS_FIELD_OBJECT,
CLAPPER_APP_BUS_FIELD_DESC,
CLAPPER_APP_BUS_FIELD_DETAILS,
CLAPPER_APP_BUS_FIELD_ERROR,
@@ -73,6 +76,7 @@ static ClapperBusQuark _field_quarks[] = {
{"unknown", 0},
{"pspec", 0},
{"signal-id", 0},
{"object", 0},
{"desc", 0},
{"details", 0},
{"error", 0},
@@ -177,6 +181,37 @@ _handle_simple_signal_msg (GstMessage *msg, const GstStructure *structure)
g_signal_emit (_MESSAGE_SRC_GOBJECT (msg), signal_id, 0);
}
void
clapper_app_bus_post_object_desc_signal (ClapperAppBus *self,
GstObject *src, guint signal_id,
GstObject *object, const gchar *desc)
{
GstStructure *structure = gst_structure_new_id (_STRUCTURE_QUARK (OBJECT_DESC_SIGNAL),
_FIELD_QUARK (SIGNAL_ID), G_TYPE_UINT, signal_id,
_FIELD_QUARK (OBJECT), GST_TYPE_OBJECT, object,
_FIELD_QUARK (DESC), G_TYPE_STRING, desc,
NULL);
gst_bus_post (GST_BUS_CAST (self), gst_message_new_application (src, structure));
}
static inline void
_handle_object_desc_signal_msg (GstMessage *msg, const GstStructure *structure)
{
guint signal_id = 0;
GstObject *object = NULL;
gchar *desc = NULL;
gst_structure_id_get (structure,
_FIELD_QUARK (SIGNAL_ID), G_TYPE_UINT, &signal_id,
_FIELD_QUARK (OBJECT), GST_TYPE_OBJECT, &object,
_FIELD_QUARK (DESC), G_TYPE_STRING, &desc,
NULL);
g_signal_emit (_MESSAGE_SRC_GOBJECT (msg), signal_id, 0, object, desc);
gst_object_unref (object);
g_free (desc);
}
void
clapper_app_bus_post_desc_with_details_signal (ClapperAppBus *self,
GstObject *src, guint signal_id,
@@ -253,6 +288,8 @@ clapper_app_bus_message_func (GstBus *bus, GstMessage *msg, gpointer user_data G
_handle_refresh_timeline_msg (msg, structure);
else if (quark == _STRUCTURE_QUARK (SIMPLE_SIGNAL))
_handle_simple_signal_msg (msg, structure);
else if (quark == _STRUCTURE_QUARK (OBJECT_DESC_SIGNAL))
_handle_object_desc_signal_msg (msg, structure);
else if (quark == _STRUCTURE_QUARK (ERROR_SIGNAL))
_handle_error_signal_msg (msg, structure);
else if (quark == _STRUCTURE_QUARK (DESC_WITH_DETAILS_SIGNAL))

View File

@@ -25,6 +25,8 @@
#include <glib.h>
#include <glib-object.h>
#include <clapper/clapper-visibility.h>
#include <clapper/clapper-stream.h>
G_BEGIN_DECLS
@@ -32,20 +34,28 @@ G_BEGIN_DECLS
#define CLAPPER_TYPE_AUDIO_STREAM (clapper_audio_stream_get_type())
#define CLAPPER_AUDIO_STREAM_CAST(obj) ((ClapperAudioStream *)(obj))
CLAPPER_API
G_DECLARE_FINAL_TYPE (ClapperAudioStream, clapper_audio_stream, CLAPPER, AUDIO_STREAM, ClapperStream)
CLAPPER_API
gchar * clapper_audio_stream_get_codec (ClapperAudioStream *stream);
CLAPPER_API
guint clapper_audio_stream_get_bitrate (ClapperAudioStream *stream);
CLAPPER_API
gchar * clapper_audio_stream_get_sample_format (ClapperAudioStream *stream);
CLAPPER_API
gint clapper_audio_stream_get_sample_rate (ClapperAudioStream *stream);
CLAPPER_API
gint clapper_audio_stream_get_channels (ClapperAudioStream *stream);
CLAPPER_API
gchar * clapper_audio_stream_get_lang_code (ClapperAudioStream *stream);
CLAPPER_API
gchar * clapper_audio_stream_get_lang_name (ClapperAudioStream *stream);
G_END_DECLS

View File

@@ -0,0 +1,42 @@
/* Clapper Playback Library
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#include <glib.h>
#include <glib-object.h>
G_BEGIN_DECLS
G_GNUC_INTERNAL
void clapper_enhancers_loader_initialize (void);
G_GNUC_INTERNAL
gboolean clapper_enhancers_loader_has_enhancers (GType iface_type);
G_GNUC_INTERNAL
gchar ** clapper_enhancers_loader_get_schemes (GType iface_type);
G_GNUC_INTERNAL
gboolean clapper_enhancers_loader_check (GType iface_type, const gchar *scheme, const gchar *host, const gchar **name);
G_GNUC_INTERNAL
GObject * clapper_enhancers_loader_create_enhancer_for_uri (GType iface_type, GUri *uri);
G_END_DECLS

View File

@@ -0,0 +1,388 @@
/* Clapper Playback Library
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include <gst/gst.h>
#include <libpeas.h>
#include "clapper-enhancers-loader-private.h"
#define ENHANCER_INTERFACES "X-Interfaces"
#define ENHANCER_SCHEMES "X-Schemes"
#define ENHANCER_HOSTS "X-Hosts"
#define ENHANCER_IFACE_NAME_FROM_TYPE(type) (g_type_name (type) + 7) // strip "Clapper" prefix
#define GST_CAT_DEFAULT clapper_enhancers_loader_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
static PeasEngine *_engine = NULL;
static GMutex load_lock;
/*
* clapper_enhancers_loader_initialize:
*
* Initializes #PeasEngine with directories that store enhancers.
*/
void
clapper_enhancers_loader_initialize (void)
{
const gchar *enhancers_path;
gchar **dir_paths;
guint i;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperenhancersloader", 0,
"Clapper Enhancer Loader");
enhancers_path = g_getenv ("CLAPPER_ENHANCERS_PATH");
if (!enhancers_path || *enhancers_path == '\0')
enhancers_path = CLAPPER_ENHANCERS_PATH;
GST_INFO ("Initializing Clapper enhancers with path: \"%s\"", enhancers_path);
_engine = peas_engine_new ();
/* Peas loaders are loaded lazily, so it should be fine
* to just enable them all here (even if not installed) */
peas_engine_enable_loader (_engine, "python");
peas_engine_enable_loader (_engine, "gjs");
dir_paths = g_strsplit (enhancers_path, G_SEARCHPATH_SEPARATOR_S, 0);
for (i = 0; dir_paths[i]; ++i)
peas_engine_add_search_path (_engine, dir_paths[i], NULL);
g_strfreev (dir_paths);
if (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= GST_LEVEL_INFO) {
GListModel *list = (GListModel *) _engine;
guint n_items = g_list_model_get_n_items (list);
for (i = 0; i < n_items; ++i) {
PeasPluginInfo *info = (PeasPluginInfo *) g_list_model_get_item (list, i);
GST_INFO ("Found enhancer: %s (%s)", peas_plugin_info_get_name (info),
peas_plugin_info_get_external_data (info, ENHANCER_INTERFACES));
g_object_unref (info);
}
GST_INFO ("Clapper enhancers initialized, found: %u", n_items);
}
}
static inline gboolean
_is_name_listed (const gchar *name, const gchar *list_str)
{
gsize name_len = strlen (name);
guint i = 0;
while (list_str[i] != '\0') {
guint end = i;
while (list_str[end] != ';' && list_str[end] != '\0')
++end;
/* Compare letters count until separator and prefix of whole string */
if (end - i == name_len && g_str_has_prefix (list_str + i, name))
return TRUE;
i = end;
/* Move to the next letter after ';' */
if (list_str[i] != '\0')
++i;
}
return FALSE;
}
/*
* clapper_enhancers_loader_get_info:
* @iface_type: an interface #GType
* @scheme: an URI scheme
* @host: (nullable): an URI host
*
* Returns: (transfer full) (nullable): available #PeasPluginInfo or %NULL.
*/
static PeasPluginInfo *
clapper_enhancers_loader_get_info (GType iface_type, const gchar *scheme, const gchar *host)
{
GListModel *list = (GListModel *) _engine;
PeasPluginInfo *found_info = NULL;
guint i, n_plugins = g_list_model_get_n_items (list);
const gchar *iface_name;
gboolean is_https;
if (n_plugins == 0) {
GST_INFO ("No Clapper enhancers found");
return NULL;
}
iface_name = ENHANCER_IFACE_NAME_FROM_TYPE (iface_type);
/* Strip common subdomains, so plugins do not
* have to list all combinations */
if (host) {
if (g_str_has_prefix (host, "www."))
host += 4;
else if (g_str_has_prefix (host, "m."))
host += 2;
}
GST_INFO ("Enhancer check, iface: %s, scheme: %s, host: %s",
iface_name, scheme, GST_STR_NULL (host));
is_https = (g_str_has_prefix (scheme, "http")
&& (scheme[4] == '\0' || (scheme[4] == 's' && scheme[5] == '\0')));
if (!host && is_https)
return NULL;
for (i = 0; i < n_plugins; ++i) {
PeasPluginInfo *info = (PeasPluginInfo *) g_list_model_get_item (list, i);
const gchar *iface_names, *schemes, *hosts;
if (!(iface_names = peas_plugin_info_get_external_data (info, ENHANCER_INTERFACES))) {
GST_DEBUG ("Skipping enhancer without interfaces: %s", peas_plugin_info_get_name (info));
g_object_unref (info);
continue;
}
if (!_is_name_listed (iface_name, iface_names)) {
g_object_unref (info);
continue;
}
if (!(schemes = peas_plugin_info_get_external_data (info, ENHANCER_SCHEMES))) {
GST_DEBUG ("Skipping enhancer without schemes: %s", peas_plugin_info_get_name (info));
g_object_unref (info);
continue;
}
if (!_is_name_listed (scheme, schemes)) {
g_object_unref (info);
continue;
}
if (is_https) {
if (!(hosts = peas_plugin_info_get_external_data (info, ENHANCER_HOSTS))) {
GST_DEBUG ("Skipping enhancer without hosts: %s", peas_plugin_info_get_name (info));
g_object_unref (info);
continue;
}
if (!_is_name_listed (host, hosts)) {
g_object_unref (info);
continue;
}
}
found_info = info;
break;
}
return found_info;
}
/*
* clapper_enhancers_loader_has_enhancers:
* @iface_type: an interface #GType
*
* Check if any enhancer implementing given interface type is available.
*
* Returns: whether any valid enhancer was found.
*/
gboolean
clapper_enhancers_loader_has_enhancers (GType iface_type)
{
GListModel *list = (GListModel *) _engine;
const gchar *iface_name = ENHANCER_IFACE_NAME_FROM_TYPE (iface_type);
guint i, n_plugins;
GST_DEBUG ("Checking for any enhancers of type: \"%s\"", iface_name);
n_plugins = g_list_model_get_n_items (list);
for (i = 0; i < n_plugins; ++i) {
PeasPluginInfo *info = (PeasPluginInfo *) g_list_model_get_item (list, i);
const gchar *iface_names;
if (!(iface_names = peas_plugin_info_get_external_data (info, ENHANCER_INTERFACES))) {
g_object_unref (info);
continue;
}
if (!_is_name_listed (iface_name, iface_names)) {
g_object_unref (info);
continue;
}
/* Additional validation */
if (!peas_plugin_info_get_external_data (info, ENHANCER_SCHEMES)
|| !peas_plugin_info_get_external_data (info, ENHANCER_HOSTS)) {
g_object_unref (info);
continue;
}
GST_DEBUG ("Found valid enhancers of type: \"%s\"", iface_name);
g_object_unref (info);
return TRUE;
}
GST_DEBUG ("No available enhancers of type: \"%s\"", iface_name);
return FALSE;
}
/*
* clapper_enhancers_loader_get_schemes:
* @iface_type: an interface #GType
*
* Get all supported schemes for a given interface type.
* The returned array consists of unique strings (no duplicates).
*
* Returns: (transfer full): all supported schemes by enhancers of type.
*/
gchar **
clapper_enhancers_loader_get_schemes (GType iface_type)
{
GListModel *list = (GListModel *) _engine;
GSList *found_schemes = NULL, *fs;
const gchar *iface_name = ENHANCER_IFACE_NAME_FROM_TYPE (iface_type);
gchar **schemes_strv;
guint i, n_plugins, n_schemes;
GST_DEBUG ("Checking supported URI schemes for \"%s\"", iface_name);
n_plugins = g_list_model_get_n_items (list);
for (i = 0; i < n_plugins; ++i) {
PeasPluginInfo *info = (PeasPluginInfo *) g_list_model_get_item (list, i);
const gchar *iface_names, *schemes;
gchar **tmp_strv;
gint j;
if (!(iface_names = peas_plugin_info_get_external_data (info, ENHANCER_INTERFACES))) {
g_object_unref (info);
continue;
}
if (!_is_name_listed (iface_name, iface_names)) {
g_object_unref (info);
continue;
}
if (!(schemes = peas_plugin_info_get_external_data (info, ENHANCER_SCHEMES))) {
g_object_unref (info);
continue;
}
tmp_strv = g_strsplit (schemes, ";", 0);
for (j = 0; tmp_strv[j]; ++j) {
const gchar *scheme = tmp_strv[j];
if (!found_schemes || !g_slist_find_custom (found_schemes,
scheme, (GCompareFunc) strcmp)) {
found_schemes = g_slist_append (found_schemes, g_strdup (scheme));
GST_INFO ("Found supported URI scheme: %s", scheme);
}
}
g_strfreev (tmp_strv);
g_object_unref (info);
}
n_schemes = g_slist_length (found_schemes);
schemes_strv = g_new0 (gchar *, n_schemes + 1);
fs = found_schemes;
for (i = 0; i < n_schemes; ++i) {
schemes_strv[i] = fs->data;
fs = fs->next;
}
GST_DEBUG ("Total found URI schemes: %u", n_schemes);
/* Since string pointers were taken,
* free list without content */
g_slist_free (found_schemes);
return schemes_strv;
}
/*
* clapper_enhancers_loader_check:
* @iface_type: a requested #GType
* @scheme: an URI scheme
* @host: (nullable): an URI host
* @name: (out) (optional) (transfer none): return location for found enhancer name
*
* Checks if any enhancer can handle @uri without initializing loader
* or creating enhancer instance, thus this can be used freely from any thread.
*
* Returns: whether enhancer for given scheme and host is available.
*/
gboolean
clapper_enhancers_loader_check (GType iface_type,
const gchar *scheme, const gchar *host, const gchar **name)
{
PeasPluginInfo *info;
if ((info = clapper_enhancers_loader_get_info (iface_type, scheme, host))) {
if (name)
*name = peas_plugin_info_get_name (info);
g_object_unref (info);
return TRUE;
}
return FALSE;
}
/*
* clapper_enhancers_loader_create_enhancer_for_uri:
* @iface_type: a requested #GType
* @uri: a #GUri
*
* Creates a new enhancer object for given URI.
*
* Enhancer should only be created and used within single thread.
*
* Returns: (transfer full) (nullable): a new enhancer instance.
*/
GObject *
clapper_enhancers_loader_create_enhancer_for_uri (GType iface_type, GUri *uri)
{
GObject *enhancer = NULL;
PeasPluginInfo *info;
const gchar *scheme = g_uri_get_scheme (uri);
const gchar *host = g_uri_get_host (uri);
if ((info = clapper_enhancers_loader_get_info (iface_type, scheme, host))) {
g_mutex_lock (&load_lock);
if (!peas_plugin_info_is_loaded (info) && !peas_engine_load_plugin (_engine, info)) {
GST_ERROR ("Could not load enhancer: %s", peas_plugin_info_get_name (info));
} else if (!peas_engine_provides_extension (_engine, info, iface_type)) {
GST_ERROR ("No \"%s\" enhancer in plugin: %s", ENHANCER_IFACE_NAME_FROM_TYPE (iface_type),
peas_plugin_info_get_name (info));
} else {
enhancer = peas_engine_create_extension (_engine, info, iface_type, NULL);
}
g_mutex_unlock (&load_lock);
g_object_unref (info);
}
return enhancer;
}

View File

@@ -0,0 +1,33 @@
/* Clapper Playback Library
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#include <glib.h>
#include <gio/gio.h>
#include "clapper-extractable.h"
#include "clapper-harvest.h"
G_BEGIN_DECLS
G_GNUC_INTERNAL
gboolean clapper_extractable_extract (ClapperExtractable *extractable, GUri *uri, ClapperHarvest *harvest, GCancellable *cancellable, GError **error);
G_END_DECLS

View File

@@ -0,0 +1,61 @@
/* Clapper Playback Library
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* ClapperExtractable:
*
* An interface for creating enhancers that resolve given URI into something playable.
*
* Since: 0.8
*/
#include <gst/gst.h>
#include "clapper-extractable-private.h"
#include "clapper-harvest-private.h"
G_DEFINE_INTERFACE (ClapperExtractable, clapper_extractable, G_TYPE_OBJECT);
static gboolean
clapper_extractable_default_extract (ClapperExtractable *self, GUri *uri,
ClapperHarvest *harvest, GCancellable *cancellable, GError **error)
{
if (*error == NULL) {
g_set_error (error, GST_CORE_ERROR,
GST_CORE_ERROR_NOT_IMPLEMENTED,
"Extractable object did not implement extract function");
}
return FALSE;
}
static void
clapper_extractable_default_init (ClapperExtractableInterface *iface)
{
iface->extract = clapper_extractable_default_extract;
}
gboolean
clapper_extractable_extract (ClapperExtractable *self, GUri *uri,
ClapperHarvest *harvest, GCancellable *cancellable, GError **error)
{
ClapperExtractableInterface *iface = CLAPPER_EXTRACTABLE_GET_IFACE (self);
return iface->extract (self, uri, harvest, cancellable, error);
}

View File

@@ -0,0 +1,69 @@
/* Clapper Playback Library
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#if !defined(__CLAPPER_INSIDE__) && !defined(CLAPPER_COMPILATION)
#error "Only <clapper/clapper.h> can be included directly."
#endif
#include <glib.h>
#include <glib-object.h>
#include <gio/gio.h>
#include <clapper/clapper-visibility.h>
#include <clapper/clapper-harvest.h>
G_BEGIN_DECLS
#define CLAPPER_TYPE_EXTRACTABLE (clapper_extractable_get_type())
#define CLAPPER_EXTRACTABLE_CAST(obj) ((ClapperExtractable *)(obj))
CLAPPER_API
G_DECLARE_INTERFACE (ClapperExtractable, clapper_extractable, CLAPPER, EXTRACTABLE, GObject)
/**
* ClapperExtractableInterface:
* @parent_iface: The parent interface structure.
* @extract: Extract data and fill harvest.
*/
struct _ClapperExtractableInterface
{
GTypeInterface parent_iface;
/**
* ClapperExtractableInterface::extract:
* @extractable: a #ClapperExtractable
* @uri: a #GUri
* @cancellable: a #GCancellable object
* @error: a #GError
*
* Extract data and fill harvest.
*
* Returns: whether extraction was successful.
*
* Since: 0.8
*/
gboolean (* extract) (ClapperExtractable *extractable, GUri *uri, ClapperHarvest *harvest, GCancellable *cancellable, GError **error);
/*< private >*/
gpointer padding[8];
};
G_END_DECLS

View File

@@ -27,6 +27,8 @@
#include <glib-object.h>
#include <gio/gio.h>
#include <gst/gst.h>
#include <clapper/clapper-visibility.h>
#include <clapper/clapper-media-item.h>
#include <clapper/clapper-enums.h>
@@ -35,6 +37,7 @@ G_BEGIN_DECLS
#define CLAPPER_TYPE_FEATURE (clapper_feature_get_type())
#define CLAPPER_FEATURE_CAST(obj) ((ClapperFeature *)(obj))
CLAPPER_API
G_DECLARE_DERIVABLE_TYPE (ClapperFeature, clapper_feature, CLAPPER, FEATURE, GstObject)
/**

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#if !defined(__CLAPPER_INSIDE__) && !defined(CLAPPER_COMPILATION)
#error "Only <clapper/clapper.h> can be included directly."
#endif
/**
* CLAPPER_WITH_ENHANCERS_LOADER:
*
* Check if Clapper was compiled with Enhancers Loader functionality.
*
* Alternatively, apps before compiling can also check whether `pkgconfig`
* variable named `functionalities` contains `enhancers-loader` string.
*
* Since: 0.8
*/
#define CLAPPER_WITH_ENHANCERS_LOADER (@CLAPPER_WITH_ENHANCERS_LOADER@)

View File

@@ -0,0 +1,35 @@
/* Clapper Playback Library
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#include <glib.h>
#include <gst/tag/tag.h>
#include "clapper-harvest.h"
G_BEGIN_DECLS
G_GNUC_INTERNAL
ClapperHarvest * clapper_harvest_new (void);
G_GNUC_INTERNAL
gboolean clapper_harvest_unpack (ClapperHarvest *harvest, GstBuffer **buffer, gsize *buf_size, GstCaps **caps, GstTagList **tags, GstToc **toc, GstStructure **headers);
G_END_DECLS

View File

@@ -0,0 +1,450 @@
/* Clapper Playback Library
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* ClapperHarvest:
*
* An object storing data from enhancers that implement [iface@Clapper.Extractable] interface.
*
* Since: 0.8
*/
/*
* NOTE: We cannot simply expose GstMiniObjects for
* implementations to assemble TagList/Toc themselves, see:
* https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/2867
*/
#include "clapper-harvest-private.h"
#define GST_CAT_DEFAULT clapper_harvest_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperHarvest
{
GstObject parent;
GstCaps *caps;
GstBuffer *buffer;
gsize buf_size;
GstTagList *tags;
GstToc *toc;
GstStructure *headers;
guint16 n_chapters;
guint16 n_tracks;
};
#define parent_class clapper_harvest_parent_class
G_DEFINE_TYPE (ClapperHarvest, clapper_harvest, GST_TYPE_OBJECT);
static inline void
_ensure_tags (ClapperHarvest *self)
{
if (!self->tags) {
self->tags = gst_tag_list_new_empty ();
gst_tag_list_set_scope (self->tags, GST_TAG_SCOPE_GLOBAL);
}
}
static inline void
_ensure_toc (ClapperHarvest *self)
{
if (!self->toc)
self->toc = gst_toc_new (GST_TOC_SCOPE_GLOBAL);
}
static inline void
_ensure_headers (ClapperHarvest *self)
{
if (!self->headers)
self->headers = gst_structure_new_empty ("request-headers");
}
ClapperHarvest *
clapper_harvest_new (void)
{
ClapperHarvest *harvest;
harvest = g_object_new (CLAPPER_TYPE_HARVEST, NULL);
gst_object_ref_sink (harvest);
return harvest;
}
gboolean
clapper_harvest_unpack (ClapperHarvest *self,
GstBuffer **buffer, gsize *buf_size, GstCaps **caps,
GstTagList **tags, GstToc **toc, GstStructure **headers)
{
/* Not filled or already unpacked */
if (!self->buffer)
return FALSE;
*buffer = self->buffer;
self->buffer = NULL;
*buf_size = self->buf_size;
self->buf_size = 0;
*caps = self->caps;
self->caps = NULL;
*tags = self->tags;
self->tags = NULL;
*toc = self->toc;
self->toc = NULL;
*headers = self->headers;
self->headers = NULL;
return TRUE;
}
/**
* clapper_harvest_fill:
* @harvest: a #ClapperHarvest
* @media_type: media mime type
* @data: (array length=size) (element-type guint8) (transfer full): data to fill @harvest
* @size: allocated size of @data
*
* Fill harvest with extracted data. It can be anything that GStreamer
* can parse and play such as single URI or a streaming manifest.
*
* Calling again this function will replace previously filled content.
*
* Commonly used media types are:
*
* * `application/dash+xml`
*
* * `application/x-hls`
*
* * `text/uri-list`
*
* Returns: %TRUE when filled successfully, %FALSE if taken data was empty.
*
* Since: 0.8
*/
gboolean
clapper_harvest_fill (ClapperHarvest *self, const gchar *media_type, gpointer data, gsize size)
{
g_return_val_if_fail (CLAPPER_IS_HARVEST (self), FALSE);
g_return_val_if_fail (media_type != NULL, FALSE);
g_return_val_if_fail (data != NULL, FALSE);
if (!data || size == 0) {
g_free (data);
return FALSE;
}
if (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >= GST_LEVEL_DEBUG) {
gboolean is_printable = (strcmp (media_type, "application/dash+xml") == 0)
|| (strcmp (media_type, "application/x-hls") == 0)
|| (strcmp (media_type, "text/uri-list") == 0);
if (is_printable) {
gchar *data_str;
data_str = g_new0 (gchar, size + 1);
memcpy (data_str, data, size);
GST_DEBUG_OBJECT (self, "Filled with data:\n%s", data_str);
g_free (data_str);
}
}
gst_clear_buffer (&self->buffer);
gst_clear_caps (&self->caps);
self->buffer = gst_buffer_new_wrapped (data, size);
self->buf_size = size;
self->caps = gst_caps_new_simple (media_type,
"source", G_TYPE_STRING, "clapper-harvest", NULL);
return TRUE;
}
/**
* clapper_harvest_fill_with_text:
* @harvest: a #ClapperHarvest
* @media_type: media mime type
* @text: (transfer full): data to fill @harvest as %NULL terminated string
*
* A convenience method to fill @harvest using a %NULL terminated string.
*
* For more info, see [method@Clapper.Harvest.fill] documentation.
*
* Returns: %TRUE when filled successfully, %FALSE if taken data was empty.
*
* Since: 0.8
*/
gboolean
clapper_harvest_fill_with_text (ClapperHarvest *self, const gchar *media_type, gchar *text)
{
g_return_val_if_fail (text != NULL, FALSE);
return clapper_harvest_fill (self, media_type, text, strlen (text));
}
/**
* clapper_harvest_fill_with_bytes:
* @harvest: a #ClapperHarvest
* @media_type: media mime type
* @bytes: (transfer full): a #GBytes to fill @harvest
*
* A convenience method to fill @harvest with data from #GBytes.
*
* For more info, see [method@Clapper.Harvest.fill] documentation.
*
* Returns: %TRUE when filled successfully, %FALSE if taken data was empty.
*
* Since: 0.8
*/
gboolean
clapper_harvest_fill_with_bytes (ClapperHarvest *self, const gchar *media_type, GBytes *bytes)
{
gpointer data;
gsize size = 0;
g_return_val_if_fail (bytes != NULL, FALSE);
data = g_bytes_unref_to_data (bytes, &size);
return clapper_harvest_fill (self, media_type, data, size);
}
/**
* clapper_harvest_tags_add:
* @harvest: a #ClapperHarvest
* @tag: a name of tag to set
* @...: %NULL-terminated list of arguments
*
* Append one or more tags into the tag list.
*
* Variable arguments should be in the form of tag and value pairs.
*
* Since: 0.8
*/
void
clapper_harvest_tags_add (ClapperHarvest *self, const gchar *tag, ...)
{
va_list args;
g_return_if_fail (CLAPPER_IS_HARVEST (self));
g_return_if_fail (tag != NULL);
_ensure_tags (self);
va_start (args, tag);
gst_tag_list_add_valist (self->tags, GST_TAG_MERGE_APPEND, tag, args);
va_end (args);
}
/**
* clapper_harvest_tags_add_value: (rename-to clapper_harvest_tags_add)
* @harvest: a #ClapperHarvest
* @tag: a name of tag to set
* @value: a #GValue of tag
*
* Append another tag into the tag list using #GValue.
*
* Since: 0.8
*/
void
clapper_harvest_tags_add_value (ClapperHarvest *self, const gchar *tag, const GValue *value)
{
g_return_if_fail (CLAPPER_IS_HARVEST (self));
g_return_if_fail (tag != NULL);
g_return_if_fail (G_IS_VALUE (value));
_ensure_tags (self);
gst_tag_list_add_value (self->tags, GST_TAG_MERGE_APPEND, tag, value);
}
/**
* clapper_harvest_toc_add:
* @harvest: a #ClapperHarvest
* @type: a #GstTocEntryType
* @title: an entry title
* @start: entry start time in seconds
* @end: entry end time in seconds or -1 if none
*
* Append a chapter or track name into table of contents.
*
* Since: 0.8
*/
void
clapper_harvest_toc_add (ClapperHarvest *self, GstTocEntryType type,
const gchar *title, gdouble start, gdouble end)
{
GstTocEntry *entry, *subentry;
GstClockTime start_time, end_time;
gchar edition[3]; // 2 + 1
gchar id[14]; // 7 + 1 + 5 + 1
const gchar *id_prefix;
guint16 nth_entry;
g_return_if_fail (CLAPPER_IS_HARVEST (self));
g_return_if_fail (type == GST_TOC_ENTRY_TYPE_CHAPTER || type == GST_TOC_ENTRY_TYPE_TRACK);
g_return_if_fail (title != NULL);
g_return_if_fail (start >= 0 && end >= start);
switch (type) {
case GST_TOC_ENTRY_TYPE_CHAPTER:
id_prefix = "chapter";
nth_entry = ++(self->n_chapters);
break;
case GST_TOC_ENTRY_TYPE_TRACK:
id_prefix = "track";
nth_entry = ++(self->n_tracks);
break;
default:
g_assert_not_reached ();
return;
}
start_time = start * GST_SECOND;
end_time = (end >= 0) ? end * GST_SECOND : GST_CLOCK_TIME_NONE;
g_snprintf (edition, sizeof (edition), "0%i", type);
g_snprintf (id, sizeof (id), "%s.%" G_GUINT16_FORMAT, id_prefix, nth_entry);
GST_DEBUG_OBJECT (self, "Inserting TOC %s: \"%s\""
" (%" G_GUINT64_FORMAT "-%" G_GUINT64_FORMAT ")",
id, title, start_time, end_time);
subentry = gst_toc_entry_new (type, id);
gst_toc_entry_set_tags (subentry, gst_tag_list_new (GST_TAG_TITLE, title, NULL));
gst_toc_entry_set_start_stop_times (subentry, start_time, end_time);
_ensure_toc (self);
find_entry:
if (!(entry = gst_toc_find_entry (self->toc, edition))) {
GstTocEntry *toc_entry;
toc_entry = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, edition);
gst_toc_entry_set_start_stop_times (toc_entry, 0, GST_CLOCK_TIME_NONE);
gst_toc_append_entry (self->toc, toc_entry); // transfer full and must be writable
goto find_entry;
}
gst_toc_entry_append_sub_entry (entry, subentry);
}
/**
* clapper_harvest_headers_set:
* @harvest: a #ClapperHarvest
* @key: a header name
* @...: %NULL-terminated list of arguments
*
* Set one or more request headers named with @key to specified `value`.
*
* Arguments should be %NULL terminated list of `key+value` string pairs.
*
* Setting again the same key will update its value to the new one.
*
* Since: 0.8
*/
void
clapper_harvest_headers_set (ClapperHarvest *self, const gchar *key, ...)
{
va_list args;
g_return_if_fail (CLAPPER_IS_HARVEST (self));
g_return_if_fail (key != NULL);
_ensure_headers (self);
va_start (args, key);
while (key != NULL) {
const gchar *val = va_arg (args, const gchar *);
GST_DEBUG_OBJECT (self, "Set header, \"%s\": \"%s\"", key, val);
gst_structure_set (self->headers, key, G_TYPE_STRING, val, NULL);
key = va_arg (args, const gchar *);
}
va_end (args);
}
/**
* clapper_harvest_headers_set_value: (rename-to clapper_harvest_headers_set)
* @harvest: a #ClapperHarvest
* @key: a header name
* @value: a string #GValue of header
*
* Set another header in the request headers list using #GValue.
*
* Setting again the same key will update its value to the new one.
*
* Since: 0.8
*/
void
clapper_harvest_headers_set_value (ClapperHarvest *self, const gchar *key, const GValue *value)
{
g_return_if_fail (CLAPPER_IS_HARVEST (self));
g_return_if_fail (key != NULL);
g_return_if_fail (G_IS_VALUE (value) && G_VALUE_HOLDS_STRING (value));
_ensure_headers (self);
GST_DEBUG_OBJECT (self, "Set header, \"%s\": \"%s\"", key, g_value_get_string (value));
gst_structure_set_value (self->headers, key, value);
}
static void
clapper_harvest_init (ClapperHarvest *self)
{
}
static void
clapper_harvest_finalize (GObject *object)
{
ClapperHarvest *self = CLAPPER_HARVEST_CAST (object);
GST_TRACE_OBJECT (self, "Finalize");
gst_clear_caps (&self->caps);
gst_clear_buffer (&self->buffer);
if (self->tags)
gst_tag_list_unref (self->tags);
if (self->toc)
gst_toc_unref (self->toc);
if (self->headers)
gst_structure_free (self->headers);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
clapper_harvest_class_init (ClapperHarvestClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperharvest", 0,
"Clapper Harvest");
gobject_class->finalize = clapper_harvest_finalize;
}

View File

@@ -0,0 +1,64 @@
/* Clapper Playback Library
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#if !defined(__CLAPPER_INSIDE__) && !defined(CLAPPER_COMPILATION)
#error "Only <clapper/clapper.h> can be included directly."
#endif
#include <glib.h>
#include <glib-object.h>
#include <gst/gst.h>
#include <gst/tag/tag.h>
#include <clapper/clapper-visibility.h>
G_BEGIN_DECLS
#define CLAPPER_TYPE_HARVEST (clapper_harvest_get_type())
#define CLAPPER_HARVEST_CAST(obj) ((ClapperHarvest *)(obj))
G_DECLARE_FINAL_TYPE (ClapperHarvest, clapper_harvest, CLAPPER, HARVEST, GstObject)
CLAPPER_API
gboolean clapper_harvest_fill (ClapperHarvest *harvest, const gchar *media_type, gpointer data, gsize size);
CLAPPER_API
gboolean clapper_harvest_fill_with_text (ClapperHarvest *harvest, const gchar *media_type, gchar *text);
CLAPPER_API
gboolean clapper_harvest_fill_with_bytes (ClapperHarvest *harvest, const gchar *media_type, GBytes *bytes);
CLAPPER_API
void clapper_harvest_tags_add (ClapperHarvest *harvest, const gchar *tag, ...) G_GNUC_NULL_TERMINATED;
CLAPPER_API
void clapper_harvest_tags_add_value (ClapperHarvest *harvest, const gchar *tag, const GValue *value);
CLAPPER_API
void clapper_harvest_toc_add (ClapperHarvest *harvest, GstTocEntryType type, const gchar *title, gdouble start, gdouble end);
CLAPPER_API
void clapper_harvest_headers_set (ClapperHarvest *harvest, const gchar *key, ...) G_GNUC_NULL_TERMINATED;
CLAPPER_API
void clapper_harvest_headers_set_value (ClapperHarvest *harvest, const gchar *key, const GValue *value);
G_END_DECLS

View File

@@ -26,6 +26,8 @@
#include <glib.h>
#include <glib-object.h>
#include <gst/gst.h>
#include <clapper/clapper-visibility.h>
#include <clapper/clapper-enums.h>
G_BEGIN_DECLS
@@ -36,6 +38,7 @@ G_BEGIN_DECLS
/* NOTE: #ClapperMarker are immutable objects that cannot be derived,
* otherwise #ClapperFeaturesManager would not be able to announce media
* item changed caused by changes within them */
CLAPPER_API
G_DECLARE_FINAL_TYPE (ClapperMarker, clapper_marker, CLAPPER, MARKER, GstObject)
/**
@@ -45,14 +48,19 @@ G_DECLARE_FINAL_TYPE (ClapperMarker, clapper_marker, CLAPPER, MARKER, GstObject)
*/
#define CLAPPER_MARKER_NO_END ((gdouble) -1) // Needs a cast from int, otherwise GIR is generated incorrectly
CLAPPER_API
ClapperMarker * clapper_marker_new (ClapperMarkerType marker_type, const gchar *title, gdouble start, gdouble end);
CLAPPER_API
ClapperMarkerType clapper_marker_get_marker_type (ClapperMarker *marker);
CLAPPER_API
const gchar * clapper_marker_get_title (ClapperMarker *marker);
CLAPPER_API
gdouble clapper_marker_get_start (ClapperMarker *marker);
CLAPPER_API
gdouble clapper_marker_get_end (ClapperMarker *marker);
G_END_DECLS

View File

@@ -37,6 +37,12 @@ void clapper_media_item_update_from_discoverer_info (ClapperMediaItem *self, Gst
G_GNUC_INTERNAL
gboolean clapper_media_item_set_duration (ClapperMediaItem *item, gdouble duration, ClapperAppBus *app_bus);
G_GNUC_INTERNAL
void clapper_media_item_set_cache_location (ClapperMediaItem *item, const gchar *location);
G_GNUC_INTERNAL
const gchar * clapper_media_item_get_playback_uri (ClapperMediaItem *item);
G_GNUC_INTERNAL
void clapper_media_item_set_used (ClapperMediaItem *item, gboolean used);

View File

@@ -50,6 +50,8 @@ struct _ClapperMediaItem
gchar *container_format;
gdouble duration;
gchar *cache_uri;
/* For shuffle */
gboolean used;
};
@@ -60,6 +62,7 @@ enum
PROP_ID,
PROP_URI,
PROP_SUBURI,
PROP_CACHE_LOCATION,
PROP_TITLE,
PROP_CONTAINER_FORMAT,
PROP_DURATION,
@@ -141,6 +144,35 @@ clapper_media_item_new_from_file (GFile *file)
return item;
}
/**
* clapper_media_item_new_cached:
* @uri: a media URI
* @location: (type filename) (nullable): a path to downloaded file
*
* Same as [ctor@Clapper.MediaItem.new], but allows to provide
* a location of a cache file where particular media at @uri
* is supposed to be found.
*
* File at @location existence will be checked upon starting playback
* of created item. If cache file is not found, media item @uri will be
* used as fallback. In this case when [property@Clapper.Player:download-enabled]
* is set to %TRUE, item will be downloaded and cached again if possible.
*
* Returns: (transfer full): a new #ClapperMediaItem.
*
* Since: 0.8
*/
ClapperMediaItem *
clapper_media_item_new_cached (const gchar *uri, const gchar *location)
{
ClapperMediaItem *item = clapper_media_item_new (uri);
if (location && G_LIKELY (item != NULL))
clapper_media_item_set_cache_location (item, location);
return item;
}
/**
* clapper_media_item_get_id:
* @item: a #ClapperMediaItem
@@ -450,6 +482,43 @@ clapper_media_item_update_from_discoverer_info (ClapperMediaItem *self, GstDisco
gst_object_unref (player);
}
/* XXX: Must be set from player thread or upon construction */
void
clapper_media_item_set_cache_location (ClapperMediaItem *self, const gchar *location)
{
g_clear_pointer (&self->cache_uri, g_free);
if (location)
self->cache_uri = g_filename_to_uri (location, NULL, NULL);
GST_DEBUG_OBJECT (self, "Set cache URI: \"%s\"",
GST_STR_NULL (self->cache_uri));
}
/* XXX: Can only be read from player thread.
* Returns cache URI if available, item URI otherwise. */
inline const gchar *
clapper_media_item_get_playback_uri (ClapperMediaItem *self)
{
if (self->cache_uri) {
GFile *file = g_file_new_for_uri (self->cache_uri);
gboolean exists;
/* It is an app error if it removes files in non-stopped state,
* and this function is only called when starting playback */
exists = g_file_query_exists (file, NULL);
g_object_unref (file);
if (exists)
return self->cache_uri;
/* Do not test file existence next time */
clapper_media_item_set_cache_location (self, NULL);
}
return self->uri;
}
void
clapper_media_item_set_used (ClapperMediaItem *self, gboolean used)
{
@@ -505,6 +574,8 @@ clapper_media_item_finalize (GObject *object)
gst_object_unparent (GST_OBJECT_CAST (self->timeline));
gst_object_unref (self->timeline);
g_free (self->cache_uri);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@@ -521,6 +592,9 @@ clapper_media_item_set_property (GObject *object, guint prop_id,
case PROP_SUBURI:
clapper_media_item_set_suburi (self, g_value_get_string (value));
break;
case PROP_CACHE_LOCATION:
clapper_media_item_set_cache_location (self, g_value_get_string (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -601,6 +675,17 @@ clapper_media_item_class_init (ClapperMediaItemClass *klass)
NULL, NULL, NULL,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperMediaItem:cache-location:
*
* Media downloaded cache file location.
*
* Since: 0.8
*/
param_specs[PROP_CACHE_LOCATION] = g_param_spec_string ("cache-location",
NULL, NULL, NULL,
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperMediaItem:title:
*

View File

@@ -28,6 +28,7 @@
#include <gio/gio.h>
#include <gst/gst.h>
#include <clapper/clapper-visibility.h>
#include <clapper/clapper-timeline.h>
G_BEGIN_DECLS
@@ -35,26 +36,40 @@ G_BEGIN_DECLS
#define CLAPPER_TYPE_MEDIA_ITEM (clapper_media_item_get_type())
#define CLAPPER_MEDIA_ITEM_CAST(obj) ((ClapperMediaItem *)(obj))
CLAPPER_API
G_DECLARE_FINAL_TYPE (ClapperMediaItem, clapper_media_item, CLAPPER, MEDIA_ITEM, GstObject)
CLAPPER_API
ClapperMediaItem * clapper_media_item_new (const gchar *uri);
CLAPPER_API
ClapperMediaItem * clapper_media_item_new_from_file (GFile *file);
CLAPPER_API
ClapperMediaItem * clapper_media_item_new_cached (const gchar *uri, const gchar *location);
CLAPPER_API
guint clapper_media_item_get_id (ClapperMediaItem *item);
CLAPPER_API
const gchar * clapper_media_item_get_uri (ClapperMediaItem *item);
CLAPPER_API
void clapper_media_item_set_suburi (ClapperMediaItem *item, const gchar *suburi);
CLAPPER_API
gchar * clapper_media_item_get_suburi (ClapperMediaItem *item);
CLAPPER_API
gchar * clapper_media_item_get_title (ClapperMediaItem *item);
CLAPPER_API
gchar * clapper_media_item_get_container_format (ClapperMediaItem *item);
CLAPPER_API
gdouble clapper_media_item_get_duration (ClapperMediaItem *item);
CLAPPER_API
ClapperTimeline * clapper_media_item_get_timeline (ClapperMediaItem *item);
G_END_DECLS

View File

@@ -30,6 +30,12 @@
#include "clapper-stream-private.h"
#include "clapper-stream-list-private.h"
#include "clapper-functionalities-availability.h"
#if CLAPPER_WITH_ENHANCERS_LOADER
#include "gst/clapper-enhancer-src-private.h"
#endif
#define GST_CAT_DEFAULT clapper_playbin_bus_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
@@ -827,6 +833,40 @@ _handle_element_msg (GstMessage *msg, ClapperPlayer *player)
g_free (name);
g_free (details);
} else if (gst_message_has_name (msg, "GstCacheDownloadComplete")) {
ClapperMediaItem *downloaded_item = NULL;
const GstStructure *structure;
const gchar *location;
guint signal_id;
GST_OBJECT_LOCK (player);
/* Short video might be fully downloaded before playback starts */
if (player->pending_item)
downloaded_item = gst_object_ref (player->pending_item);
else if (player->played_item)
downloaded_item = gst_object_ref (player->played_item);
GST_OBJECT_UNLOCK (player);
if (G_UNLIKELY (downloaded_item == NULL)) {
GST_WARNING_OBJECT (player, "Download completed without media item set");
return;
}
structure = gst_message_get_structure (msg);
location = gst_structure_get_string (structure, "location");
signal_id = g_signal_lookup ("download-complete", CLAPPER_TYPE_PLAYER);
GST_INFO_OBJECT (player, "Download of %" GST_PTR_FORMAT
" complete: %s", downloaded_item, location);
clapper_media_item_set_cache_location (downloaded_item, location);
clapper_app_bus_post_object_desc_signal (player->app_bus,
GST_OBJECT_CAST (player), signal_id,
GST_OBJECT_CAST (downloaded_item), location);
gst_object_unref (downloaded_item);
}
}
@@ -835,6 +875,7 @@ _handle_tag_msg (GstMessage *msg, ClapperPlayer *player)
{
GstObject *src = GST_MESSAGE_SRC (msg);
GstTagList *tags = NULL;
gboolean from_enhancer_src;
/* Tag messages should only be posted by sink elements */
if (G_UNLIKELY (!src))
@@ -845,8 +886,21 @@ _handle_tag_msg (GstMessage *msg, ClapperPlayer *player)
GST_LOG_OBJECT (player, "Got tags from element: %s: %" GST_PTR_FORMAT,
GST_OBJECT_NAME (src), tags);
if (G_LIKELY (player->played_item != NULL))
#if CLAPPER_WITH_ENHANCERS_LOADER
from_enhancer_src = CLAPPER_IS_ENHANCER_SRC (src);
#else
from_enhancer_src = FALSE;
#endif
/* ClapperEnhancerSrc determines tags before stream start */
if (from_enhancer_src) {
if (player->pending_tags) {
gst_tag_list_unref (player->pending_tags);
}
player->pending_tags = gst_tag_list_ref (tags);
} else if (G_LIKELY (player->played_item != NULL)) {
clapper_media_item_update_from_tag_list (player->played_item, tags, player);
}
gst_tag_list_unref (tags);
}
@@ -856,11 +910,10 @@ _handle_toc_msg (GstMessage *msg, ClapperPlayer *player)
{
GstObject *src = GST_MESSAGE_SRC (msg);
GstToc *toc = NULL;
ClapperTimeline *timeline;
gboolean updated = FALSE;
gboolean from_enhancer_src, updated = FALSE;
/* TOC messages should only be posted by sink elements after start */
if (G_UNLIKELY (!src || !player->played_item))
/* TOC messages should only be posted by sink elements */
if (G_UNLIKELY (!src))
return;
/* Either new TOC was found or previous one was updated */
@@ -870,11 +923,27 @@ _handle_toc_msg (GstMessage *msg, ClapperPlayer *player)
" from element: %s, updated: %s",
toc, GST_OBJECT_NAME (src), (updated) ? "yes" : "no");
timeline = clapper_media_item_get_timeline (player->played_item);
#if CLAPPER_WITH_ENHANCERS_LOADER
from_enhancer_src = CLAPPER_IS_ENHANCER_SRC (src);
#else
from_enhancer_src = FALSE;
#endif
if (clapper_timeline_set_toc (timeline, toc, updated)) {
clapper_app_bus_post_refresh_timeline (player->app_bus,
GST_OBJECT_CAST (player->played_item));
/* ClapperEnhancerSrc determines TOC before stream start */
if (from_enhancer_src) {
if (player->pending_toc) {
gst_toc_unref (player->pending_toc);
}
player->pending_toc = gst_toc_ref (toc);
} else if (G_LIKELY (player->played_item != NULL)) {
ClapperTimeline *timeline;
timeline = clapper_media_item_get_timeline (player->played_item);
if (clapper_timeline_set_toc (timeline, toc, updated)) {
clapper_app_bus_post_refresh_timeline (player->app_bus,
GST_OBJECT_CAST (player->played_item));
}
}
gst_toc_unref (toc);
@@ -1001,6 +1070,26 @@ _handle_stream_start_msg (GstMessage *msg, ClapperPlayer *player)
/* With playbin2 we update all decoders at once after stream start */
if (!player->use_playbin3)
clapper_player_playbin_update_current_decoders (player);
if (player->pending_tags) {
if (G_LIKELY (player->played_item != NULL))
clapper_media_item_update_from_tag_list (player->played_item, player->pending_tags, player);
gst_clear_tag_list (&player->pending_tags);
}
if (player->pending_toc) {
if (G_LIKELY (player->played_item != NULL)) {
ClapperTimeline *timeline = clapper_media_item_get_timeline (player->played_item);
if (clapper_timeline_set_toc (timeline, player->pending_toc, FALSE)) {
clapper_app_bus_post_refresh_timeline (player->app_bus,
GST_OBJECT_CAST (player->played_item));
}
}
gst_toc_unref (player->pending_toc);
player->pending_toc = NULL;
}
}
static inline void

View File

@@ -19,6 +19,8 @@
#pragma once
#include <gst/tag/tag.h>
#include "clapper-player.h"
#include "clapper-queue.h"
#include "clapper-enums.h"
@@ -53,6 +55,11 @@ struct _ClapperPlayer
* different thread, thus needs a lock */
ClapperMediaItem *pending_item;
/* Pending tags/toc that arrive before stream start.
* To be applied to "played_item", thus no lock needed. */
GstTagList *pending_tags;
GstToc *pending_toc;
GstElement *playbin;
GstBus *bus;
@@ -80,6 +87,9 @@ struct _ClapperPlayer
gboolean pending_eos; // when pausing due to EOS
gint eos; // atomic integer
/* Set adaptive props immediately */
GstElement *adaptive_demuxer;
/* Playbin2 compat */
gint n_video, n_audio, n_text;
@@ -95,6 +105,12 @@ struct _ClapperPlayer
gboolean video_enabled;
gboolean audio_enabled;
gboolean subtitles_enabled;
gchar *download_dir;
gboolean download_enabled;
guint start_bitrate;
guint min_bitrate;
guint max_bitrate;
guint bandwidth;
gdouble audio_offset;
gdouble subtitle_offset;
};

View File

@@ -43,7 +43,7 @@
#include "clapper-playbin-bus-private.h"
#include "clapper-app-bus-private.h"
#include "clapper-queue-private.h"
#include "clapper-media-item.h"
#include "clapper-media-item-private.h"
#include "clapper-stream-list-private.h"
#include "clapper-stream-private.h"
#include "clapper-video-stream-private.h"
@@ -61,6 +61,8 @@
#define DEFAULT_VIDEO_ENABLED TRUE
#define DEFAULT_AUDIO_ENABLED TRUE
#define DEFAULT_SUBTITLES_ENABLED TRUE
#define DEFAULT_DOWNLOAD_ENABLED FALSE
#define DEFAULT_ADAPTIVE_START_BITRATE 1600000
#define GST_CAT_DEFAULT clapper_player_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
@@ -90,6 +92,12 @@ enum
PROP_VIDEO_ENABLED,
PROP_AUDIO_ENABLED,
PROP_SUBTITLES_ENABLED,
PROP_DOWNLOAD_DIR,
PROP_DOWNLOAD_ENABLED,
PROP_ADAPTIVE_START_BITRATE,
PROP_ADAPTIVE_MIN_BITRATE,
PROP_ADAPTIVE_MAX_BITRATE,
PROP_ADAPTIVE_BANDWIDTH,
PROP_AUDIO_OFFSET,
PROP_SUBTITLE_OFFSET,
PROP_SUBTITLE_FONT_DESC,
@@ -99,6 +107,7 @@ enum
enum
{
SIGNAL_SEEK_DONE,
SIGNAL_DOWNLOAD_COMPLETE,
SIGNAL_MISSING_PLUGIN,
SIGNAL_WARNING,
SIGNAL_ERROR,
@@ -278,14 +287,15 @@ void
clapper_player_handle_playbin_flags_changed (ClapperPlayer *self, const GValue *value)
{
gint flags;
gboolean video_enabled, audio_enabled, subtitles_enabled;
gboolean video_changed, audio_changed, subtitles_changed;
gboolean video_enabled, audio_enabled, subtitles_enabled, download_enabled;
gboolean video_changed, audio_changed, subtitles_changed, download_changed;
flags = g_value_get_flags (value);
video_enabled = ((flags & CLAPPER_PLAYER_PLAY_FLAG_VIDEO) == CLAPPER_PLAYER_PLAY_FLAG_VIDEO);
audio_enabled = ((flags & CLAPPER_PLAYER_PLAY_FLAG_AUDIO) == CLAPPER_PLAYER_PLAY_FLAG_AUDIO);
subtitles_enabled = ((flags & CLAPPER_PLAYER_PLAY_FLAG_TEXT) == CLAPPER_PLAYER_PLAY_FLAG_TEXT);
download_enabled = ((flags & CLAPPER_PLAYER_PLAY_FLAG_DOWNLOAD) == CLAPPER_PLAYER_PLAY_FLAG_DOWNLOAD);
GST_OBJECT_LOCK (self);
@@ -295,6 +305,8 @@ clapper_player_handle_playbin_flags_changed (ClapperPlayer *self, const GValue *
self->audio_enabled = audio_enabled;
if ((subtitles_changed = self->subtitles_enabled != subtitles_enabled))
self->subtitles_enabled = subtitles_enabled;
if ((download_changed = self->download_enabled != download_enabled))
self->download_enabled = download_enabled;
GST_OBJECT_UNLOCK (self);
@@ -313,6 +325,11 @@ clapper_player_handle_playbin_flags_changed (ClapperPlayer *self, const GValue *
clapper_app_bus_post_prop_notify (self->app_bus,
GST_OBJECT_CAST (self), param_specs[PROP_SUBTITLES_ENABLED]);
}
if (download_changed) {
GST_INFO_OBJECT (self, "Download enabled: %s", (download_enabled) ? "yes" : "no");
clapper_app_bus_post_prop_notify (self->app_bus,
GST_OBJECT_CAST (self), param_specs[PROP_DOWNLOAD_ENABLED]);
}
}
void
@@ -438,7 +455,7 @@ clapper_player_set_pending_item (ClapperPlayer *self, ClapperMediaItem *pending_
/* Might be NULL (e.g. after queue is cleared) */
if (pending_item) {
uri = clapper_media_item_get_uri (pending_item);
uri = clapper_media_item_get_playback_uri (pending_item);
suburi = clapper_media_item_get_suburi (pending_item);
}
@@ -700,6 +717,32 @@ clapper_player_playbin_update_current_decoders (ClapperPlayer *self)
GST_DEBUG_OBJECT (self, "Active audio decoder not found");
}
static void
_adaptive_demuxer_bandwidth_changed_cb (GstElement *adaptive_demuxer,
GParamSpec *pspec G_GNUC_UNUSED, ClapperPlayer *self)
{
guint bandwidth = 0;
gboolean changed;
g_object_get (adaptive_demuxer, "current-bandwidth", &bandwidth, NULL);
/* Skip uncalculated bandwidth from
* new adaptive demuxer instance */
if (bandwidth == 0)
return;
GST_OBJECT_LOCK (self);
if ((changed = bandwidth != self->bandwidth))
self->bandwidth = bandwidth;
GST_OBJECT_UNLOCK (self);
if (changed) {
GST_LOG_OBJECT (self, "Adaptive bandwidth: %u", bandwidth);
clapper_app_bus_post_prop_notify (self->app_bus,
GST_OBJECT_CAST (self), param_specs[PROP_ADAPTIVE_BANDWIDTH]);
}
}
void
clapper_player_reset (ClapperPlayer *self, gboolean pending_dispose)
{
@@ -715,8 +758,21 @@ clapper_player_reset (ClapperPlayer *self, gboolean pending_dispose)
gst_clear_object (&self->audio_decoder);
}
if (self->adaptive_demuxer) {
g_signal_handlers_disconnect_by_func (self->adaptive_demuxer,
_adaptive_demuxer_bandwidth_changed_cb, self);
gst_clear_object (&self->adaptive_demuxer);
}
GST_OBJECT_UNLOCK (self);
gst_clear_tag_list (&self->pending_tags);
if (self->pending_toc) {
gst_toc_unref (self->pending_toc);
self->pending_toc = NULL;
}
/* Emit notify when we are not going to be disposed */
if (!pending_dispose) {
/* Clear current decoders (next item might not have video/audio track) */
@@ -725,6 +781,83 @@ clapper_player_reset (ClapperPlayer *self, gboolean pending_dispose)
}
}
static inline gchar *
_make_download_template (ClapperPlayer *self)
{
gchar *download_template = NULL;
GST_OBJECT_LOCK (self);
if (self->download_enabled && self->download_dir) {
if (g_mkdir_with_parents (self->download_dir, 0755) == 0) {
download_template = g_build_filename (self->download_dir, "XXXXXX", NULL);
} else {
GST_ERROR_OBJECT (self, "Could not create download dir: \"%s\"", self->download_dir);
}
}
GST_OBJECT_UNLOCK (self);
return download_template;
}
static void
_element_setup_cb (GstElement *playbin, GstElement *element, ClapperPlayer *self)
{
GstElementFactory *factory = gst_element_get_factory (element);
const gchar *factory_name;
if (G_UNLIKELY (factory == NULL))
return;
factory_name = g_intern_static_string (GST_OBJECT_NAME (factory));
GST_INFO_OBJECT (self, "Element setup: %s", factory_name);
if (factory_name == g_intern_static_string ("downloadbuffer")) {
gchar *download_template;
/* Only set props if we have download template */
if ((download_template = _make_download_template (self))) {
g_object_set (element,
"temp-template", download_template,
"temp-remove", FALSE,
NULL);
g_free (download_template);
}
} else if (factory_name == g_intern_static_string ("dashdemux2")
|| factory_name == g_intern_static_string ("hlsdemux2")) {
guint start_bitrate, min_bitrate, max_bitrate;
GST_OBJECT_LOCK (self);
start_bitrate = self->start_bitrate;
min_bitrate = self->min_bitrate;
max_bitrate = self->max_bitrate;
if (self->adaptive_demuxer) {
g_signal_handlers_disconnect_by_func (self->adaptive_demuxer,
_adaptive_demuxer_bandwidth_changed_cb, self);
}
gst_object_replace ((GstObject **) &self->adaptive_demuxer, GST_OBJECT_CAST (element));
if (self->adaptive_demuxer) {
g_signal_connect (self->adaptive_demuxer, "notify::current-bandwidth",
G_CALLBACK (_adaptive_demuxer_bandwidth_changed_cb), self);
}
GST_OBJECT_UNLOCK (self);
g_object_set (element,
"low-watermark-time", 3 * GST_SECOND,
"high-watermark-time", 10 * GST_SECOND,
"start-bitrate", start_bitrate,
"min-bitrate", min_bitrate,
"max-bitrate", max_bitrate,
NULL);
}
}
static void
_about_to_finish_cb (GstElement *playbin, ClapperPlayer *self)
{
@@ -1505,6 +1638,293 @@ clapper_player_get_subtitles_enabled (ClapperPlayer *self)
return enabled;
}
/**
* clapper_player_set_download_dir:
* @player: a #ClapperPlayer
* @path: (type filename): the path of a directory to use for media downloads
*
* Set a directory that @player will use to store downloads.
*
* See [property@Clapper.Player:download-enabled] description for more
* info how this works.
*
* Since: 0.8
*/
void
clapper_player_set_download_dir (ClapperPlayer *self, const gchar *path)
{
gboolean changed;
g_return_if_fail (CLAPPER_IS_PLAYER (self));
g_return_if_fail (path != NULL);
GST_OBJECT_LOCK (self);
changed = g_set_str (&self->download_dir, path);
GST_OBJECT_UNLOCK (self);
if (changed) {
GST_INFO_OBJECT (self, "Current download dir: %s", path);
clapper_app_bus_post_prop_notify (self->app_bus,
GST_OBJECT_CAST (self), param_specs[PROP_DOWNLOAD_DIR]);
}
}
/**
* clapper_player_get_download_dir:
* @player: a #ClapperPlayer
*
* Get path to a directory set for media downloads.
*
* Returns: (type filename) (transfer full) (nullable): the path of a directory
* set for media downloads or %NULL if no directory was set yet.
*
* Since: 0.8
*/
gchar *
clapper_player_get_download_dir (ClapperPlayer *self)
{
gchar *download_dir;
g_return_val_if_fail (CLAPPER_IS_PLAYER (self), NULL);
GST_OBJECT_LOCK (self);
download_dir = g_strdup (self->download_dir);
GST_OBJECT_UNLOCK (self);
return download_dir;
}
/**
* clapper_player_set_download_enabled:
* @player: a #ClapperPlayer
* @enabled: whether enabled
*
* Set whether player should attempt progressive download buffering.
*
* For this to actually work a [property@Clapper.Player:download-dir]
* must also be set.
*
* Since: 0.8
*/
void
clapper_player_set_download_enabled (ClapperPlayer *self, gboolean enabled)
{
g_return_if_fail (CLAPPER_IS_PLAYER (self));
clapper_playbin_bus_post_set_play_flag (self->bus, CLAPPER_PLAYER_PLAY_FLAG_DOWNLOAD, enabled);
}
/**
* clapper_player_get_download_enabled:
* @player: a #ClapperPlayer
*
* Get whether progressive download buffering is enabled.
*
* Returns: %TRUE if enabled, %FALSE otherwise.
*
* Since: 0.8
*/
gboolean
clapper_player_get_download_enabled (ClapperPlayer *self)
{
gboolean enabled;
g_return_val_if_fail (CLAPPER_IS_PLAYER (self), FALSE);
GST_OBJECT_LOCK (self);
enabled = self->download_enabled;
GST_OBJECT_UNLOCK (self);
return enabled;
}
static void
_set_adaptive_bitrate (ClapperPlayer *self, guint *internal_ptr,
const gchar *prop_name, guint bitrate, GParamSpec *pspec)
{
GstElement *element = NULL;
gboolean changed;
if (!self->use_playbin3) {
GST_WARNING_OBJECT (self, "Setting adaptive-%s when using playbin2"
" has no effect", prop_name);
}
GST_OBJECT_LOCK (self);
if ((changed = (*internal_ptr != bitrate))) {
*internal_ptr = bitrate;
if (self->adaptive_demuxer)
element = gst_object_ref (self->adaptive_demuxer);
}
GST_OBJECT_UNLOCK (self);
if (changed) {
GST_INFO_OBJECT (self, "Set adaptive-%s: %u", prop_name, bitrate);
if (element)
g_object_set (element, prop_name, bitrate, NULL);
clapper_app_bus_post_prop_notify (self->app_bus, GST_OBJECT_CAST (self), pspec);
}
gst_clear_object (&element);
}
/**
* clapper_player_set_adaptive_start_bitrate:
* @player: a #ClapperPlayer
* @bitrate: a bitrate to set (bits/s)
*
* Set initial bitrate to select when starting adaptive
* streaming such as DASH or HLS.
*
* Since: 0.8
*/
void
clapper_player_set_adaptive_start_bitrate (ClapperPlayer *self, guint bitrate)
{
g_return_if_fail (CLAPPER_IS_PLAYER (self));
_set_adaptive_bitrate (self, &self->start_bitrate,
"start-bitrate", bitrate, param_specs[PROP_ADAPTIVE_START_BITRATE]);
}
/**
* clapper_player_get_adaptive_start_bitrate:
* @player: a #ClapperPlayer
*
* Get currently set initial bitrate (bits/s) for adaptive streaming.
*
* Returns: the start bitrate value.
*
* Since: 0.8
*/
guint
clapper_player_get_adaptive_start_bitrate (ClapperPlayer *self)
{
guint bitrate;
g_return_val_if_fail (CLAPPER_IS_PLAYER (self), 0);
GST_OBJECT_LOCK (self);
bitrate = self->start_bitrate;
GST_OBJECT_UNLOCK (self);
return bitrate;
}
/**
* clapper_player_set_adaptive_min_bitrate:
* @player: a #ClapperPlayer
* @bitrate: a bitrate to set (bits/s)
*
* Set minimal bitrate to select for adaptive streaming
* such as DASH or HLS.
*
* Since: 0.8
*/
void
clapper_player_set_adaptive_min_bitrate (ClapperPlayer *self, guint bitrate)
{
g_return_if_fail (CLAPPER_IS_PLAYER (self));
_set_adaptive_bitrate (self, &self->min_bitrate,
"min-bitrate", bitrate, param_specs[PROP_ADAPTIVE_MIN_BITRATE]);
}
/**
* clapper_player_get_adaptive_min_bitrate:
* @player: a #ClapperPlayer
*
* Get currently set minimal bitrate (bits/s) for adaptive streaming.
*
* Returns: the minimal bitrate value.
*
* Since: 0.8
*/
guint
clapper_player_get_adaptive_min_bitrate (ClapperPlayer *self)
{
guint bitrate;
g_return_val_if_fail (CLAPPER_IS_PLAYER (self), 0);
GST_OBJECT_LOCK (self);
bitrate = self->min_bitrate;
GST_OBJECT_UNLOCK (self);
return bitrate;
}
/**
* clapper_player_set_adaptive_max_bitrate:
* @player: a #ClapperPlayer
* @bitrate: a bitrate to set (bits/s)
*
* Set maximal bitrate to select for adaptive streaming
* such as DASH or HLS.
*
* Since: 0.8
*/
void
clapper_player_set_adaptive_max_bitrate (ClapperPlayer *self, guint bitrate)
{
g_return_if_fail (CLAPPER_IS_PLAYER (self));
_set_adaptive_bitrate (self, &self->max_bitrate,
"max-bitrate", bitrate, param_specs[PROP_ADAPTIVE_MAX_BITRATE]);
}
/**
* clapper_player_get_adaptive_max_bitrate:
* @player: a #ClapperPlayer
*
* Get currently set maximal bitrate (bits/s) for adaptive streaming.
*
* Returns: the maximal bitrate value.
*
* Since: 0.8
*/
guint
clapper_player_get_adaptive_max_bitrate (ClapperPlayer *self)
{
guint bitrate;
g_return_val_if_fail (CLAPPER_IS_PLAYER (self), 0);
GST_OBJECT_LOCK (self);
bitrate = self->max_bitrate;
GST_OBJECT_UNLOCK (self);
return bitrate;
}
/**
* clapper_player_get_adaptive_bandwidth:
* @player: a #ClapperPlayer
*
* Get last fragment download bandwidth (bits/s) during
* adaptive streaming.
*
* Returns: the adaptive bandwidth.
*
* Since: 0.8
*/
guint
clapper_player_get_adaptive_bandwidth (ClapperPlayer *self)
{
guint bandwidth;
g_return_val_if_fail (CLAPPER_IS_PLAYER (self), 0);
GST_OBJECT_LOCK (self);
bandwidth = self->bandwidth;
GST_OBJECT_UNLOCK (self);
return bandwidth;
}
/**
* clapper_player_set_audio_offset:
* @player: a #ClapperPlayer
@@ -1766,6 +2186,28 @@ clapper_player_add_feature (ClapperPlayer *self, ClapperFeature *feature)
clapper_features_manager_add_feature (self->features_manager, feature, GST_OBJECT (self));
}
/**
* clapper_player_make_pipeline_graph:
* @player: a #ClapperPlayer
* @details: a #GstDebugGraphDetails level
*
* Make current #GStreamer pipeline graph in `graphviz` dot format.
*
* Applications can use tools like `graphviz` to display returned
* data or just save it to a file as-is for the user to do it manually.
*
* Returns: (transfer full): current pipeline description in dot format.
*
* Since: 0.8
*/
gchar *
clapper_player_make_pipeline_graph (ClapperPlayer *self, GstDebugGraphDetails details)
{
g_return_val_if_fail (CLAPPER_IS_PLAYER (self), NULL);
return gst_debug_bin_to_dot_data (GST_BIN (self->playbin), details);
}
static void
clapper_player_thread_start (ClapperThreadedObject *threaded_object)
{
@@ -1779,7 +2221,7 @@ clapper_player_thread_start (ClapperThreadedObject *threaded_object)
if (!(env = g_getenv ("CLAPPER_USE_PLAYBIN3"))) // Clapper override
env = g_getenv ("GST_CLAPPER_USE_PLAYBIN3"); // compat
self->use_playbin3 = (env && g_str_has_prefix (env, "1"));
self->use_playbin3 = (!env || g_str_has_prefix (env, "1"));
playbin_str = (self->use_playbin3) ? "playbin3" : "playbin";
if (!(self->playbin = gst_element_factory_make (playbin_str, NULL))) {
@@ -1793,6 +2235,7 @@ clapper_player_thread_start (ClapperThreadedObject *threaded_object)
for (i = 0; playbin_watchlist[i]; ++i)
gst_element_add_property_notify_watch (self->playbin, playbin_watchlist[i], TRUE);
g_signal_connect (self->playbin, "element-setup", G_CALLBACK (_element_setup_cb), self);
g_signal_connect (self->playbin, "about-to-finish", G_CALLBACK (_about_to_finish_cb), self);
if (!self->use_playbin3) {
@@ -1866,6 +2309,8 @@ clapper_player_init (ClapperPlayer *self)
self->video_enabled = DEFAULT_VIDEO_ENABLED;
self->audio_enabled = DEFAULT_AUDIO_ENABLED;
self->subtitles_enabled = DEFAULT_SUBTITLES_ENABLED;
self->download_enabled = DEFAULT_DOWNLOAD_ENABLED;
self->start_bitrate = DEFAULT_ADAPTIVE_START_BITRATE;
}
static void
@@ -1924,6 +2369,8 @@ clapper_player_finalize (GObject *object)
gst_clear_object (&self->pending_item);
gst_clear_object (&self->played_item);
g_free (self->download_dir);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@@ -1991,6 +2438,24 @@ clapper_player_get_property (GObject *object, guint prop_id,
case PROP_SUBTITLES_ENABLED:
g_value_set_boolean (value, clapper_player_get_subtitles_enabled (self));
break;
case PROP_DOWNLOAD_DIR:
g_value_take_string (value, clapper_player_get_download_dir (self));
break;
case PROP_DOWNLOAD_ENABLED:
g_value_set_boolean (value, clapper_player_get_download_enabled (self));
break;
case PROP_ADAPTIVE_START_BITRATE:
g_value_set_uint (value, clapper_player_get_adaptive_start_bitrate (self));
break;
case PROP_ADAPTIVE_MIN_BITRATE:
g_value_set_uint (value, clapper_player_get_adaptive_min_bitrate (self));
break;
case PROP_ADAPTIVE_MAX_BITRATE:
g_value_set_uint (value, clapper_player_get_adaptive_max_bitrate (self));
break;
case PROP_ADAPTIVE_BANDWIDTH:
g_value_set_uint (value, clapper_player_get_adaptive_bandwidth (self));
break;
case PROP_AUDIO_OFFSET:
g_value_set_double (value, clapper_player_get_audio_offset (self));
break;
@@ -2046,6 +2511,21 @@ clapper_player_set_property (GObject *object, guint prop_id,
case PROP_SUBTITLES_ENABLED:
clapper_player_set_subtitles_enabled (self, g_value_get_boolean (value));
break;
case PROP_DOWNLOAD_DIR:
clapper_player_set_download_dir (self, g_value_get_string (value));
break;
case PROP_DOWNLOAD_ENABLED:
clapper_player_set_download_enabled (self, g_value_get_boolean (value));
break;
case PROP_ADAPTIVE_START_BITRATE:
clapper_player_set_adaptive_start_bitrate (self, g_value_get_uint (value));
break;
case PROP_ADAPTIVE_MIN_BITRATE:
clapper_player_set_adaptive_min_bitrate (self, g_value_get_uint (value));
break;
case PROP_ADAPTIVE_MAX_BITRATE:
clapper_player_set_adaptive_max_bitrate (self, g_value_get_uint (value));
break;
case PROP_AUDIO_OFFSET:
clapper_player_set_audio_offset (self, g_value_get_double (value));
break;
@@ -2251,6 +2731,118 @@ clapper_player_class_init (ClapperPlayerClass *klass)
NULL, NULL, DEFAULT_SUBTITLES_ENABLED,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperPlayer:download-dir:
*
* A directory that @player will use to download network content
* when [property@Clapper.Player:download-enabled] is set to %TRUE.
*
* If directory at @path does not exist, it will be automatically created.
*
* Since: 0.8
*/
param_specs[PROP_DOWNLOAD_DIR] = g_param_spec_string ("download-dir",
NULL, NULL, NULL,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperPlayer:download-enabled:
*
* Whether progressive download buffering is enabled.
*
* If progressive download is enabled and [property@Clapper.Player:download-dir]
* is set, streamed network content will be cached to the disk space instead
* of memory whenever possible. This allows for faster seeking through
* currently played media.
*
* Not every type of content is download applicable. Mainly applies to
* web content that does not use adaptive streaming.
*
* Once data that media item URI points to is fully downloaded, player
* will emit [signal@Clapper.Player::download-complete] signal with a
* location of downloaded file.
*
* Playing again the exact same [class@Clapper.MediaItem] object that was
* previously fully downloaded will cause player to automatically use that
* cached file if it still exists, avoiding any further network requests.
*
* Please note that player will not delete nor manage downloaded content.
* It is up to application to cleanup data in created cache directory
* (e.g. before app exits), in order to remove any downloads that app
* is not going to use next time it is run and incomplete ones.
*
* Since: 0.8
*/
param_specs[PROP_DOWNLOAD_ENABLED] = g_param_spec_boolean ("download-enabled",
NULL, NULL, DEFAULT_DOWNLOAD_ENABLED,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperPlayer:adaptive-start-bitrate:
*
* An initial bitrate (bits/s) to select during
* starting adaptive streaming such as DASH or HLS.
*
* If value is higher than lowest available bitrate in streaming
* manifest, then lowest possible bitrate will be selected.
*
* Since: 0.8
*/
param_specs[PROP_ADAPTIVE_START_BITRATE] = g_param_spec_uint ("adaptive-start-bitrate",
NULL, NULL, 0, G_MAXUINT, DEFAULT_ADAPTIVE_START_BITRATE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperPlayer:adaptive-min-bitrate:
*
* A minimal allowed bitrate (bits/s) during adaptive streaming
* such as DASH or HLS.
*
* Setting this will prevent streaming from entering lower qualities
* (even when connection speed cannot keep up). When set together with
* [property@Clapper.Player:adaptive-max-bitrate] it can be used to
* enforce some specific quality.
*
* Since: 0.8
*/
param_specs[PROP_ADAPTIVE_MIN_BITRATE] = g_param_spec_uint ("adaptive-min-bitrate",
NULL, NULL, 0, G_MAXUINT, 0,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperPlayer:adaptive-max-bitrate:
*
* A maximal allowed bitrate (bits/s) during adaptive streaming
* such as DASH or HLS (`0` for unlimited).
*
* Setting this will prevent streaming from entering qualities with
* higher bandwidth than the one set. When set together with
* [property@Clapper.Player:adaptive-min-bitrate] it can be used to
* enforce some specific quality.
*
* Since: 0.8
*/
param_specs[PROP_ADAPTIVE_MAX_BITRATE] = g_param_spec_uint ("adaptive-max-bitrate",
NULL, NULL, 0, G_MAXUINT, 0,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperPlayer:adaptive-bandwidth:
*
* Last fragment download bandwidth (bits/s) during adaptive streaming.
*
* This property only changes when adaptive streaming and later stays
* at the last value until streaming some adaptive content again.
*
* Apps can use this to determine and set an optimal value for
* [property@Clapper.Player:adaptive-start-bitrate].
*
* Since: 0.8
*/
param_specs[PROP_ADAPTIVE_BANDWIDTH] = g_param_spec_uint ("adaptive-bandwidth",
NULL, NULL, 0, G_MAXUINT, 0,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperPlayer:audio-offset:
*
@@ -2288,6 +2880,22 @@ clapper_player_class_init (ClapperPlayerClass *klass)
G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
0, NULL, NULL, NULL, G_TYPE_NONE, 0);
/**
* ClapperPlayer::download-complete:
* @player: a #ClapperPlayer
* @item: a #ClapperMediaItem
* @location: (type filename): a path to downloaded file
*
* Media was fully downloaded to local cache directory. This signal will
* be only emitted when progressive download buffering is enabled by
* setting [property@Clapper.Player:download-enabled] property to %TRUE.
*
* Since: 0.8
*/
signals[SIGNAL_DOWNLOAD_COMPLETE] = g_signal_new ("download-complete",
G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
0, NULL, NULL, NULL, G_TYPE_NONE, 2, CLAPPER_TYPE_MEDIA_ITEM, G_TYPE_STRING);
/**
* ClapperPlayer::missing-plugin:
* @player: a #ClapperPlayer

View File

@@ -26,6 +26,8 @@
#include <glib.h>
#include <glib-object.h>
#include <gst/gst.h>
#include <clapper/clapper-visibility.h>
#include <clapper/clapper-threaded-object.h>
#include <clapper/clapper-queue.h>
#include <clapper/clapper-stream-list.h>
@@ -37,92 +39,172 @@ G_BEGIN_DECLS
#define CLAPPER_TYPE_PLAYER (clapper_player_get_type())
#define CLAPPER_PLAYER_CAST(obj) ((ClapperPlayer *)(obj))
CLAPPER_API
G_DECLARE_FINAL_TYPE (ClapperPlayer, clapper_player, CLAPPER, PLAYER, ClapperThreadedObject)
CLAPPER_API
ClapperPlayer * clapper_player_new (void);
CLAPPER_API
ClapperQueue * clapper_player_get_queue (ClapperPlayer *player);
CLAPPER_API
ClapperStreamList * clapper_player_get_video_streams (ClapperPlayer *player);
CLAPPER_API
ClapperStreamList * clapper_player_get_audio_streams (ClapperPlayer *player);
CLAPPER_API
ClapperStreamList * clapper_player_get_subtitle_streams (ClapperPlayer *player);
CLAPPER_API
void clapper_player_set_autoplay (ClapperPlayer *player, gboolean enabled);
CLAPPER_API
gboolean clapper_player_get_autoplay (ClapperPlayer *player);
CLAPPER_API
gdouble clapper_player_get_position (ClapperPlayer *player);
CLAPPER_API
ClapperPlayerState clapper_player_get_state (ClapperPlayer *player);
CLAPPER_API
void clapper_player_set_mute (ClapperPlayer *player, gboolean mute);
CLAPPER_API
gboolean clapper_player_get_mute (ClapperPlayer *player);
CLAPPER_API
void clapper_player_set_volume (ClapperPlayer *player, gdouble volume);
CLAPPER_API
gdouble clapper_player_get_volume (ClapperPlayer *player);
CLAPPER_API
void clapper_player_set_speed (ClapperPlayer *player, gdouble speed);
CLAPPER_API
gdouble clapper_player_get_speed (ClapperPlayer *player);
CLAPPER_API
void clapper_player_set_video_sink (ClapperPlayer *player, GstElement *element);
CLAPPER_API
GstElement * clapper_player_get_video_sink (ClapperPlayer *player);
CLAPPER_API
void clapper_player_set_audio_sink (ClapperPlayer *player, GstElement *element);
CLAPPER_API
GstElement * clapper_player_get_audio_sink (ClapperPlayer *player);
CLAPPER_API
void clapper_player_set_video_filter (ClapperPlayer *player, GstElement *element);
CLAPPER_API
GstElement * clapper_player_get_video_filter (ClapperPlayer *player);
CLAPPER_API
void clapper_player_set_audio_filter (ClapperPlayer *player, GstElement *element);
CLAPPER_API
GstElement * clapper_player_get_audio_filter (ClapperPlayer *player);
CLAPPER_API
GstElement * clapper_player_get_current_video_decoder (ClapperPlayer *player);
CLAPPER_API
GstElement * clapper_player_get_current_audio_decoder (ClapperPlayer *player);
CLAPPER_API
void clapper_player_set_video_enabled (ClapperPlayer *player, gboolean enabled);
CLAPPER_API
gboolean clapper_player_get_video_enabled (ClapperPlayer *player);
CLAPPER_API
void clapper_player_set_audio_enabled (ClapperPlayer *player, gboolean enabled);
CLAPPER_API
gboolean clapper_player_get_audio_enabled (ClapperPlayer *player);
CLAPPER_API
void clapper_player_set_subtitles_enabled (ClapperPlayer *player, gboolean enabled);
CLAPPER_API
gboolean clapper_player_get_subtitles_enabled (ClapperPlayer *player);
CLAPPER_API
void clapper_player_set_download_dir (ClapperPlayer *player, const gchar *path);
CLAPPER_API
gchar * clapper_player_get_download_dir (ClapperPlayer *player);
CLAPPER_API
void clapper_player_set_download_enabled (ClapperPlayer *player, gboolean enabled);
CLAPPER_API
gboolean clapper_player_get_download_enabled (ClapperPlayer *player);
CLAPPER_API
void clapper_player_set_adaptive_start_bitrate (ClapperPlayer *player, guint bitrate);
CLAPPER_API
guint clapper_player_get_adaptive_start_bitrate (ClapperPlayer *player);
CLAPPER_API
void clapper_player_set_adaptive_min_bitrate (ClapperPlayer *player, guint bitrate);
CLAPPER_API
guint clapper_player_get_adaptive_min_bitrate (ClapperPlayer *player);
CLAPPER_API
void clapper_player_set_adaptive_max_bitrate (ClapperPlayer *player, guint bitrate);
CLAPPER_API
guint clapper_player_get_adaptive_max_bitrate (ClapperPlayer *player);
CLAPPER_API
guint clapper_player_get_adaptive_bandwidth (ClapperPlayer *player);
CLAPPER_API
void clapper_player_set_audio_offset (ClapperPlayer *player, gdouble offset);
CLAPPER_API
gdouble clapper_player_get_audio_offset (ClapperPlayer *player);
CLAPPER_API
void clapper_player_set_subtitle_offset (ClapperPlayer *player, gdouble offset);
CLAPPER_API
gdouble clapper_player_get_subtitle_offset (ClapperPlayer *player);
CLAPPER_API
void clapper_player_set_subtitle_font_desc (ClapperPlayer *player, const gchar *font_desc);
CLAPPER_API
gchar * clapper_player_get_subtitle_font_desc (ClapperPlayer *player);
CLAPPER_API
void clapper_player_play (ClapperPlayer *player);
CLAPPER_API
void clapper_player_pause (ClapperPlayer *player);
CLAPPER_API
void clapper_player_stop (ClapperPlayer *player);
CLAPPER_API
void clapper_player_seek (ClapperPlayer *player, gdouble position);
CLAPPER_API
void clapper_player_seek_custom (ClapperPlayer *player, gdouble position, ClapperPlayerSeekMethod method);
CLAPPER_API
void clapper_player_add_feature (ClapperPlayer *player, ClapperFeature *feature);
CLAPPER_API
gchar * clapper_player_make_pipeline_graph (ClapperPlayer *player, GstDebugGraphDetails details);
G_END_DECLS

View File

@@ -27,6 +27,7 @@
#include <glib-object.h>
#include <gst/gst.h>
#include <clapper/clapper-visibility.h>
#include <clapper/clapper-enums.h>
#include <clapper/clapper-media-item.h>
@@ -35,6 +36,7 @@ G_BEGIN_DECLS
#define CLAPPER_TYPE_QUEUE (clapper_queue_get_type())
#define CLAPPER_QUEUE_CAST(obj) ((ClapperQueue *)(obj))
CLAPPER_API
G_DECLARE_FINAL_TYPE (ClapperQueue, clapper_queue, CLAPPER, QUEUE, GstObject)
/**
@@ -44,50 +46,73 @@ G_DECLARE_FINAL_TYPE (ClapperQueue, clapper_queue, CLAPPER, QUEUE, GstObject)
*/
#define CLAPPER_QUEUE_INVALID_POSITION ((guint) 0xffffffff)
CLAPPER_API
void clapper_queue_add_item (ClapperQueue *queue, ClapperMediaItem *item);
CLAPPER_API
void clapper_queue_insert_item (ClapperQueue *queue, ClapperMediaItem *item, gint index);
CLAPPER_API
void clapper_queue_reposition_item (ClapperQueue *queue, ClapperMediaItem *item, gint index);
CLAPPER_API
void clapper_queue_remove_item (ClapperQueue *queue, ClapperMediaItem *item);
CLAPPER_API
void clapper_queue_remove_index (ClapperQueue *queue, guint index);
CLAPPER_API
ClapperMediaItem * clapper_queue_steal_index (ClapperQueue *queue, guint index);
CLAPPER_API
void clapper_queue_clear (ClapperQueue *queue);
CLAPPER_API
gboolean clapper_queue_select_item (ClapperQueue *queue, ClapperMediaItem *item);
CLAPPER_API
gboolean clapper_queue_select_index (ClapperQueue *queue, guint index);
CLAPPER_API
gboolean clapper_queue_select_next_item (ClapperQueue *queue);
CLAPPER_API
gboolean clapper_queue_select_previous_item (ClapperQueue *queue);
CLAPPER_API
ClapperMediaItem * clapper_queue_get_item (ClapperQueue *queue, guint index);
CLAPPER_API
ClapperMediaItem * clapper_queue_get_current_item (ClapperQueue *queue);
CLAPPER_API
guint clapper_queue_get_current_index (ClapperQueue *queue);
CLAPPER_API
gboolean clapper_queue_item_is_current (ClapperQueue *queue, ClapperMediaItem *item);
CLAPPER_API
gboolean clapper_queue_find_item (ClapperQueue *queue, ClapperMediaItem *item, guint *index);
CLAPPER_API
guint clapper_queue_get_n_items (ClapperQueue *queue);
CLAPPER_API
void clapper_queue_set_progression_mode (ClapperQueue *queue, ClapperQueueProgressionMode mode);
CLAPPER_API
ClapperQueueProgressionMode clapper_queue_get_progression_mode (ClapperQueue *queue);
CLAPPER_API
void clapper_queue_set_gapless (ClapperQueue *queue, gboolean gapless);
CLAPPER_API
gboolean clapper_queue_get_gapless (ClapperQueue *queue);
CLAPPER_API
void clapper_queue_set_instant (ClapperQueue *queue, gboolean instant);
CLAPPER_API
gboolean clapper_queue_get_instant (ClapperQueue *queue);
G_END_DECLS

View File

@@ -27,6 +27,7 @@
#include <glib-object.h>
#include <gst/gst.h>
#include <clapper/clapper-visibility.h>
#include <clapper/clapper-stream.h>
G_BEGIN_DECLS
@@ -34,6 +35,7 @@ G_BEGIN_DECLS
#define CLAPPER_TYPE_STREAM_LIST (clapper_stream_list_get_type())
#define CLAPPER_STREAM_LIST_CAST(obj) ((ClapperStreamList *)(obj))
CLAPPER_API
G_DECLARE_FINAL_TYPE (ClapperStreamList, clapper_stream_list, CLAPPER, STREAM_LIST, GstObject)
/**
@@ -43,16 +45,22 @@ G_DECLARE_FINAL_TYPE (ClapperStreamList, clapper_stream_list, CLAPPER, STREAM_LI
*/
#define CLAPPER_STREAM_LIST_INVALID_POSITION ((guint) 0xffffffff)
CLAPPER_API
gboolean clapper_stream_list_select_stream (ClapperStreamList *list, ClapperStream *stream);
CLAPPER_API
gboolean clapper_stream_list_select_index (ClapperStreamList *list, guint index);
CLAPPER_API
ClapperStream * clapper_stream_list_get_stream (ClapperStreamList *list, guint index);
CLAPPER_API
ClapperStream * clapper_stream_list_get_current_stream (ClapperStreamList *list);
CLAPPER_API
guint clapper_stream_list_get_current_index (ClapperStreamList *list);
CLAPPER_API
guint clapper_stream_list_get_n_streams (ClapperStreamList *list);
G_END_DECLS

View File

@@ -27,6 +27,7 @@
#include <glib-object.h>
#include <gst/gst.h>
#include <clapper/clapper-visibility.h>
#include <clapper/clapper-enums.h>
G_BEGIN_DECLS
@@ -34,6 +35,7 @@ G_BEGIN_DECLS
#define CLAPPER_TYPE_STREAM (clapper_stream_get_type())
#define CLAPPER_STREAM_CAST(obj) ((ClapperStream *)(obj))
CLAPPER_API
G_DECLARE_DERIVABLE_TYPE (ClapperStream, clapper_stream, CLAPPER, STREAM, GstObject)
struct _ClapperStreamClass
@@ -58,8 +60,10 @@ struct _ClapperStreamClass
gpointer padding[4];
};
CLAPPER_API
ClapperStreamType clapper_stream_get_stream_type (ClapperStream *stream);
CLAPPER_API
gchar * clapper_stream_get_title (ClapperStream *stream);
G_END_DECLS

View File

@@ -25,6 +25,8 @@
#include <glib.h>
#include <glib-object.h>
#include <clapper/clapper-visibility.h>
#include <clapper/clapper-stream.h>
G_BEGIN_DECLS
@@ -32,10 +34,13 @@ G_BEGIN_DECLS
#define CLAPPER_TYPE_SUBTITLE_STREAM (clapper_subtitle_stream_get_type())
#define CLAPPER_SUBTITLE_STREAM_CAST(obj) ((ClapperSubtitleStream *)(obj))
CLAPPER_API
G_DECLARE_FINAL_TYPE (ClapperSubtitleStream, clapper_subtitle_stream, CLAPPER, SUBTITLE_STREAM, ClapperStream)
CLAPPER_API
gchar * clapper_subtitle_stream_get_lang_code (ClapperSubtitleStream *stream);
CLAPPER_API
gchar * clapper_subtitle_stream_get_lang_name (ClapperSubtitleStream *stream);
G_END_DECLS

View File

@@ -27,11 +27,14 @@
#include <glib-object.h>
#include <gst/gst.h>
#include <clapper/clapper-visibility.h>
G_BEGIN_DECLS
#define CLAPPER_TYPE_THREADED_OBJECT (clapper_threaded_object_get_type())
#define CLAPPER_THREADED_OBJECT_CAST(obj) ((ClapperThreadedObject *)(obj))
CLAPPER_API
G_DECLARE_DERIVABLE_TYPE (ClapperThreadedObject, clapper_threaded_object, CLAPPER, THREADED_OBJECT, GstObject)
/**
@@ -68,6 +71,7 @@ struct _ClapperThreadedObjectClass
gpointer padding[4];
};
CLAPPER_API
GMainContext * clapper_threaded_object_get_context (ClapperThreadedObject *threaded_object);
G_END_DECLS

View File

@@ -27,6 +27,7 @@
#include <glib-object.h>
#include <gst/gst.h>
#include <clapper/clapper-visibility.h>
#include <clapper/clapper-marker.h>
G_BEGIN_DECLS
@@ -34,14 +35,19 @@ G_BEGIN_DECLS
#define CLAPPER_TYPE_TIMELINE (clapper_timeline_get_type())
#define CLAPPER_TIMELINE_CAST(obj) ((ClapperTimeline *)(obj))
CLAPPER_API
G_DECLARE_FINAL_TYPE (ClapperTimeline, clapper_timeline, CLAPPER, TIMELINE, GstObject)
CLAPPER_API
gboolean clapper_timeline_insert_marker (ClapperTimeline *timeline, ClapperMarker *marker);
CLAPPER_API
void clapper_timeline_remove_marker (ClapperTimeline *timeline, ClapperMarker *marker);
CLAPPER_API
ClapperMarker * clapper_timeline_get_marker (ClapperTimeline *timeline, guint index);
CLAPPER_API
guint clapper_timeline_get_n_markers (ClapperTimeline *timeline);
G_END_DECLS

View File

@@ -25,6 +25,8 @@
#include <glib.h>
#include <glib-object.h>
#include <clapper/clapper-visibility.h>
#include <clapper/clapper-stream.h>
G_BEGIN_DECLS
@@ -32,18 +34,25 @@ G_BEGIN_DECLS
#define CLAPPER_TYPE_VIDEO_STREAM (clapper_video_stream_get_type())
#define CLAPPER_VIDEO_STREAM_CAST(obj) ((ClapperVideoStream *)(obj))
CLAPPER_API
G_DECLARE_FINAL_TYPE (ClapperVideoStream, clapper_video_stream, CLAPPER, VIDEO_STREAM, ClapperStream)
CLAPPER_API
gchar * clapper_video_stream_get_codec (ClapperVideoStream *stream);
CLAPPER_API
gint clapper_video_stream_get_width (ClapperVideoStream *stream);
CLAPPER_API
gint clapper_video_stream_get_height (ClapperVideoStream *stream);
CLAPPER_API
gdouble clapper_video_stream_get_fps (ClapperVideoStream *stream);
CLAPPER_API
guint clapper_video_stream_get_bitrate (ClapperVideoStream *stream);
CLAPPER_API
gchar * clapper_video_stream_get_pixel_format (ClapperVideoStream *stream);
G_END_DECLS

View File

@@ -17,6 +17,8 @@
* Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include <gst/gst.h>
#include <gst/pbutils/pbutils.h>
@@ -25,6 +27,11 @@
#include "clapper-playbin-bus-private.h"
#include "clapper-app-bus-private.h"
#include "clapper-features-bus-private.h"
#include "gst/clapper-plugin-private.h"
#if CLAPPER_WITH_ENHANCERS_LOADER
#include "clapper-enhancers-loader-private.h"
#endif
static gboolean is_initialized = FALSE;
static GMutex init_lock;
@@ -44,6 +51,22 @@ clapper_init_check_internal (int *argc, char **argv[])
clapper_app_bus_initialize ();
clapper_features_bus_initialize ();
#if CLAPPER_WITH_ENHANCERS_LOADER
clapper_enhancers_loader_initialize ();
#endif
gst_plugin_register_static (
GST_VERSION_MAJOR,
GST_VERSION_MINOR,
PACKAGE "internal",
PLUGIN_DESC,
(GstPluginInitFunc) clapper_gst_plugin_init,
PACKAGE_VERSION,
PLUGIN_LICENSE,
PACKAGE,
PACKAGE,
PACKAGE_ORIGIN);
is_initialized = TRUE;
finish:
@@ -92,3 +115,52 @@ clapper_init_check (int *argc, char **argv[])
{
return clapper_init_check_internal (argc, argv);
}
/**
* clapper_enhancer_check:
* @iface_type: an interface #GType
* @scheme: an URI scheme
* @host: (nullable): an URI host
* @name: (out) (optional) (transfer none): return location for found enhancer name
*
* Check if an enhancer of @type is available for given @scheme and @host.
*
* A check that compares requested capabilites of all available Clapper enhancers,
* thus it is fast but does not guarantee that the found one will succeed. Please note
* that this function will always return %FALSE if Clapper was built without enhancers
* loader functionality. To check that, use [const@Clapper.WITH_ENHANCERS_LOADER].
*
* This function can be used to quickly determine early if Clapper will at least try to
* handle URI and with one of its enhancers and which one.
*
* Example:
*
* ```c
* gboolean supported = clapper_enhancer_check (CLAPPER_TYPE_EXTRACTABLE, "https", "example.com", NULL);
* ```
*
* For self hosted services a custom URI @scheme without @host can be used. Enhancers should announce
* support for such schemes by defining them in their plugin info files.
*
* ```c
* gboolean supported = clapper_enhancer_check (CLAPPER_TYPE_EXTRACTABLE, "example", NULL, NULL);
* ```
*
* Returns: whether a plausible enhancer was found.
*
* Since: 0.8
*/
gboolean
clapper_enhancer_check (GType iface_type, const gchar *scheme, const gchar *host, const gchar **name)
{
gboolean success = FALSE;
g_return_val_if_fail (G_TYPE_IS_INTERFACE (iface_type), FALSE);
g_return_val_if_fail (scheme != NULL, FALSE);
#if CLAPPER_WITH_ENHANCERS_LOADER
success = clapper_enhancers_loader_check (iface_type, scheme, host, name);
#endif
return success;
}

View File

@@ -19,13 +19,19 @@
#pragma once
#include <glib.h>
#include <glib-object.h>
#define __CLAPPER_INSIDE__
#include <clapper/clapper-visibility.h>
#include <clapper/clapper-enums.h>
#include <clapper/clapper-version.h>
#include <clapper/clapper-audio-stream.h>
#include <clapper/clapper-feature.h>
#include <clapper/clapper-harvest.h>
#include <clapper/clapper-marker.h>
#include <clapper/clapper-media-item.h>
#include <clapper/clapper-player.h>
@@ -38,6 +44,9 @@
#include <clapper/clapper-utils.h>
#include <clapper/clapper-video-stream.h>
#include <clapper/clapper-extractable.h>
#include <clapper/clapper-functionalities-availability.h>
#include <clapper/features/clapper-features-availability.h>
#if CLAPPER_HAVE_DISCOVERER
@@ -52,10 +61,15 @@
G_BEGIN_DECLS
CLAPPER_API
void clapper_init (int *argc, char **argv[]);
CLAPPER_API
gboolean clapper_init_check (int *argc, char **argv[]);
CLAPPER_API
gboolean clapper_enhancer_check (GType iface_type, const gchar *scheme, const gchar *host, const gchar **name);
G_END_DECLS
#undef __CLAPPER_INSIDE__

View File

@@ -25,6 +25,8 @@
#include <glib.h>
#include <glib-object.h>
#include <clapper/clapper-visibility.h>
#include <clapper/clapper-feature.h>
G_BEGIN_DECLS
@@ -32,12 +34,16 @@ G_BEGIN_DECLS
#define CLAPPER_TYPE_DISCOVERER (clapper_discoverer_get_type())
#define CLAPPER_DISCOVERER_CAST(obj) ((ClapperDiscoverer *)(obj))
CLAPPER_API
G_DECLARE_FINAL_TYPE (ClapperDiscoverer, clapper_discoverer, CLAPPER, DISCOVERER, ClapperFeature)
CLAPPER_API
ClapperDiscoverer * clapper_discoverer_new (void);
CLAPPER_API
void clapper_discoverer_set_discovery_mode (ClapperDiscoverer *discoverer, ClapperDiscovererDiscoveryMode mode);
CLAPPER_API
ClapperDiscovererDiscoveryMode clapper_discoverer_get_discovery_mode (ClapperDiscoverer *discoverer);
G_END_DECLS

View File

@@ -14,7 +14,7 @@ clapper_possible_features = [
foreach feature_name : clapper_possible_features
subdir(feature_name)
features_availability_conf.set(
'CLAPPER_HAVE_@0@'.format(feature_name.to_upper()),
'CLAPPER_HAVE_@0@'.format(feature_name.replace('-', '_').to_upper()),
clapper_available_features.contains(feature_name) ? 'TRUE' : 'FALSE'
)
endforeach

Some files were not shown because too many files have changed in this diff Show More