Compare commits
389 Commits
gstplaylis
...
experiment
Author | SHA1 | Date | |
---|---|---|---|
|
08216528cf | ||
|
44db8c0d3b | ||
|
6c5753db53 | ||
|
a25bfa5418 | ||
|
aaa3f6c4da | ||
|
845de1db8b | ||
|
301e1f2ade | ||
|
ff5091e7d3 | ||
|
0d74fbb3e6 | ||
|
9d6ec43b0a | ||
|
cbc3061714 | ||
|
d8b02824f3 | ||
|
dd255dd337 | ||
|
447c6f30d0 | ||
|
afa06d4579 | ||
|
b04bf11c42 | ||
|
939f75763d | ||
|
d4c44049ca | ||
|
11c27eccaa | ||
|
823cb0a894 | ||
|
b27db1ac22 | ||
|
564c6e7299 | ||
|
6f4632d940 | ||
|
23bb253ca2 | ||
|
8ebf4e3b2f | ||
|
04a028d689 | ||
|
0920914b89 | ||
|
2343e84a41 | ||
|
c288223321 | ||
|
2ba6645462 | ||
|
adcbb5d827 | ||
|
9885a2f424 | ||
|
66b2d8c7de | ||
|
2c5d65d0b3 | ||
|
e37f2a0a30 | ||
|
387bc7a2f3 | ||
|
495a59a07a | ||
|
e5723cd25a | ||
|
e7808df7cc | ||
|
8f22b36045 | ||
|
dc257846ce | ||
|
af77873205 | ||
|
5289b2f0a4 | ||
|
87b3fbc15a | ||
|
59ff5bc006 | ||
|
91efc4ecdb | ||
|
713449b4ab | ||
|
3f7ba0674f | ||
|
5b89879031 | ||
|
2385af9b94 | ||
|
54e4644236 | ||
|
3c248250f3 | ||
|
51619cbd2a | ||
|
f9e84ac99b | ||
|
1bd371dabd | ||
|
5f6c0922c0 | ||
|
041deb559c | ||
|
fce8ec59b8 | ||
|
306505dc4d | ||
|
60234419c2 | ||
|
c395c6b9be | ||
|
8301cded2f | ||
|
34386bc96c | ||
|
2b85afc03e | ||
|
bb56efa705 | ||
|
82922e51f9 | ||
|
ca64d9d2ee | ||
|
2a18c6a374 | ||
|
af6cf01c98 | ||
|
85bdbb1132 | ||
|
8e89b0d8ca | ||
|
4e3cc3e23f | ||
|
c6c43346fa | ||
|
be58af2256 | ||
|
343eef5894 | ||
|
04657017f9 | ||
|
1cdaa36559 | ||
|
f351982067 | ||
|
ae531db2b7 | ||
|
66fb8d4a1f | ||
|
8f0ad12795 | ||
|
908a388f68 | ||
|
7f33db4159 | ||
|
288b829139 | ||
|
aa6d21973c | ||
|
e506063409 | ||
|
e679430d3e | ||
|
4b93d4d132 | ||
|
b7b99c20cf | ||
|
5775738f67 | ||
|
7159459160 | ||
|
00650a596f | ||
|
6a995e2143 | ||
|
01b3feb213 | ||
|
5b9e7eacba | ||
|
b37ab432d7 | ||
|
0410c6e9b5 | ||
|
3e0a0e0555 | ||
|
5b7b7085e4 | ||
|
044710f97e | ||
|
b83d500352 | ||
|
3fadf13125 | ||
|
f8fe49a809 | ||
|
264f0abb64 | ||
|
b330aa1ccd | ||
|
de8ecb8f82 | ||
|
a2bb927502 | ||
|
df728f383b | ||
|
c80d2e9fd6 | ||
|
5a00301935 | ||
|
46b5a6df96 | ||
|
f771e0320c | ||
|
72a64a41d9 | ||
|
2818d3c91b | ||
|
ffb481b52b | ||
|
c5c289d466 | ||
|
3074051b3d | ||
|
980e1d9e1a | ||
|
045e4fc2c4 | ||
|
4f27739287 | ||
|
52aa7710dc | ||
|
0e49eaec7e | ||
|
62a923a3c1 | ||
|
f59b937316 | ||
|
8c914d0555 | ||
|
d2fab16093 | ||
|
67e877af0f | ||
|
c825649a71 | ||
|
5101fce5a7 | ||
|
e0daf8435a | ||
|
9f18295728 | ||
|
8ce977505e | ||
|
8ac839c9aa | ||
|
8fa2036265 | ||
|
1d5bb1e6aa | ||
|
9ec87c1b58 | ||
|
16c0f8baae | ||
|
c94d21fc53 | ||
|
159f96c984 | ||
|
96ad4fa3db | ||
|
b2e7bef8d4 | ||
|
40a1dc6960 | ||
|
c4bd604e17 | ||
|
83c0e3b598 | ||
|
54715023c0 | ||
|
95046ceba8 | ||
|
b189e24b5e | ||
|
cb9e2ac979 | ||
|
545d8826e0 | ||
|
c2160198b9 | ||
|
aa7eae2417 | ||
|
3192a32845 | ||
|
706c783498 | ||
|
8ba3ca4af6 | ||
|
38efa7ab9f | ||
|
d19ddbcaca | ||
|
615f1553fb | ||
|
09c9e7560b | ||
|
f40ce756ad | ||
|
23d51be185 | ||
|
0db2a4a045 | ||
|
469c06d22b | ||
|
3feaf225b5 | ||
|
fc8d881efd | ||
|
ac76836fd7 | ||
|
f6a1aaf1dc | ||
|
000dca82d9 | ||
|
7a508fef39 | ||
|
d465d9f150 | ||
|
5e4dfb322c | ||
|
0c561ab4b3 | ||
|
46ce261524 | ||
|
50aac8cdd8 | ||
|
810aea476f | ||
|
24905f1d60 | ||
|
82e3c9a52f | ||
|
654b8aaf60 | ||
|
3c0e33e4a4 | ||
|
d2df1c3bd8 | ||
|
af24073590 | ||
|
44cee14eb2 | ||
|
b853685dd4 | ||
|
15461dd38a | ||
|
1c1989bc32 | ||
|
e9c9ae073f | ||
|
22d2c40b0f | ||
|
5301c8b368 | ||
|
e910978590 | ||
|
3b99f0ee0c | ||
|
f93a0fed98 | ||
|
6b8c35b16e | ||
|
5fdb6e64be | ||
|
60d4b5aec1 | ||
|
f5258889cf | ||
|
31eddf5dd2 | ||
|
89f7304e0e | ||
|
3c918bda73 | ||
|
2a8ef2559f | ||
|
7676e18eee | ||
|
00b05b460d | ||
|
62f02cf2a3 | ||
|
9fc1d9b8c4 | ||
|
45a5ebb4b6 | ||
|
2500a42286 | ||
|
bafffa59b9 | ||
|
201cb6f64c | ||
|
636098182b | ||
|
5442b0b70a | ||
|
67d1b9f5bc | ||
|
a62483fda9 | ||
|
c825385a8f | ||
|
02a7419d66 | ||
|
e727b8fb50 | ||
|
3cbfd1a57c | ||
|
75b728457c | ||
|
b293a84df6 | ||
|
928244dc32 | ||
|
11caafe847 | ||
|
dbeb4cb0b7 | ||
|
3e04139288 | ||
|
c41b77a14b | ||
|
3c500e0be9 | ||
|
6cddb1a16d | ||
|
b5007d12a1 | ||
|
0d7274fb03 | ||
|
94be4c08c5 | ||
|
bfcc468528 | ||
|
98d2b80103 | ||
|
fa39c45773 | ||
|
b2a6e19fe1 | ||
|
e8c44dc400 | ||
|
5cc9c17632 | ||
|
35d359daf9 | ||
|
637fa75484 | ||
|
8733610a9b | ||
|
873e724bf2 | ||
|
7aa03f3e72 | ||
|
99acd36cc0 | ||
|
cd7d99606d | ||
|
574e084383 | ||
|
d49df86397 | ||
|
3483c94913 | ||
|
a99abc652a | ||
|
a9173bb481 | ||
|
a16444cefb | ||
|
65673dbf4f | ||
|
dea67e4712 | ||
|
3c765e9f17 | ||
|
a77eb5352e | ||
|
ca168f34ed | ||
|
d0304d4ded | ||
|
cd479f3bb4 | ||
|
70942ba419 | ||
|
ad0c6151a9 | ||
|
cccc584961 | ||
|
38e721be03 | ||
|
cf6d41b725 | ||
|
b68c1a62b2 | ||
|
dc25ec976a | ||
|
914292612b | ||
|
e34b164f5a | ||
|
2e0a455f92 | ||
|
3aab01d35c | ||
|
e264304c9d | ||
|
a145890f9d | ||
|
0323c816ad | ||
|
4f1d9be423 | ||
|
82557df7c4 | ||
|
c457158541 | ||
|
451711b9c6 | ||
|
bbada8fb4e | ||
|
04122d46a7 | ||
|
347d90b1ad | ||
|
27ffb65d8f | ||
|
9328a7af75 | ||
|
1f6e439632 | ||
|
97c1207b40 | ||
|
833559a208 | ||
|
9a8655b0a2 | ||
|
d2a03d40a3 | ||
|
66998ff6c6 | ||
|
7cdd905d73 | ||
|
10eb5102c1 | ||
|
9e8854c5e2 | ||
|
16a9546c97 | ||
|
2686c2ca03 | ||
|
ad563d16d0 | ||
|
12ce95b664 | ||
|
2dbabe52bb | ||
|
2c35ceb35a | ||
|
5eac1aa675 | ||
|
0e6a68665d | ||
|
86fe1628c3 | ||
|
a427817e53 | ||
|
7a767c34eb | ||
|
a7298e371e | ||
|
f6393a177d | ||
|
45decb8563 | ||
|
2e12e52730 | ||
|
ffbd8c35e1 | ||
|
01277f4a71 | ||
|
a54c3e4bcc | ||
|
17b2aec170 | ||
|
75bc0d6b69 | ||
|
edef26fc0e | ||
|
0edab83f86 | ||
|
9681d0eea1 | ||
|
b6559ee50f | ||
|
ce183595cd | ||
|
71f50ba414 | ||
|
0de8ee2d54 | ||
|
8be49f25b4 | ||
|
69bef3d174 | ||
|
d429e9105c | ||
|
a1ce04edfa | ||
|
8ca4c30beb | ||
|
9b53a3e387 | ||
|
f31682624f | ||
|
5874db45ea | ||
|
87a0dd473e | ||
|
c50df5f2a3 | ||
|
a72a8057e5 | ||
|
f29ec78b92 | ||
|
10f54c7c3d | ||
|
2b91056543 | ||
|
2656762cbc | ||
|
f5ff59f786 | ||
|
e4fe7844c4 | ||
|
b7d9a22d33 | ||
|
c8a5277908 | ||
|
62fab289b7 | ||
|
56ca13b370 | ||
|
ce46905319 | ||
|
5fe5f89e5d | ||
|
9ba6f5d09e | ||
|
4c0fc1dac2 | ||
|
13a792c0a9 | ||
|
02b0d5f03f | ||
|
b577dd762d | ||
|
d798539dce | ||
|
19e159d1bd | ||
|
c3cdabb050 | ||
|
c53cde4d78 | ||
|
b308ae367f | ||
|
b2005fd34f | ||
|
a6dfeeca37 | ||
|
90844e49ad | ||
|
eb60d036db | ||
|
6e20a5fa12 | ||
|
cf370961b4 | ||
|
6db36d6ac2 | ||
|
4738673f7f | ||
|
1004000ba2 | ||
|
e40f11def5 | ||
|
5378facb46 | ||
|
c6b252f261 | ||
|
0f1447b14e | ||
|
cfd6e1bd23 | ||
|
0cb89d3a84 | ||
|
3bf636d46b | ||
|
8f79a76a51 | ||
|
0efe9144d8 | ||
|
2e776ddf7b | ||
|
2eea3b05b1 | ||
|
e647cdc8c6 | ||
|
1f3ddab566 | ||
|
a38480c46b | ||
|
562587846a | ||
|
c6cd829e7b | ||
|
9b31581aac | ||
|
5018b3a28a | ||
|
bad836cc6d | ||
|
e1845a212f | ||
|
f86e32ed88 | ||
|
af2c74a4b1 | ||
|
1c414ef36b | ||
|
11e89dab26 | ||
|
37d2f7aebd | ||
|
f2ac3b20a3 | ||
|
ade60b93a4 | ||
|
48bc96f074 | ||
|
0d9cb91705 | ||
|
21ccab1cc2 | ||
|
bea3b1670d | ||
|
0d4d3f1a8c | ||
|
fc525ffcb1 | ||
|
6f1a5626bc | ||
|
fbe6a8804c | ||
|
c0b92c190b |
4
.gitattributes
vendored
@@ -1,5 +1,3 @@
|
||||
extras/**/* linguist-vendored
|
||||
lib/**/* linguist-vendored
|
||||
lib/**/**/* linguist-vendored
|
||||
lib/gst/clapper/* linguist-vendored
|
||||
lib/gst/clapper/gstclapper-mpris* linguist-vendored=false
|
||||
lib/gst/clapper/gtk4/* linguist-vendored=false
|
||||
|
42
.github/workflows/flatpak-nightly.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
name: "Flatpak Nightly"
|
||||
jobs:
|
||||
flatpak:
|
||||
name: "Flatpak"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 600
|
||||
container:
|
||||
image: bilelmoussaoui/flatpak-github-actions:gnome-nightly
|
||||
options: --privileged
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x86_64, aarch64]
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install Docker
|
||||
run: |
|
||||
dnf -y install docker
|
||||
- name: Setup QEMU
|
||||
id: qemu
|
||||
uses: docker/setup-qemu-action@v1
|
||||
with:
|
||||
platforms: arm64
|
||||
- name: Prepare Runtime
|
||||
run: |
|
||||
flatpak --system install -y --noninteractive flathub org.freedesktop.Sdk.Extension.rust-nightly/${{ matrix.arch }}/22.08
|
||||
flatpak --system install -y --noninteractive flathub org.freedesktop.Sdk.Extension.llvm14/${{ matrix.arch }}/22.08
|
||||
- uses: bilelmoussaoui/flatpak-github-actions/flatpak-builder@v4
|
||||
name: Build
|
||||
with:
|
||||
bundle: com.github.rafostar.Clapper.flatpak
|
||||
manifest-path: pkgs/flatpak/com.github.rafostar.Clapper-nightly.json
|
||||
repository-name: gnome-nightly
|
||||
repository-url: https://nightly.gnome.org/gnome-nightly.flatpakrepo
|
||||
cache-key: flatpak-builder-${{ github.sha }}-testing-${{ github.run_number }}
|
||||
arch: ${{ matrix.arch }}
|
40
.github/workflows/flatpak.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
name: "Flatpak"
|
||||
jobs:
|
||||
flatpak:
|
||||
name: "Flatpak"
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 600
|
||||
container:
|
||||
image: bilelmoussaoui/flatpak-github-actions:gnome-43
|
||||
options: --privileged
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x86_64, aarch64]
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install Docker
|
||||
run: |
|
||||
dnf -y install docker
|
||||
- name: Setup QEMU
|
||||
id: qemu
|
||||
uses: docker/setup-qemu-action@v1
|
||||
with:
|
||||
platforms: arm64
|
||||
- uses: bilelmoussaoui/flatpak-github-actions/flatpak-builder@v4
|
||||
name: "Build"
|
||||
with:
|
||||
bundle: com.github.rafostar.Clapper.flatpak
|
||||
manifest-path: pkgs/flatpak/com.github.rafostar.Clapper.json
|
||||
cache-key: flatpak-builder-${{ github.sha }}
|
||||
arch: ${{ matrix.arch }}
|
10
.obs/workflows.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
rebuild_master:
|
||||
steps:
|
||||
- trigger_services:
|
||||
project: home:Rafostar
|
||||
package: clapper
|
||||
filters:
|
||||
event: push
|
||||
branches:
|
||||
only:
|
||||
- master
|
59
README.md
@@ -1,6 +1,12 @@
|
||||
# Clapper
|
||||
A GNOME media player build using [GJS](https://gitlab.gnome.org/GNOME/gjs) with [GTK4](https://www.gtk.org) toolkit.
|
||||
The media player is using [GStreamer](https://gstreamer.freedesktop.org/) as a media backend and renders everything via [OpenGL](https://www.opengl.org).
|
||||
[](https://github.com/Rafostar/clapper/actions/workflows/flatpak.yml)
|
||||
[](https://github.com/Rafostar/clapper/actions/workflows/flatpak-nightly.yml)
|
||||
[](https://crowdin.com/project/clapper)
|
||||
[](https://matrix.to/#/#clapper-player:matrix.org)
|
||||
[](https://liberapay.com/Clapper)
|
||||
|
||||
A GNOME media player built using [GJS](https://gitlab.gnome.org/GNOME/gjs) with [GTK4](https://www.gtk.org) toolkit.
|
||||
The media player uses [GStreamer](https://gstreamer.freedesktop.org/) as a media backend and renders everything via [OpenGL](https://www.opengl.org).
|
||||
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/wiki/Rafostar/clapper/media/screenshot-windowed.png"><br>
|
||||
@@ -23,49 +29,42 @@ The media player is using [GStreamer](https://gstreamer.freedesktop.org/) as a m
|
||||
* [Adaptive UI](https://raw.githubusercontent.com/wiki/Rafostar/clapper/media/screenshot-mobile.png)
|
||||
* [Playlist from file](https://github.com/Rafostar/clapper/wiki/Playlists)
|
||||
* Chapters on progress bar
|
||||
* MPRIS support
|
||||
|
||||
## Installation from Flatpak
|
||||
The `Flatpak` package includes all required dependencies and codecs.
|
||||
Additionally it also has a few patches, thus some functionalities work better (or are only available) in `Flatpak` version (until my changes are accepted upstream). List of patches used in this version can be found [here](https://github.com/Rafostar/clapper/issues/35).
|
||||
Additionally it also has a few patches, thus some functionalities work better (or are only available) in `Flatpak` version (until my changes are accepted upstream).
|
||||
List of patches used in this version can be found [here](https://github.com/Rafostar/clapper/issues/35).
|
||||
|
||||
<a href='https://flathub.org/apps/details/com.github.rafostar.Clapper'><img width='240' alt='Download on Flathub' src='https://flathub.org/assets/badges/flathub-badge-en.png'/></a>
|
||||
<a href='https://flathub.org/apps/details/com.github.rafostar.Clapper'>
|
||||
<img width='240' alt='Download on Flathub' src='https://flathub.org/assets/badges/flathub-badge-en.png'/>
|
||||
</a>
|
||||
|
||||
**Important:** If you have been using the flatpak package from my custom 3rd party repo, please remove it and replace your installation with version from Flathub. That repository will not be maintained any longer. Thank you for understanding.
|
||||
## Packages in Linux Distributions
|
||||
[](https://repology.org/project/clapper/versions)
|
||||
|
||||
## Packages
|
||||
The [pkgs folder](https://github.com/Rafostar/clapper/tree/master/pkgs) in this repository contains build scripts for various package formats. You can use them to build package yourself or download one of pre-built packages:
|
||||
Pre-built RPM packages are also available in [my repo](https://software.opensuse.org//download.html?project=home%3ARafostar&package=clapper) ([see status](https://build.opensuse.org/package/show/home:Rafostar/clapper)).<br>
|
||||
Those are automatically built on each git commit, thus are considered unstable.
|
||||
|
||||
#### Debian, Fedora & openSUSE
|
||||
Pre-built packages are available in [my repo](https://software.opensuse.org//download.html?project=home%3ARafostar&package=clapper) ([see status](https://build.opensuse.org/package/show/home:Rafostar/clapper)).<br>
|
||||
Those are automatically build on each git commit, thus are considered unstable.
|
||||
|
||||
#### Arch Linux
|
||||
You can get Clapper from the AUR:
|
||||
* [clapper](https://aur.archlinux.org/packages/clapper) (stable version)
|
||||
* [clapper-git](https://aur.archlinux.org/packages/clapper-git)
|
||||
|
||||
## Installation from source code
|
||||
## Installation from Source Code
|
||||
```sh
|
||||
meson builddir --prefix=/usr/local
|
||||
sudo meson install -C builddir
|
||||
```
|
||||
|
||||
## Q&A
|
||||
**Q:** Does using `GJS` negatively impact video performance?<br>
|
||||
**A:** Absolutely not. `GJS` here is used to put together the GUI during startup.
|
||||
It has nothing to do with video rendering. All used `GTK4` and `GStreamer` libraries are in C.
|
||||
Even the custom video widget that I prepared for this player (based on original `GTK3` implementation) is a compiled C code.
|
||||
All these libs are acting "on their own" and no function calls from `GJS` related to video decoding and rendering are performed during playback.
|
||||
|
||||
**Q:** What settings should I set to maximize performance?<br>
|
||||
**A:** As of now, player works best on `Wayland` session. `Wayland` users can try enabling highly experimental `vah264dec` plugin for improved performance (this plugin does not work on `Xorg` right now) for standard (8-bit) `H.264` videos.
|
||||
It can be enabled from inside player preferences dialog inside `Advanced -> GStreamer` tab using customizable `Plugin Ranking` feature.
|
||||
Since the whole app is rendered using your GPU, users of VERY weak GPUs might want to disable the "render window shadows" option to have more GPU power available for non-fullscreen video rendering.
|
||||
|
||||
## Other Questions?
|
||||
## Questions?
|
||||
Feel free to ask me any questions. Come and talk on Matrix: [#clapper-player:matrix.org](https://matrix.to/#/#clapper-player:matrix.org)
|
||||
|
||||
## Translations
|
||||
Preferred translation method is to use [Clapper Crowdin](https://crowdin.com/project/clapper) web page.
|
||||
|
||||
Crowdin does not require any additional tools and translating can be done through web browser.
|
||||
You can login using GitHub account or create a new one. Only I can add new languages to this project,
|
||||
so if your language is not available, please contact me first.
|
||||
|
||||
## Special Thanks
|
||||
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.
|
||||
|
||||
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.
|
||||
|
6
TODO.md
@@ -40,7 +40,7 @@
|
||||
- [X] Remote playback controls via HTTP (VLC) + WebSockets
|
||||
- [ ] Expand available API
|
||||
- [ ] API documentation
|
||||
- [ ] Integration with the top bar
|
||||
- [ ] MPRIS support
|
||||
- [ ] Controls in the notifications panel
|
||||
- [X] Integration with the top bar
|
||||
- [X] MPRIS support
|
||||
- [X] Controls in the notifications panel
|
||||
- [ ] Progress bar in the notifications panel (maybe via extension)
|
||||
|
1
_service
@@ -4,7 +4,6 @@
|
||||
<param name="url">https://github.com/Rafostar/clapper.git</param>
|
||||
<param name="extract">pkgs/rpm/clapper.spec</param>
|
||||
<param name="extract">pkgs/rpm/clapper.rpmlintrc</param>
|
||||
<param name="extract">pkgs/deb/clapper.dsc</param>
|
||||
</service>
|
||||
<service name="tar" mode="buildtime"/>
|
||||
<service name="recompress" mode="buildtime">
|
||||
|
@@ -1,17 +0,0 @@
|
||||
#!@GJS@
|
||||
|
||||
/* pkg init enforces the imports path to the folder
|
||||
* named after the pkg name, but I would prefer to have
|
||||
* the bundled subprocess stored in the same directory */
|
||||
imports.searchPath.unshift('@datadir@/@PACKAGE_NAME@');
|
||||
|
||||
const Package = imports.package;
|
||||
|
||||
Package.init({
|
||||
name: '@PACKAGE_NAME@.@ID_POSTFIX@',
|
||||
version: '@PACKAGE_VERSION@',
|
||||
prefix: '@prefix@',
|
||||
libdir: '@libdir@',
|
||||
datadir: '@datadir@',
|
||||
});
|
||||
Package.run(imports.src.main@ID_POSTFIX@);
|
@@ -1,12 +1,9 @@
|
||||
#!@GJS@
|
||||
|
||||
const Package = imports.package;
|
||||
|
||||
Package.init({
|
||||
imports.package.init({
|
||||
name: '@PACKAGE_NAME@',
|
||||
version: '@PACKAGE_VERSION@',
|
||||
prefix: '@prefix@',
|
||||
libdir: '@libdir@',
|
||||
datadir: '@datadir@',
|
||||
});
|
||||
Package.run(imports.src.main);
|
||||
imports.package.run(imports.src.main);
|
||||
|
@@ -1,24 +1,22 @@
|
||||
clapper_apps = ['', 'Remote', 'Daemon']
|
||||
bin_conf = configuration_data()
|
||||
|
||||
foreach id_postfix : clapper_apps
|
||||
app_postfix = (id_postfix != '') ? '.' + id_postfix : ''
|
||||
template_type = (id_postfix != '') ? '.Subprocess' : ''
|
||||
bin_conf.set('GJS', find_program('gjs').path())
|
||||
bin_conf.set('PACKAGE_NAME', meson.project_name())
|
||||
bin_conf.set('PACKAGE_VERSION', meson.project_version())
|
||||
bin_conf.set('prefix', get_option('prefix'))
|
||||
bin_conf.set('libdir', libdir)
|
||||
|
||||
bin_conf = configuration_data()
|
||||
bin_conf.set('GJS', find_program('gjs').path())
|
||||
bin_conf.set('PACKAGE_NAME', meson.project_name())
|
||||
bin_conf.set('PACKAGE_VERSION', meson.project_version())
|
||||
bin_conf.set('ID_POSTFIX', id_postfix)
|
||||
bin_conf.set('prefix', get_option('prefix'))
|
||||
bin_conf.set('libdir', join_paths(get_option('prefix'), get_option('libdir')))
|
||||
bin_conf.set('datadir', join_paths(get_option('prefix'), get_option('datadir')))
|
||||
configure_file(
|
||||
input: 'com.github.rafostar.Clapper.in',
|
||||
output: 'com.github.rafostar.Clapper',
|
||||
configuration: bin_conf,
|
||||
install: true,
|
||||
install_dir: bindir,
|
||||
install_mode: 'rwxr-xr-x'
|
||||
)
|
||||
|
||||
configure_file(
|
||||
input: 'com.github.rafostar.Clapper' + template_type + '.in',
|
||||
output: 'com.github.rafostar.Clapper' + app_postfix,
|
||||
configuration: bin_conf,
|
||||
install: true,
|
||||
install_dir: get_option('bindir'),
|
||||
install_mode: 'rwxr-xr-x'
|
||||
)
|
||||
endforeach
|
||||
clapper_symlink_cmd = 'ln -fs @0@ $DESTDIR@1@'.format(
|
||||
'com.github.rafostar.Clapper',
|
||||
join_paths(bindir, 'clapper')
|
||||
)
|
||||
meson.add_install_script('sh', '-c', clapper_symlink_cmd)
|
||||
|
3
crowdin.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
files:
|
||||
- source: /po/com.github.rafostar.Clapper.pot
|
||||
translation: /po/%osx_locale%.po
|
262
css/styles.css
@@ -5,19 +5,70 @@ scale marks {
|
||||
radio {
|
||||
margin-left: -2px;
|
||||
}
|
||||
scrolledwindow scrollbar.vertical slider {
|
||||
min-height: 16px;
|
||||
}
|
||||
|
||||
/* Consistent scales color */
|
||||
scale trough highlight {
|
||||
color: @accent_fg_color;
|
||||
background-color: @accent_bg_color;
|
||||
}
|
||||
/* Consistent radio buttons color */
|
||||
.osd radio {
|
||||
color: @accent_fg_color;
|
||||
background-color: transparent;
|
||||
background-image: none;
|
||||
}
|
||||
.osd radio:hover,
|
||||
.osd radio:checked {
|
||||
background-image: image(rgba(255,255,255,0.1));
|
||||
}
|
||||
.osd radio:active {
|
||||
background-image: image(rgba(255,255,255,0.3));
|
||||
}
|
||||
|
||||
/* Adwaita OSD background color is unacceptable:
|
||||
* https://gitlab.gnome.org/GNOME/libadwaita/-/issues/454 */
|
||||
box.osd,
|
||||
.osd popover contents,
|
||||
.osd popover arrow,
|
||||
.osdheaderbar button {
|
||||
background-color: rgba(38,38,38,0.78);
|
||||
}
|
||||
.osdheaderbar button:hover,
|
||||
.osdheaderbar button:checked {
|
||||
background-color: rgba(63,63,63,0.78);
|
||||
}
|
||||
.osdheaderbar button:active {
|
||||
background-color: rgba(82,82,82,0.78);
|
||||
}
|
||||
|
||||
/* Adwaita is missing osd ListBox */
|
||||
.osd list {
|
||||
.clapperplaylist {
|
||||
background: none;
|
||||
}
|
||||
.osd list row image {
|
||||
.clapperplaylist row {
|
||||
border-radius: 5px;
|
||||
}
|
||||
.clapperplaylist row {
|
||||
color: @theme_fg_color;
|
||||
}
|
||||
.clapperplaylist row button {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
min-width: 28px;
|
||||
min-height: 28px;
|
||||
}
|
||||
.fullscreen.tvmode .clapperplaylist row button {
|
||||
min-width: 36px;
|
||||
min-height: 36px;
|
||||
margin-left: 2px;
|
||||
margin-right: 2px;
|
||||
}
|
||||
.osd .clapperplaylist row image {
|
||||
-gtk-icon-shadow: none;
|
||||
}
|
||||
.gtk402 trough highlight {
|
||||
border-color: transparent;
|
||||
}
|
||||
.gtk402 .osd trough highlight {
|
||||
border-color: inherit;
|
||||
}
|
||||
.osdheaderbar {
|
||||
background: transparent;
|
||||
}
|
||||
@@ -34,18 +85,20 @@ radio {
|
||||
.linkedright image {
|
||||
margin-right: 2px;
|
||||
}
|
||||
/* Non-osd style for popover menu */
|
||||
.menupopover label {
|
||||
color: @theme_text_color;
|
||||
|
||||
/* Flat popovers */
|
||||
popover arrow,
|
||||
popover contents {
|
||||
border-color: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
.menupopover arrow {
|
||||
background: @theme_base_color;
|
||||
border-color: @insensitive_base_color;
|
||||
}
|
||||
.menupopover contents {
|
||||
background: @theme_base_color;
|
||||
border-color: @insensitive_base_color;
|
||||
.popoverseparator separator {
|
||||
background-color: @insensitive_fg_color;
|
||||
margin-left: 3px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
/* Rounded corners */
|
||||
.adwrounded.csd {
|
||||
border-radius: 8px;
|
||||
}
|
||||
@@ -61,8 +114,15 @@ radio {
|
||||
.roundedcorners {
|
||||
border-radius: 8px;
|
||||
}
|
||||
.adwthemedark scale trough highlight {
|
||||
filter: brightness(120%);
|
||||
|
||||
/* Reduce sliders size */
|
||||
scale trough slider {
|
||||
min-height: 18px;
|
||||
min-width: 18px;
|
||||
}
|
||||
.fullscreen.tvmode scale trough slider {
|
||||
min-height: 20px;
|
||||
min-width: 20px;
|
||||
}
|
||||
|
||||
.videowidget {
|
||||
@@ -75,24 +135,29 @@ radio {
|
||||
font-size: 21px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.adwicons .playercontrols {
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
.playercontrols {
|
||||
.clappercontrols {
|
||||
margin-left: 2px;
|
||||
margin-right: 2px;
|
||||
}
|
||||
.playercontrols button {
|
||||
.fullscreen.tvmode .clappercontrols {
|
||||
margin-left: 1px;
|
||||
margin-right: 1px;
|
||||
}
|
||||
.clappercontrolsbutton {
|
||||
margin: 3px;
|
||||
margin-left: 1px;
|
||||
margin-right: 1px;
|
||||
}
|
||||
.fullscreen.tvmode .playercontrols button {
|
||||
.fullscreen.tvmode .clappercontrolsbutton {
|
||||
min-width: 32px;
|
||||
min-height: 32px;
|
||||
margin: 5px;
|
||||
margin-left: 3px;
|
||||
margin-right: 3px;
|
||||
margin-left: 4px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.clappercontrolsbutton.text-button {
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
.fullscreen.tvmode button image {
|
||||
-gtk-icon-shadow: none;
|
||||
@@ -101,28 +166,21 @@ radio {
|
||||
margin-left: 0px;
|
||||
margin-right: 4px;
|
||||
border: 2px solid;
|
||||
min-width: 17px;
|
||||
min-height: 17px;
|
||||
min-width: 16px;
|
||||
min-height: 16px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.fullscreen.tvmode .playercontrols button image {
|
||||
-gtk-icon-size: 24px;
|
||||
/* Also affects popover buttons */
|
||||
.fullscreen.tvmode .clappercontrols button image {
|
||||
-gtk-icon-size: 26px;
|
||||
}
|
||||
.adwicons .playbackicon {
|
||||
-gtk-icon-size: 20px;
|
||||
}
|
||||
.adwicons.fullscreen.tvmode .playbackicon {
|
||||
-gtk-icon-size: 28px;
|
||||
}
|
||||
.labelbuttonlabel {
|
||||
margin-left: -4px;
|
||||
margin-right: -4px;
|
||||
min-width: 8px;
|
||||
.clappercontrolsbutton.text-button label {
|
||||
font-family: 'Cantarell', sans-serif;
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-weight: 600;
|
||||
}
|
||||
.fullscreen.tvmode .labelbuttonlabel {
|
||||
.fullscreen.tvmode .clappercontrolsbutton.text-button label {
|
||||
font-size: 22px;
|
||||
text-shadow: none;
|
||||
}
|
||||
@@ -153,29 +211,27 @@ radio {
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* Button Inside Popover */
|
||||
.popoverbutton {
|
||||
min-width: 24px;
|
||||
min-height: 24px;
|
||||
}
|
||||
|
||||
/* Position Scale */
|
||||
.positionscale {
|
||||
margin: -2px;
|
||||
margin-left: -4px;
|
||||
margin-right: -4px;
|
||||
}
|
||||
.positionscale trough highlight {
|
||||
min-height: 4px;
|
||||
}
|
||||
.osd .positionscale trough highlight {
|
||||
min-height: 6px;
|
||||
}
|
||||
.fullscreen.tvmode .positionscale {
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
.fullscreen.tvmode .positionscale.fine-tune {
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
.fullscreen.tvmode .positionscale trough slider {
|
||||
color: transparent;
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
box-shadow: none;
|
||||
outline: none;
|
||||
}
|
||||
.positionscale mark indicator {
|
||||
min-height: 6px;
|
||||
@@ -205,6 +261,9 @@ radio {
|
||||
.fullscreen.tvmode .positionscale marks.bottom {
|
||||
margin-top: 2px;
|
||||
}
|
||||
.fullscreen.tvmode .positionscale trough {
|
||||
border-radius: 3px;
|
||||
}
|
||||
.fullscreen.tvmode .positionscale trough highlight {
|
||||
border-radius: 3px;
|
||||
min-height: 20px;
|
||||
@@ -232,22 +291,33 @@ radio {
|
||||
margin-top: -4px;
|
||||
margin-bottom: -6px;
|
||||
}
|
||||
.volumescale trough highlight {
|
||||
min-width: 4px;
|
||||
}
|
||||
.fullscreen.tvmode .volumescale trough highlight {
|
||||
min-width: 6px;
|
||||
}
|
||||
.overamp trough highlight {
|
||||
background: @error_color;
|
||||
color: @error_fg_color;
|
||||
background-color: @error_bg_color;
|
||||
}
|
||||
|
||||
/* Elapsed Popover */
|
||||
.elapsedpopoverbox {
|
||||
min-width: 260px;
|
||||
.elapsedpopover {
|
||||
min-width: 326px;
|
||||
}
|
||||
.elapsedpopoverbox box separator {
|
||||
background: @insensitive_fg_color;
|
||||
.fullscreen.tvmode .elapsedpopover {
|
||||
min-width: 448px;
|
||||
}
|
||||
.fullscreen.tvmode .elapsedpopoverbox {
|
||||
min-width: 360px;
|
||||
.elapsedpopover contents {
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
.speedscale {
|
||||
margin-left: 4px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.speedscale trough highlight {
|
||||
min-height: 4px;
|
||||
}
|
||||
.fullscreen.tvmode .speedscale trough highlight {
|
||||
min-height: 6px;
|
||||
@@ -279,26 +349,9 @@ radio {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Preferences */
|
||||
.prefsnotebook grid {
|
||||
margin: 10px;
|
||||
}
|
||||
.prefssubpage header {
|
||||
background: none;
|
||||
}
|
||||
.prefssubpage header tabs tab {
|
||||
box-shadow: none;
|
||||
margin: 0px;
|
||||
margin-right: 1px;
|
||||
}
|
||||
.prefssubpage header tabs tab:checked {
|
||||
color: initial;
|
||||
background: @theme_selected_bg_color;
|
||||
}
|
||||
|
||||
/* Open URI Dialog */
|
||||
.uridialogbox {
|
||||
margin: 12px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
/* Tweaks */
|
||||
@@ -309,7 +362,7 @@ radio {
|
||||
border: none;
|
||||
}
|
||||
.controlsbox {
|
||||
background: @theme_bg_color;
|
||||
background: @popover_bg_color;
|
||||
}
|
||||
.gpufriendly {
|
||||
box-shadow: -8px -8px transparent, 8px 8px transparent;
|
||||
@@ -322,3 +375,52 @@ radio {
|
||||
.blackbackground {
|
||||
background: black;
|
||||
}
|
||||
|
||||
/** SCALING LOW-RES **/
|
||||
.fullscreen.tvmode.lowres .clappercontrols button image {
|
||||
-gtk-icon-size: 22px;
|
||||
}
|
||||
.fullscreen.tvmode.lowres .clappercontrolsbutton {
|
||||
min-width: 28px;
|
||||
min-height: 28px;
|
||||
}
|
||||
.fullscreen.tvmode.lowres .clappercontrolsbutton.text-button label {
|
||||
font-size: 21px;
|
||||
}
|
||||
.fullscreen.tvmode.lowres .positionscale trough highlight {
|
||||
min-height: 18px;
|
||||
}
|
||||
.fullscreen.tvmode.lowres .positionscale.fine-tune trough highlight {
|
||||
min-height: 18px;
|
||||
}
|
||||
.fullscreen.tvmode.lowres popover box {
|
||||
font-size: 19px;
|
||||
}
|
||||
.fullscreen.tvmode.lowres radio {
|
||||
min-width: 15px;
|
||||
min-height: 15px;
|
||||
}
|
||||
.fullscreen.tvmode.lowres .clapperplaylist row button {
|
||||
min-width: 32px;
|
||||
min-height: 32px;
|
||||
}
|
||||
.fullscreen.tvmode.lowres .tvtitle {
|
||||
font-size: 26px;
|
||||
}
|
||||
.fullscreen.tvmode.lowres .tvtime {
|
||||
font-size: 34px;
|
||||
}
|
||||
.fullscreen.tvmode.lowres .tvendtime {
|
||||
font-size: 21px;
|
||||
}
|
||||
.fullscreen.tvmode.lowres .elapsedpopover {
|
||||
min-width: 410px;
|
||||
}
|
||||
.fullscreen.tvmode.lowres .chapterlabel {
|
||||
font-size: 21px;
|
||||
}
|
||||
|
||||
/** SCALING HI-RES **/
|
||||
.fullscreen.tvmode.hires .clappercontrols button image {
|
||||
-gtk-icon-size: 24px; /* Sharpest on 2160p with scaling 2x */
|
||||
}
|
||||
|
115
data/com.github.rafostar.Clapper-symbolic.svg
Normal file
@@ -0,0 +1,115 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 4.2333333 4.2333334"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||
sodipodi:docname="com.github.rafostar.Clapper-symbolic.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="true"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-bbox-edge-midpoints="true"
|
||||
inkscape:snap-bbox-midpoints="true"
|
||||
inkscape:object-paths="true"
|
||||
inkscape:snap-intersection-paths="true"
|
||||
inkscape:snap-smooth-nodes="true"
|
||||
inkscape:snap-midpoints="true"
|
||||
inkscape:snap-global="false"
|
||||
units="px"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="6.078125"
|
||||
inkscape:cy="8.09375"
|
||||
inkscape:window-width="1680"
|
||||
inkscape:window-height="981"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" />
|
||||
<defs
|
||||
id="defs2">
|
||||
<inkscape:path-effect
|
||||
effect="fillet_chamfer"
|
||||
id="path-effect1853"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
satellites_param="F,0,0,1,0,1.8520833,0,1 @ F,0,0,1,0,1.8520833,0,1 @ F,0,0,1,0,1.8520833,0,1 @ F,0,0,1,0,1.8520833,0,1"
|
||||
unit="px"
|
||||
method="auto"
|
||||
mode="F"
|
||||
radius="7"
|
||||
chamfer_steps="1"
|
||||
flexible="false"
|
||||
use_knot_distance="true"
|
||||
apply_no_radius="true"
|
||||
apply_with_radius="true"
|
||||
only_selected="false"
|
||||
hide_knots="false" />
|
||||
<inkscape:path-effect
|
||||
effect="fillet_chamfer"
|
||||
id="path-effect1732"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
satellites_param="F,0,0,1,0,1.8520833,0,1 @ F,0,0,1,0,1.8520833,0,1 @ F,0,0,1,0,1.8520833,0,1 @ F,0,0,1,0,1.8520833,0,1"
|
||||
unit="px"
|
||||
method="auto"
|
||||
mode="F"
|
||||
radius="7"
|
||||
chamfer_steps="1"
|
||||
flexible="false"
|
||||
use_knot_distance="true"
|
||||
apply_no_radius="true"
|
||||
apply_with_radius="true"
|
||||
only_selected="false"
|
||||
hide_knots="false" />
|
||||
</defs>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<g
|
||||
id="g2022"
|
||||
transform="matrix(0.06169519,0,0,0.06168906,-4.7800087,-3.2713603)">
|
||||
<path
|
||||
id="rect973"
|
||||
style="fill:#000000;stroke-width:1.30776;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke markers fill"
|
||||
d="m 88.193064,81.795006 c -0.699254,0 -1.342327,0.227875 -1.864484,0.609782 h 51.32503 c -0.52216,-0.381907 -1.16471,-0.609782 -1.86397,-0.609782 z m -3.157945,10.475846 v 26.225278 c 0,1.74939 1.40856,3.15743 3.157945,3.15743 h 47.596576 c 1.74939,0 3.15795,-1.40804 3.15795,-3.15743 V 92.270852 Z m 20.323311,4.964038 15.40009,9.27283 -15.5205,9.56634 z" />
|
||||
<path
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 83.974394,69.464471 7.344033,-0.06509 a 2.7923103,2.7923103 33.047712 0 1 2.587466,1.683384 l 7.009937,16.201526 a 1.2163248,1.2163248 123.30899 0 1 -1.116623,1.699322 l -15.720141,-0.004 a 1.862525,1.862525 44.853691 0 1 -1.862019,-1.852534 l -0.08473,-15.79409 a 1.8585738,1.8585738 134.59241 0 1 1.842075,-1.868472 z"
|
||||
id="path1422"
|
||||
inkscape:path-effect="#path-effect1732"
|
||||
inkscape:original-d="m 82.122383,69.480886 11.048055,-0.09792 8.480852,19.601124 -19.424307,-0.005 z" />
|
||||
<rect
|
||||
style="fill:#000000;stroke-width:1.3;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke markers fill"
|
||||
id="rect1544"
|
||||
width="59.366463"
|
||||
height="9.8661175"
|
||||
x="82"
|
||||
y="79.292183"
|
||||
ry="1.2306831" />
|
||||
<path
|
||||
id="rect1847"
|
||||
style="fill:#000000;stroke-width:4.91339;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke markers fill"
|
||||
d="m 522.12695,200.42773 c -0.45798,3.8e-4 -0.92335,0.0696 -1.38476,0.21289 l -172.88672,53.70313 5.28515,-0.0469 a 10.55362,10.55362 0 0 1 9.7793,6.36328 l 10.69922,24.72656 158.18359,-49.13477 c 2.46089,-0.7644 3.82691,-3.36137 3.0625,-5.82226 l -8.30078,-26.72657 c -0.62108,-1.99947 -2.4529,-3.277 -4.4375,-3.27539 z m -203.69531,63.05469 -3.08398,0.95899 c -2.46089,0.7644 -3.82691,3.35942 -3.0625,5.82031 l 6.29101,20.2539 z"
|
||||
transform="scale(0.26458333)" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.8 KiB |
9
data/com.github.rafostar.Clapper.data.gresource.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gresources>
|
||||
<gresource prefix="/com/github/rafostar/Clapper">
|
||||
<file preprocess="xml-stripblanks" alias="icons/scalable/actions/play-symbolic.svg">icons/play-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks" alias="icons/scalable/actions/pause-symbolic.svg">icons/pause-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks" alias="icons/scalable/actions/pip-in-symbolic.svg">icons/pip-in-symbolic.svg</file>
|
||||
<file preprocess="xml-stripblanks" alias="icons/scalable/actions/pip-out-symbolic.svg">icons/pip-out-symbolic.svg</file>
|
||||
</gresource>
|
||||
</gresources>
|
@@ -6,6 +6,8 @@ Categories=GTK;GNOME;AudioVideo;Player;Video;TV;
|
||||
MimeType=application/claps;application/mpeg4-iod;application/mpeg4-muxcodetable;application/mxf;application/ogg;application/ram;application/sdp;application/streamingmedia;application/vnd.apple.mpegurl;application/vnd.ms-asf;application/vnd.rn-realmedia;application/vnd.rn-realmedia-vbr;application/x-extension-m4a;application/x-extension-mp4;application/x-flac;application/x-flash-video;application/x-matroska;application/x-ogg;application/x-streamingmedia;audio/3gpp;audio/3gpp2;audio/aac;audio/ac3;audio/amr;audio/amr-wb;audio/basic;audio/dv;audio/eac3;audio/flac;audio/m4a;audio/midi;audio/mp1;audio/mp2;audio/mp3;audio/mp4;audio/mpeg;audio/mpegurl;audio/mpg;audio/ogg;audio/opus;audio/scpls;audio/vnd.dolby.heaac.1;audio/vnd.dolby.heaac.2;audio/vnd.dolby.mlp;audio/vnd.dts;audio/vnd.dts.hd;audio/vnd.rn-realaudio;audio/wav;audio/webm;audio/x-aac;audio/x-aiff;audio/x-ape;audio/x-flac;audio/x-gsm;audio/x-it;audio/x-m4a;audio/x-matroska;audio/x-mod;audio/x-mp1;audio/x-mp2;audio/x-mp3;audio/x-mpeg;audio/x-mpegurl;audio/x-mpg;audio/x-ms-asf;audio/x-ms-wma;audio/x-musepack;audio/x-pn-aiff;audio/x-pn-au;audio/x-pn-realaudio;audio/x-pn-wav;audio/x-real-audio;audio/x-realaudio;audio/x-s3m;audio/x-scpls;audio/x-shorten;audio/x-speex;audio/x-tta;audio/x-vorbis;audio/x-vorbis+ogg;audio/x-wav;audio/x-wavpack;audio/x-xm;video/3gp;video/3gpp;video/3gpp2;video/divx;video/dv;video/fli;video/flv;video/mp2t;video/mp4;video/mp4v-es;video/mpeg;video/mpeg-system;video/msvideo;video/ogg;video/quicktime;video/vnd.mpegurl;video/vnd.rn-realvideo;video/webm;video/x-avi;video/x-flc;video/x-fli;video/x-flv;video/x-m4v;video/x-matroska;video/x-mpeg;video/x-mpeg-system;video/x-mpeg2;video/x-ms-asf;video/x-ms-wm;video/x-ms-wmv;video/x-ms-wmx;video/x-msvideo;video/x-nsv;video/x-ogm+ogg;video/x-theora;video/x-theora+ogg;x-content/audio-cdda;x-content/audio-player;x-content/video-dvd;x-scheme-handler/mms;x-scheme-handler/mmsh;x-scheme-handler/rtmp;x-scheme-handler/rtp;x-scheme-handler/rtsp;
|
||||
Exec=com.github.rafostar.Clapper %U
|
||||
Icon=com.github.rafostar.Clapper
|
||||
DBusActivatable=true
|
||||
StartupNotify=true
|
||||
Terminal=false
|
||||
Type=Application
|
||||
# Translators: Search terms to find this application. Do NOT translate the semicolons!
|
||||
|
@@ -6,34 +6,34 @@
|
||||
<default>false</default>
|
||||
<summary>Automatically enter fullscreen when first file is loaded</summary>
|
||||
</key>
|
||||
<key name="volume-initial" type="s">
|
||||
<default>"restore"</default>
|
||||
<summary>Mode used for startup volume value</summary>
|
||||
<key name="volume-custom" type="b">
|
||||
<default>false</default>
|
||||
<summary>Set custom volume value at startup</summary>
|
||||
</key>
|
||||
<key name="volume-value" type="i">
|
||||
<default>100</default>
|
||||
<summary>Custom initial volume value in percentage after startup</summary>
|
||||
</key>
|
||||
<key name="close-auto" type="b">
|
||||
<default>false</default>
|
||||
<summary>Automatically close the app after playback finishes</summary>
|
||||
<key name="after-playback" type="i">
|
||||
<default>0</default>
|
||||
<summary>What to do after playback finishes</summary>
|
||||
</key>
|
||||
|
||||
<!-- Behaviour -->
|
||||
<key name="seeking-mode" type="s">
|
||||
<default>"normal"</default>
|
||||
<!-- Behavior -->
|
||||
<key name="seeking-mode" type="i">
|
||||
<default>0</default>
|
||||
<summary>Mode used for seeking</summary>
|
||||
</key>
|
||||
<key name="seeking-value" type="i">
|
||||
<default>10</default>
|
||||
<summary>Time amount to seek with single press of arrow keys</summary>
|
||||
</key>
|
||||
<key name="seeking-unit" type="s">
|
||||
<default>"second"</default>
|
||||
<summary>Unit to use with seeking value</summary>
|
||||
<key name="seeking-unit" type="i">
|
||||
<default>0</default>
|
||||
<summary>Unit ID to use with seeking value</summary>
|
||||
</key>
|
||||
<key name="resume-enabled" type="b">
|
||||
<default>true</default>
|
||||
<default>false</default>
|
||||
<summary>Ask to resume unfinished video</summary>
|
||||
</key>
|
||||
<key name="resume-database" type="s">
|
||||
@@ -46,16 +46,12 @@
|
||||
</key>
|
||||
|
||||
<!-- Audio -->
|
||||
<key name="audio-offset" type="d">
|
||||
<key name="audio-offset" type="i">
|
||||
<default>0</default>
|
||||
<summary>Offset time for audio tracks relative to video (milliseconds)</summary>
|
||||
</key>
|
||||
|
||||
<!-- Subtitles -->
|
||||
<key name="subtitle-offset" type="d">
|
||||
<default>0</default>
|
||||
<summary>Offset time for subtitle tracks relative to video (milliseconds)</summary>
|
||||
</key>
|
||||
<key name="subtitle-font" type="s">
|
||||
<default>"Sans 12"</default>
|
||||
<summary>The subtitles font description</summary>
|
||||
@@ -70,14 +66,6 @@
|
||||
<default>6446</default>
|
||||
<summary>Listening port to use for incoming WebSocket connections</summary>
|
||||
</key>
|
||||
<key name="webapp-enabled" type="b">
|
||||
<default>false</default>
|
||||
<summary>Run built-in broadway based web application</summary>
|
||||
</key>
|
||||
<key name="webapp-port" type="i">
|
||||
<default>8086</default>
|
||||
<summary>Port for running broadwayd service</summary>
|
||||
</key>
|
||||
|
||||
<!-- Tweaks -->
|
||||
<key name="dark-theme" type="b">
|
||||
@@ -91,22 +79,20 @@
|
||||
|
||||
<!-- GStreamer -->
|
||||
<key name="plugin-ranking" type="s">
|
||||
<default>'[{"apply":false,"name":"vah264dec","rank":300}]'</default>
|
||||
<default>'{}'</default>
|
||||
<summary>Custom values for GStreamer plugin ranking</summary>
|
||||
</key>
|
||||
<key name="play-flags" type="i">
|
||||
<default>1687</default>
|
||||
<summary>Set PlayFlags for playbin</summary>
|
||||
</key>
|
||||
|
||||
<!-- YouTube -->
|
||||
<key name="yt-adaptive-enabled" type="b">
|
||||
<key name="use-playbin3" type="b">
|
||||
<default>false</default>
|
||||
<summary>Enable to use adaptive streaming for YouTube</summary>
|
||||
<summary>Use playbin3 element instead of playbin2</summary>
|
||||
</key>
|
||||
<key name="yt-quality-type" type="s">
|
||||
<default>"hfr"</default>
|
||||
<summary>Max YouTube video quality type</summary>
|
||||
<key name="use-pipewire" type="b">
|
||||
<default>false</default>
|
||||
<summary>Use PipeWire for audio output</summary>
|
||||
</key>
|
||||
<key name="play-flags" type="i">
|
||||
<default>1559</default>
|
||||
<summary>Set PlayFlags for playbin</summary>
|
||||
</key>
|
||||
|
||||
<!-- Other -->
|
||||
|
@@ -9,24 +9,20 @@
|
||||
<launchable type="desktop-id">com.github.rafostar.Clapper.desktop</launchable>
|
||||
<description>
|
||||
<p>
|
||||
Clapper is a GNOME media player build using GJS with GTK4 toolkit.
|
||||
Clapper is a GNOME media player built using GJS with GTK4 toolkit.
|
||||
The media player is using GStreamer as a media backend and renders
|
||||
everything via OpenGL. Player works natively on both Xorg and Wayland.
|
||||
It also supports VA-API on AMD/Intel GPUs.
|
||||
It also supports hardware acceleration through VA-API on AMD/Intel GPUs,
|
||||
NVDEC on Nvidia and V4L2 on mobile devices.
|
||||
</p>
|
||||
<p>
|
||||
The media player has an adaptive GUI. When viewing videos in "Windowed Mode",
|
||||
Clapper will use mostly unmodified GTK widgets to match your OS look nicely.
|
||||
When player enters "Fullscreen Mode" all GUI elements will become darker, bigger
|
||||
and semi-transparent for your viewing comfort. It also has a "Floating Mode" which
|
||||
displays video only on top of all other windows for a PiP-like viewing experience.
|
||||
displays only video on top of all other windows for a PiP-like viewing experience.
|
||||
Mobile friendly transitions are also supported.
|
||||
</p>
|
||||
<p>
|
||||
For best stability Wayland session is recommended. Wayland users with AMD/Intel GPUs
|
||||
can try enabling HIGHLY EXPERIMENTAL "vah264dec" plugin inside player preferences
|
||||
for reduced CPU and GPU usage on standard (8-bit) H.264 videos.
|
||||
</p>
|
||||
</description>
|
||||
<developer_name>Rafał Dzięgiel</developer_name>
|
||||
<url type="homepage">https://rafostar.github.io/clapper</url>
|
||||
@@ -52,6 +48,133 @@
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
<releases>
|
||||
<release version="0.5.2" date="2022-06-24">
|
||||
<description>
|
||||
<p>Fixes:</p>
|
||||
<ul>
|
||||
<li>Fix time labels display on RTL languages</li>
|
||||
<li>Improved GL/GLES context automatic selection</li>
|
||||
</ul>
|
||||
<p>New translations:</p>
|
||||
<ul>
|
||||
<li>Hebrew</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="0.5.1" date="2022-05-29">
|
||||
<description>
|
||||
<p>
|
||||
A quick hotfix release. Fixes problems with new video sink on displays with non-100% scaling applied.
|
||||
See 0.5.0 version release notes for full recent changelog.
|
||||
</p>
|
||||
</description>
|
||||
</release>
|
||||
<release version="0.5.0" date="2022-05-28">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
<ul>
|
||||
<li>Includes and uses new, improved GStreamer video sink</li>
|
||||
<li>All networking ported to libsoup3</li>
|
||||
<li>A lot of cleanup, including removal of unfinished web application and old YT code</li>
|
||||
<li>App now supports D-Bus launching (DBusActivatable)</li>
|
||||
<li>Other small fixes</li>
|
||||
</ul>
|
||||
<p>New translations:</p>
|
||||
<ul>
|
||||
<li>Arabic</li>
|
||||
<li>Basque</li>
|
||||
<li>French</li>
|
||||
<li>Japanese</li>
|
||||
<li>Swedish</li>
|
||||
<li>Turkish</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="0.4.1" date="2021-12-20">
|
||||
<description>
|
||||
<p>Fixes:</p>
|
||||
<ul>
|
||||
<li>Compatibility with more recent libadwaita versions</li>
|
||||
<li>Toggle mute with M button alone</li>
|
||||
<li>Allow handling YouTube with external GStreamer plugins</li>
|
||||
<li>Fix catching errors when reading clipboard</li>
|
||||
<li>Fix missing translator-credits</li>
|
||||
<li>Fix missing gio-unix-2.0 dep</li>
|
||||
<li>Fix playback pausing when entering fullscreen with touchscreen</li>
|
||||
<li>Fix GST_PLUGIN_FEATURE_RANK env usage</li>
|
||||
<li>Fix video/audio decoder change detection</li>
|
||||
<li>Merge global video tags instead replacing them</li>
|
||||
<li>Few other misc bug fixes</li>
|
||||
</ul>
|
||||
<p>New translations:</p>
|
||||
<ul>
|
||||
<li>Chinese Simplified</li>
|
||||
<li>Czech</li>
|
||||
<li>Hungarian</li>
|
||||
<li>Portuguese</li>
|
||||
<li>Portuguese, Brazilian</li>
|
||||
<li>Russian</li>
|
||||
<li>Spanish</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="0.4.0" date="2021-09-12">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
<ul>
|
||||
<li>Now uses libadwaita</li>
|
||||
<li>New and adaptive preferences window</li>
|
||||
<li>Improved open URI dialog</li>
|
||||
<li>Few small tweaks to fullscreen UI design</li>
|
||||
<li>Show current video and audio decoders in popovers (easy way to check if HW accel is used)</li>
|
||||
<li>Enabled NVDEC hardware acceleration by default (requires Nvidia proprietary drivers)</li>
|
||||
<li>Added option to use PipeWire for audio output (experimental)</li>
|
||||
<li>Added option to use playbin3 element (experimental)</li>
|
||||
<li>New PiP icon from icon development kit</li>
|
||||
<li>Improved performance on devices running OpenGL ES</li>
|
||||
<li>Translations support</li>
|
||||
<li>Various bug fixes</li>
|
||||
</ul>
|
||||
<p>New keyboard shortcuts:</p>
|
||||
<ul>
|
||||
<li>Leave fullscreen with Escape key</li>
|
||||
<li>Toggle mute with Ctrl+M</li>
|
||||
</ul>
|
||||
<p>More touchscreen gestures:</p>
|
||||
<ul>
|
||||
<li>Toggle playback with a long press</li>
|
||||
<li>Switch playlist items via double tap on screen side</li>
|
||||
</ul>
|
||||
<p>New translations:</p>
|
||||
<ul>
|
||||
<li>Catalan</li>
|
||||
<li>Dutch</li>
|
||||
<li>German</li>
|
||||
<li>Italian</li>
|
||||
<li>Polish</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="0.3.0" date="2021-06-18">
|
||||
<description>
|
||||
<p>Changes:</p>
|
||||
<ul>
|
||||
<li>Added MPRIS support</li>
|
||||
<li>Added repeat modes: single video, whole playlist and shuffle</li>
|
||||
<li>Support opening folders with media files</li>
|
||||
<li>Append playlist items by holding Ctrl while doing Drag and Drop</li>
|
||||
<li>Improved handling of keyboard shortcuts</li>
|
||||
<li>Added more keyboard shortcuts</li>
|
||||
<li>Added window that shows available keyboard shortcuts</li>
|
||||
<li>Show black screen by default after playback (make showing last frame optional instead)</li>
|
||||
<li>Added ability to export playlist to file</li>
|
||||
<li>Improve handling of changing displays with different resolutions</li>
|
||||
<li>Added support for EGL under x11 with GTK 4.3.1 or later</li>
|
||||
<li>Added missing symbolic app icon</li>
|
||||
<li>Some misc bug fixes and code cleanups</li>
|
||||
</ul>
|
||||
</description>
|
||||
</release>
|
||||
<release version="0.2.1" date="2021-04-19">
|
||||
<description>
|
||||
<p>Player:</p>
|
||||
@@ -102,6 +225,14 @@
|
||||
</release>
|
||||
</releases>
|
||||
<content_rating type="oars-1.1" />
|
||||
<recommends>
|
||||
<control>keyboard</control>
|
||||
<control>pointing</control>
|
||||
<control>touch</control>
|
||||
</recommends>
|
||||
<requires>
|
||||
<display_length compare="ge">small</display_length>
|
||||
</requires>
|
||||
<custom>
|
||||
<value key="Purism::form_factor">workstation</value>
|
||||
<value key="Purism::form_factor">mobile</value>
|
||||
|
3
data/com.github.rafostar.Clapper.service.in
Normal file
@@ -0,0 +1,3 @@
|
||||
[D-BUS Service]
|
||||
Name=@app_id@
|
||||
Exec=@bindir@/@app_id@ --gapplication-service
|
@@ -1,29 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 256 256" width="256" height="256">
|
||||
<defs>
|
||||
<path d="M27.2 243.52C27.2 236.16 27.2 199.83 27.2 134.22C47.64 134.22 211.12 134.22 231.56 134.22C231.56 199.83 231.56 236.16 231.56 243.52C231.56 250.4 225.96 256 218.92 256C183.07 256 57.77 256 39.84 256C32.8 256 27.2 250.4 27.2 243.52Z" id="b1nGq5BrLC"></path>
|
||||
<path d="M24.32 103.65C24.32 106.22 24.32 127.02 24.32 129.58C24.32 132.78 26.72 135.18 29.92 135.18C50.41 135.18 215.08 135.18 235.72 135.18C238.76 135.18 241.32 132.78 241.32 129.58C241.32 127.02 241.32 106.22 241.32 103.65C241.32 100.45 238.76 98.05 235.72 98.05C194.59 98.05 50.41 98.05 29.92 98.05C26.72 98.05 24.32 100.45 24.32 103.65Z" id="a3jkaoNn4k"></path>
|
||||
<path d="M174.59 135.18L211.87 98.05L171.07 98.05L133.78 135.18L174.59 135.18Z" id="atpVQ8mnd"></path>
|
||||
<path d="M76.81 135.18L114.1 98.05L73.13 98.05L35.84 135.18L76.81 135.18Z" id="bMtYoNHu0"></path>
|
||||
<path d="M19.04 69.41C19.84 71.97 25.92 91.97 26.72 94.37C27.68 97.41 30.72 99.01 33.76 98.05C54.09 91.81 216.68 42.04 237 35.8C240.04 35 241.64 31.8 240.84 28.92C240.04 26.36 233.96 6.36 233.16 3.96C232.2 0.92 229.16 -0.68 226.12 0.28C185.47 12.6 43.21 56.29 22.88 62.53C19.84 63.33 18.24 66.53 19.04 69.41Z" id="b5oP0Glp4"></path>
|
||||
<path d="M176.51 54.37L129.94 29.4L169.15 17.56L215.72 42.52L176.51 54.37Z" id="lwBgev6DR"></path>
|
||||
<path d="M81.61 83.49L35.04 58.69L74.25 46.69L120.82 71.49L81.61 83.49Z" id="cUsjEMRUu"></path>
|
||||
<path d="M14.72 66.69C14.72 72.93 14.72 123.02 14.72 129.26C14.72 132.62 17.44 135.18 20.64 135.18C26.56 135.18 74.09 135.18 80.01 135.18C84.33 135.18 87.21 130.86 85.61 127.02C82.89 120.78 61.45 70.53 58.73 64.29C57.77 62.05 55.69 60.77 53.29 60.77C46.73 60.77 24 60.77 20.64 60.77C17.44 60.77 14.72 63.33 14.72 66.69Z" id="c1bcHZGXe"></path>
|
||||
<path d="M32.64 60.61C31.52 60.61 21.92 60.61 20.64 60.61C17.44 60.61 14.72 63.33 14.72 66.53C14.72 72.77 14.72 123.02 14.72 129.26C14.72 132.46 17.44 135.18 20.64 135.18C21.92 135.18 31.52 135.18 32.64 135.18C29.44 135.18 26.72 132.46 26.72 129.26C26.72 116.62 26.72 72.77 26.72 66.53C26.72 63.33 29.44 60.61 32.64 60.61Z" id="f2PtH0V1vC"></path>
|
||||
<path d="M231.56 135.18C231.56 143.82 231.56 148.46 231.56 149.42C231.56 149.42 231.56 149.42 231.56 149.42C108.98 149.42 40.8 149.42 27.2 149.42C27.2 149.42 27.2 149.42 27.2 149.42C27.2 140.94 27.2 136.14 27.2 135.18C27.2 135.18 27.2 135.18 27.2 135.18C149.78 135.18 217.96 135.18 231.56 135.18C231.56 135.18 231.56 135.18 231.56 135.18Z" id="a1SvrrkqVm"></path>
|
||||
<path d="M104.22 162.46L104.22 234.46L163.22 198.54L104.22 162.46Z" id="agXcvKqh8"></path>
|
||||
<path id="b1nGq5BrLC" d="M27.2 243.52C27.2 236.16 27.2 199.83 27.2 134.22C47.64 134.22 211.12 134.22 231.56 134.22C231.56 199.83 231.56 236.16 231.56 243.52C231.56 250.4 225.96 256 218.92 256C183.07 256 57.77 256 39.84 256C32.8 256 27.2 250.4 27.2 243.52Z"></path>
|
||||
<path id="a3jkaoNn4k" d="M24.32 103.65C24.32 106.22 24.32 127.02 24.32 129.58C24.32 132.78 26.72 135.18 29.92 135.18C50.41 135.18 215.08 135.18 235.72 135.18C238.76 135.18 241.32 132.78 241.32 129.58C241.32 127.02 241.32 106.22 241.32 103.65C241.32 100.45 238.76 98.05 235.72 98.05C194.59 98.05 50.41 98.05 29.92 98.05C26.72 98.05 24.32 100.45 24.32 103.65Z"></path>
|
||||
<path id="atpVQ8mnd" d="M174.59 135.18L211.87 98.05L171.07 98.05L133.78 135.18L174.59 135.18Z"></path>
|
||||
<path id="bMtYoNHu0" d="M76.81 135.18L114.1 98.05L73.13 98.05L35.84 135.18L76.81 135.18Z"></path>
|
||||
<path id="b5oP0Glp4" d="M19.04 69.41C19.84 71.97 25.92 91.97 26.72 94.37C27.68 97.41 30.72 99.01 33.76 98.05C54.09 91.81 216.68 42.04 237 35.8C240.04 35 241.64 31.8 240.84 28.92C240.04 26.36 233.96 6.36 233.16 3.96C232.2 0.92 229.16 -0.68 226.12 0.28C185.47 12.6 43.21 56.29 22.88 62.53C19.84 63.33 18.24 66.53 19.04 69.41Z"></path>
|
||||
<path id="lwBgev6DR" d="M176.51 54.37L129.94 29.4L169.15 17.56L215.72 42.52L176.51 54.37Z"></path>
|
||||
<path id="cUsjEMRUu" d="M81.61 83.49L35.04 58.69L74.25 46.69L120.82 71.49L81.61 83.49Z"></path>
|
||||
<path id="c1bcHZGXe" d="M14.72 66.69C14.72 72.93 14.72 123.02 14.72 129.26C14.72 132.62 17.44 135.18 20.64 135.18C26.56 135.18 74.09 135.18 80.01 135.18C84.33 135.18 87.21 130.86 85.61 127.02C82.89 120.78 61.45 70.53 58.73 64.29C57.77 62.05 55.69 60.77 53.29 60.77C46.73 60.77 24 60.77 20.64 60.77C17.44 60.77 14.72 63.33 14.72 66.69Z"></path>
|
||||
<path id="f2PtH0V1vC" d="M32.64 60.61C31.52 60.61 21.92 60.61 20.64 60.61C17.44 60.61 14.72 63.33 14.72 66.53C14.72 72.77 14.72 123.02 14.72 129.26C14.72 132.46 17.44 135.18 20.64 135.18C21.92 135.18 31.52 135.18 32.64 135.18C29.44 135.18 26.72 132.46 26.72 129.26C26.72 116.62 26.72 72.77 26.72 66.53C26.72 63.33 29.44 60.61 32.64 60.61Z"></path>
|
||||
<path id="a1SvrrkqVm" d="M231.56 135.18C231.56 143.82 231.56 148.46 231.56 149.42C231.56 149.42 231.56 149.42 231.56 149.42C108.98 149.42 40.8 149.42 27.2 149.42C27.2 149.42 27.2 149.42 27.2 149.42C27.2 140.94 27.2 136.14 27.2 135.18C27.2 135.18 27.2 135.18 27.2 135.18C149.78 135.18 217.96 135.18 231.56 135.18C231.56 135.18 231.56 135.18 231.56 135.18Z"></path>
|
||||
<path id="agXcvKqh8" d="M104.22 162.46L104.22 234.46L163.22 198.54L104.22 162.46Z"></path>
|
||||
</defs>
|
||||
<g>
|
||||
<g><use xlink:href="#b1nGq5BrLC" fill="#4747d1"></use></g>
|
||||
<g><use xlink:href="#a3jkaoNn4k" fill="#4747d1"></use></g>
|
||||
<g><use xlink:href="#atpVQ8mnd" fill="#f1f1f1"></use></g>
|
||||
<g><use xlink:href="#bMtYoNHu0" fill="#f1f1f1"></use></g>
|
||||
<g><use xlink:href="#b5oP0Glp4" fill="#4747d1"></use></g>
|
||||
<g><use xlink:href="#lwBgev6DR" fill="#f1f1f1"></use></g>
|
||||
<g><use xlink:href="#cUsjEMRUu" fill="#f1f1f1"></use></g>
|
||||
<g><use xlink:href="#c1bcHZGXe" fill="#a9a9a9"></use></g>
|
||||
<g><use xlink:href="#f2PtH0V1vC" opacity="0.2" fill="#000000"></use></g>
|
||||
<g><use xlink:href="#a1SvrrkqVm" opacity="0.2" fill="#000000"></use></g>
|
||||
<g><use xlink:href="#agXcvKqh8" fill="#f1f1f1"></use></g>
|
||||
<g><use xlink:href="#b1nGq5BrLC" fill="#4747d1" transform="matrix(1, 0, 0, 0.97, 0, 0)"></use></g>
|
||||
<g><use xlink:href="#a3jkaoNn4k" fill="#4747d1" transform="matrix(1, 0, 0, 0.97, 0, 0)"></use></g>
|
||||
<g><use xlink:href="#atpVQ8mnd" fill="#f1f1f1" transform="matrix(1, 0, 0, 0.97, 0, 0)"></use></g>
|
||||
<g><use xlink:href="#bMtYoNHu0" fill="#f1f1f1" transform="matrix(1, 0, 0, 0.97, 0, 0)"></use></g>
|
||||
<g><use xlink:href="#b5oP0Glp4" fill="#4747d1" transform="matrix(1, 0, 0, 0.97, 0, 0)"></use></g>
|
||||
<g><use xlink:href="#lwBgev6DR" fill="#f1f1f1" transform="matrix(1, 0, 0, 0.97, 0, 0)"></use></g>
|
||||
<g><use xlink:href="#cUsjEMRUu" fill="#f1f1f1" transform="matrix(1, 0, 0, 0.97, 0, 0)"></use></g>
|
||||
<g><use xlink:href="#c1bcHZGXe" fill="#a9a9a9" transform="matrix(1, 0, 0, 0.97, 0, 0)"></use></g>
|
||||
<g><use xlink:href="#f2PtH0V1vC" opacity="0.2" fill="#000000" transform="matrix(1, 0, 0, 0.97, 0, 0)"></use></g>
|
||||
<g><use xlink:href="#a1SvrrkqVm" opacity="0.2" fill="#000000" transform="matrix(1, 0, 0, 0.97, 0, 0)"></use></g>
|
||||
<g><use xlink:href="#agXcvKqh8" fill="#f1f1f1" transform="matrix(1, 0, 0, 0.97, 0, 0)"></use></g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.8 KiB |
7
data/icons/pause-symbolic.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="#2e3436">
|
||||
<path d="m 3 3 h 4 v 10 h -4 z m 0 0"/>
|
||||
<path d="m 9 3 h 4 v 10 h -4 z m 0 0"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 262 B |
112
data/icons/pip-in-symbolic.svg
Normal file
@@ -0,0 +1,112 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<filter id="a" height="100%" width="100%" x="0%" y="0%">
|
||||
<feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<mask id="b">
|
||||
<g filter="url(#a)">
|
||||
<path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.3"/>
|
||||
</g>
|
||||
</mask>
|
||||
<clipPath id="c">
|
||||
<path d="m 0 0 h 1024 v 800 h -1024 z"/>
|
||||
</clipPath>
|
||||
<mask id="d">
|
||||
<g filter="url(#a)">
|
||||
<path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/>
|
||||
</g>
|
||||
</mask>
|
||||
<clipPath id="e">
|
||||
<path d="m 0 0 h 1024 v 800 h -1024 z"/>
|
||||
</clipPath>
|
||||
<mask id="f">
|
||||
<g filter="url(#a)">
|
||||
<path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/>
|
||||
</g>
|
||||
</mask>
|
||||
<clipPath id="g">
|
||||
<path d="m 0 0 h 1024 v 800 h -1024 z"/>
|
||||
</clipPath>
|
||||
<mask id="h">
|
||||
<g filter="url(#a)">
|
||||
<path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/>
|
||||
</g>
|
||||
</mask>
|
||||
<clipPath id="i">
|
||||
<path d="m 0 0 h 1024 v 800 h -1024 z"/>
|
||||
</clipPath>
|
||||
<mask id="j">
|
||||
<g filter="url(#a)">
|
||||
<path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/>
|
||||
</g>
|
||||
</mask>
|
||||
<clipPath id="k">
|
||||
<path d="m 0 0 h 1024 v 800 h -1024 z"/>
|
||||
</clipPath>
|
||||
<mask id="l">
|
||||
<g filter="url(#a)">
|
||||
<path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/>
|
||||
</g>
|
||||
</mask>
|
||||
<clipPath id="m">
|
||||
<path d="m 0 0 h 1024 v 800 h -1024 z"/>
|
||||
</clipPath>
|
||||
<mask id="n">
|
||||
<g filter="url(#a)">
|
||||
<path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/>
|
||||
</g>
|
||||
</mask>
|
||||
<clipPath id="o">
|
||||
<path d="m 0 0 h 1024 v 800 h -1024 z"/>
|
||||
</clipPath>
|
||||
<mask id="p">
|
||||
<g filter="url(#a)">
|
||||
<path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.3"/>
|
||||
</g>
|
||||
</mask>
|
||||
<clipPath id="q">
|
||||
<path d="m 0 0 h 1024 v 800 h -1024 z"/>
|
||||
</clipPath>
|
||||
<mask id="r">
|
||||
<g filter="url(#a)">
|
||||
<path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.5"/>
|
||||
</g>
|
||||
</mask>
|
||||
<clipPath id="s">
|
||||
<path d="m 0 0 h 1024 v 800 h -1024 z"/>
|
||||
</clipPath>
|
||||
<g fill="#2e3436">
|
||||
<path d="m 1 2.007812 c -0.550781 0 -1 0.449219 -1 1 v 9 c 0 0.550782 0.449219 1 1 1 h 4 v -2 h -3 v -7 h 10 v 3 h 2 v -4 c 0 -0.550781 -0.449219 -1 -1 -1 z m 0 0" fill-opacity="0.35"/>
|
||||
<path d="m 9 10 c -0.550781 0 -1 0.449219 -1 1 v 4.007812 c 0 0.550782 0.449219 1 1 1 h 6 c 0.550781 0 1 -0.449218 1 -1 v -4.007812 c 0 -0.550781 -0.449219 -1 -1 -1 z m 1 2 h 4 v 2.007812 h -4 z m 0 0"/>
|
||||
<path d="m 3.132812 5.140625 c 0.171876 -0.164063 0.496094 -0.1875 0.757813 0.015625 l 3.109375 3.089844 v 0.753906 h -0.753906 l -3.109375 -3.089844 c -0.191407 -0.191406 -0.179688 -0.597656 -0.003907 -0.769531 z m 0 0"/>
|
||||
<path d="m 4 9 h 4 v 1 h -4 z m 0 0"/>
|
||||
<path d="m 7 6 h 1 v 4 h -1 z m 0 0"/>
|
||||
</g>
|
||||
<g clip-path="url(#c)" mask="url(#b)" transform="matrix(1 0 0 1 -580 -764)">
|
||||
<path d="m 562.460938 212.058594 h 10.449218 c -1.183594 0.492187 -1.296875 2.460937 0 3 h -10.449218 z m 0 0" fill="#2e3436"/>
|
||||
</g>
|
||||
<g clip-path="url(#e)" mask="url(#d)" transform="matrix(1 0 0 1 -580 -764)">
|
||||
<path d="m 16 632 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g clip-path="url(#g)" mask="url(#f)" transform="matrix(1 0 0 1 -580 -764)">
|
||||
<path d="m 17 631 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g clip-path="url(#i)" mask="url(#h)" transform="matrix(1 0 0 1 -580 -764)">
|
||||
<path d="m 18 634 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g clip-path="url(#k)" mask="url(#j)" transform="matrix(1 0 0 1 -580 -764)">
|
||||
<path d="m 16 634 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g clip-path="url(#m)" mask="url(#l)" transform="matrix(1 0 0 1 -580 -764)">
|
||||
<path d="m 17 635 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g clip-path="url(#o)" mask="url(#n)" transform="matrix(1 0 0 1 -580 -764)">
|
||||
<path d="m 19 635 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g clip-path="url(#q)" mask="url(#p)" transform="matrix(1 0 0 1 -580 -764)">
|
||||
<path d="m 136 660 v 7 h 7 v -7 z m 0 0" fill="#2e3436"/>
|
||||
</g>
|
||||
<g clip-path="url(#s)" mask="url(#r)" transform="matrix(1 0 0 1 -580 -764)">
|
||||
<path d="m 219 642 h 3 v 12 h -3 z m 0 0" fill="#2e3436"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.6 KiB |
112
data/icons/pip-out-symbolic.svg
Normal file
@@ -0,0 +1,112 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<filter id="a" height="100%" width="100%" x="0%" y="0%">
|
||||
<feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<mask id="b">
|
||||
<g filter="url(#a)">
|
||||
<path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.3"/>
|
||||
</g>
|
||||
</mask>
|
||||
<clipPath id="c">
|
||||
<path d="m 0 0 h 1024 v 800 h -1024 z"/>
|
||||
</clipPath>
|
||||
<mask id="d">
|
||||
<g filter="url(#a)">
|
||||
<path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/>
|
||||
</g>
|
||||
</mask>
|
||||
<clipPath id="e">
|
||||
<path d="m 0 0 h 1024 v 800 h -1024 z"/>
|
||||
</clipPath>
|
||||
<mask id="f">
|
||||
<g filter="url(#a)">
|
||||
<path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/>
|
||||
</g>
|
||||
</mask>
|
||||
<clipPath id="g">
|
||||
<path d="m 0 0 h 1024 v 800 h -1024 z"/>
|
||||
</clipPath>
|
||||
<mask id="h">
|
||||
<g filter="url(#a)">
|
||||
<path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/>
|
||||
</g>
|
||||
</mask>
|
||||
<clipPath id="i">
|
||||
<path d="m 0 0 h 1024 v 800 h -1024 z"/>
|
||||
</clipPath>
|
||||
<mask id="j">
|
||||
<g filter="url(#a)">
|
||||
<path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/>
|
||||
</g>
|
||||
</mask>
|
||||
<clipPath id="k">
|
||||
<path d="m 0 0 h 1024 v 800 h -1024 z"/>
|
||||
</clipPath>
|
||||
<mask id="l">
|
||||
<g filter="url(#a)">
|
||||
<path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/>
|
||||
</g>
|
||||
</mask>
|
||||
<clipPath id="m">
|
||||
<path d="m 0 0 h 1024 v 800 h -1024 z"/>
|
||||
</clipPath>
|
||||
<mask id="n">
|
||||
<g filter="url(#a)">
|
||||
<path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.05"/>
|
||||
</g>
|
||||
</mask>
|
||||
<clipPath id="o">
|
||||
<path d="m 0 0 h 1024 v 800 h -1024 z"/>
|
||||
</clipPath>
|
||||
<mask id="p">
|
||||
<g filter="url(#a)">
|
||||
<path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.3"/>
|
||||
</g>
|
||||
</mask>
|
||||
<clipPath id="q">
|
||||
<path d="m 0 0 h 1024 v 800 h -1024 z"/>
|
||||
</clipPath>
|
||||
<mask id="r">
|
||||
<g filter="url(#a)">
|
||||
<path d="m 0 0 h 16 v 16 h -16 z" fill-opacity="0.5"/>
|
||||
</g>
|
||||
</mask>
|
||||
<clipPath id="s">
|
||||
<path d="m 0 0 h 1024 v 800 h -1024 z"/>
|
||||
</clipPath>
|
||||
<g fill="#2e3436">
|
||||
<path d="m 1 2.007812 c -0.550781 0 -1 0.449219 -1 1 v 9 c 0 0.550782 0.449219 1 1 1 h 4 v -2 h -3 v -7 h 10 v 3 h 2 v -4 c 0 -0.550781 -0.449219 -1 -1 -1 z m 0 0"/>
|
||||
<path d="m 9 10 c -0.550781 0 -1 0.449219 -1 1 v 4.007812 c 0 0.550782 0.449219 1 1 1 h 6 c 0.550781 0 1 -0.449218 1 -1 v -4.007812 c 0 -0.550781 -0.449219 -1 -1 -1 z m 1 2 h 4 v 2.007812 h -4 z m 0 0" fill-opacity="0.35"/>
|
||||
<path d="m 7.863281 9.871094 c -0.167969 0.164062 -0.492187 0.1875 -0.753906 -0.019532 l -3.113281 -3.085937 v -0.753906 h 0.753906 l 3.109375 3.085937 c 0.191406 0.191406 0.179687 0.601563 0.003906 0.773438 z m 0 0"/>
|
||||
<path d="m 6.996094 6.011719 h -4 v -1 h 4 z m 0 0"/>
|
||||
<path d="m 3.996094 9.011719 h -1 v -4 h 1 z m 0 0"/>
|
||||
</g>
|
||||
<g clip-path="url(#c)" mask="url(#b)" transform="matrix(1 0 0 1 -600 -764)">
|
||||
<path d="m 562.460938 212.058594 h 10.449218 c -1.183594 0.492187 -1.296875 2.460937 0 3 h -10.449218 z m 0 0" fill="#2e3436"/>
|
||||
</g>
|
||||
<g clip-path="url(#e)" mask="url(#d)" transform="matrix(1 0 0 1 -600 -764)">
|
||||
<path d="m 16 632 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g clip-path="url(#g)" mask="url(#f)" transform="matrix(1 0 0 1 -600 -764)">
|
||||
<path d="m 17 631 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g clip-path="url(#i)" mask="url(#h)" transform="matrix(1 0 0 1 -600 -764)">
|
||||
<path d="m 18 634 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g clip-path="url(#k)" mask="url(#j)" transform="matrix(1 0 0 1 -600 -764)">
|
||||
<path d="m 16 634 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g clip-path="url(#m)" mask="url(#l)" transform="matrix(1 0 0 1 -600 -764)">
|
||||
<path d="m 17 635 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g clip-path="url(#o)" mask="url(#n)" transform="matrix(1 0 0 1 -600 -764)">
|
||||
<path d="m 19 635 h 1 v 1 h -1 z m 0 0" fill="#2e3436" fill-rule="evenodd"/>
|
||||
</g>
|
||||
<g clip-path="url(#q)" mask="url(#p)" transform="matrix(1 0 0 1 -600 -764)">
|
||||
<path d="m 136 660 v 7 h 7 v -7 z m 0 0" fill="#2e3436"/>
|
||||
</g>
|
||||
<g clip-path="url(#s)" mask="url(#r)" transform="matrix(1 0 0 1 -600 -764)">
|
||||
<path d="m 219 642 h 3 v 12 h -3 z m 0 0" fill="#2e3436"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.7 KiB |
4
data/icons/play-symbolic.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg height="16px" viewBox="2 2 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M 5 3.746094 L 5 16.246094 L 6.25 16.246094 C 6.46875 16.246094 6.683594 16.195312 6.875 16.089844 L 15.625 11.089844 C 16.015625 10.875 16.210938 10.433594 16.210938 9.996094 C 16.210938 9.554688 16.015625 9.117188 15.625 8.902344 L 6.875 3.902344 C 6.683594 3.792969 6.46875 3.746094 6.25 3.746094 Z M 5 3.746094" fill="#2e3436"/>
|
||||
</svg>
|
After Width: | Height: | Size: 480 B |
@@ -1,18 +1,46 @@
|
||||
sharedir = join_paths(get_option('prefix'), 'share')
|
||||
iconsdir = join_paths(sharedir, 'icons', 'hicolor')
|
||||
iconsdir = join_paths(datadir, 'icons', 'hicolor')
|
||||
|
||||
appstream_util = find_program('appstream-util', required: false)
|
||||
if appstream_util.found()
|
||||
test('Validate appstream file', appstream_util, args: [
|
||||
'validate-relax', '--nonet',
|
||||
join_paths(meson.current_source_dir(), 'com.github.rafostar.Clapper.metainfo.xml')
|
||||
])
|
||||
endif
|
||||
|
||||
install_data('com.github.rafostar.Clapper.svg',
|
||||
install_dir: join_paths(iconsdir, 'scalable', 'apps')
|
||||
)
|
||||
install_data('com.github.rafostar.Clapper-symbolic.svg',
|
||||
install_dir: join_paths(iconsdir, 'symbolic', 'apps')
|
||||
)
|
||||
install_data('com.github.rafostar.Clapper.gschema.xml',
|
||||
install_dir: join_paths(sharedir, 'glib-2.0', 'schemas')
|
||||
install_dir: join_paths(datadir, 'glib-2.0', 'schemas')
|
||||
)
|
||||
install_data('com.github.rafostar.Clapper.xml',
|
||||
install_dir: join_paths(sharedir, 'mime', 'packages')
|
||||
install_dir: join_paths(datadir, 'mime', 'packages')
|
||||
)
|
||||
install_data('com.github.rafostar.Clapper.desktop',
|
||||
install_dir: join_paths(sharedir, 'applications')
|
||||
install_dir: join_paths(datadir, 'applications')
|
||||
)
|
||||
install_data('com.github.rafostar.Clapper.metainfo.xml',
|
||||
install_dir: join_paths(sharedir, 'metainfo')
|
||||
install_dir: join_paths(datadir, 'metainfo')
|
||||
)
|
||||
gnome.compile_resources('com.github.rafostar.Clapper.data',
|
||||
'com.github.rafostar.Clapper.data.gresource.xml',
|
||||
gresource_bundle: true,
|
||||
install: true,
|
||||
install_dir: pkgdatadir,
|
||||
)
|
||||
|
||||
dbus_conf = configuration_data()
|
||||
dbus_conf.set('app_id', meson.project_name())
|
||||
dbus_conf.set('bindir', bindir)
|
||||
|
||||
configure_file(
|
||||
input: 'com.github.rafostar.Clapper.service.in',
|
||||
output: 'com.github.rafostar.Clapper.service',
|
||||
configuration: dbus_conf,
|
||||
install: true,
|
||||
install_dir: join_paths(datadir, 'dbus-1', 'services'),
|
||||
)
|
||||
|
2
lib/gst/clapper/clapper.h
vendored
@@ -28,8 +28,6 @@
|
||||
#include <gst/clapper/gstclapper-g-main-context-signal-dispatcher.h>
|
||||
#include <gst/clapper/gstclapper-video-overlay-video-renderer.h>
|
||||
#include <gst/clapper/gstclapper-visualization.h>
|
||||
#include <gst/clapper/gstclapper-playlist.h>
|
||||
#include <gst/clapper/gstclapper-playlist-item.h>
|
||||
#include <gst/clapper/gstclapper-mpris.h>
|
||||
#include <gst/clapper/gstclapper-gtk4-plugin.h>
|
||||
|
||||
|
@@ -147,31 +147,31 @@ gst_clapper_mpris_class_init (GstClapperMprisClass * klass)
|
||||
param_specs[PROP_OWN_NAME] =
|
||||
g_param_spec_string ("own-name", "DBus own name",
|
||||
"DBus name to own on connection",
|
||||
NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
|
||||
NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
param_specs[PROP_ID_PATH] =
|
||||
g_param_spec_string ("id-path", "DBus id path",
|
||||
"A valid D-Bus path describing this player",
|
||||
NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
|
||||
NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
param_specs[PROP_IDENTITY] =
|
||||
g_param_spec_string ("identity", "Player name",
|
||||
"A friendly name to identify the media player",
|
||||
NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
|
||||
NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
param_specs[PROP_DESKTOP_ENTRY] =
|
||||
g_param_spec_string ("desktop-entry", "Desktop entry filename",
|
||||
"The basename of an installed .desktop file",
|
||||
NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
|
||||
NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
param_specs[PROP_DEFAULT_ART_URL] =
|
||||
g_param_spec_string ("default-art-url", "Default Art URL",
|
||||
"Default art to show when media does not provide one",
|
||||
NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
|
||||
NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
param_specs[PROP_VOLUME] =
|
||||
@@ -220,6 +220,21 @@ gst_clapper_mpris_get_property (GObject * object, guint prop_id,
|
||||
GstClapperMpris *self = GST_CLAPPER_MPRIS (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_OWN_NAME:
|
||||
g_value_set_string (value, self->own_name);
|
||||
break;
|
||||
case PROP_ID_PATH:
|
||||
g_value_set_string (value, self->id_path);
|
||||
break;
|
||||
case PROP_IDENTITY:
|
||||
g_value_set_string (value, self->identity);
|
||||
break;
|
||||
case PROP_DESKTOP_ENTRY:
|
||||
g_value_set_string (value, self->desktop_entry);
|
||||
break;
|
||||
case PROP_DEFAULT_ART_URL:
|
||||
g_value_set_string (value, self->default_art_url);
|
||||
break;
|
||||
case PROP_VOLUME:
|
||||
g_object_get_property (G_OBJECT (self->player_skeleton), "volume", value);
|
||||
break;
|
||||
@@ -392,17 +407,11 @@ handle_open_uri_cb (GstClapperMprisMediaPlayer2Player * player_skeleton,
|
||||
gpointer user_data)
|
||||
{
|
||||
GstClapper *clapper = GST_CLAPPER (user_data);
|
||||
GstClapperPlaylist *playlist;
|
||||
GstClapperPlaylistItem *item;
|
||||
|
||||
GST_DEBUG ("Handle OpenUri");
|
||||
|
||||
playlist = gst_clapper_playlist_new ();
|
||||
item = gst_clapper_playlist_item_new (uri);
|
||||
|
||||
gst_clapper_playlist_append (playlist, item);
|
||||
|
||||
gst_clapper_set_playlist (clapper, playlist);
|
||||
/* FIXME: set one item playlist instead */
|
||||
gst_clapper_set_uri (clapper, uri);
|
||||
gst_clapper_mpris_media_player2_player_complete_open_uri (player_skeleton, invocation);
|
||||
|
||||
return TRUE;
|
||||
@@ -777,12 +786,8 @@ gst_clapper_mpris_new (const gchar * own_name, const gchar * id_path,
|
||||
const gchar * identity, const gchar * desktop_entry,
|
||||
const gchar * default_art_url)
|
||||
{
|
||||
GstClapperMpris *self;
|
||||
|
||||
self = g_object_new (GST_TYPE_CLAPPER,
|
||||
return g_object_new (GST_TYPE_CLAPPER_MPRIS,
|
||||
"own-name", own_name, "id_path", id_path,
|
||||
"identity", identity, "desktop-entry", desktop_entry,
|
||||
"default-art-url", default_art_url, NULL);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
281
lib/gst/clapper/gstclapper-playlist-item.c
vendored
@@ -1,281 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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 Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstclapper-playlist-item.h"
|
||||
#include "gstclapper-playlist-item-private.h"
|
||||
#include "gstclapper-playlist-private.h"
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_URI,
|
||||
PROP_SUBURI,
|
||||
PROP_CUSTOM_TITLE,
|
||||
PROP_LAST
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SIGNAL_ACTIVATED,
|
||||
SIGNAL_LAST
|
||||
};
|
||||
|
||||
#define parent_class gst_clapper_playlist_item_parent_class
|
||||
G_DEFINE_TYPE (GstClapperPlaylistItem, gst_clapper_playlist_item, GST_TYPE_OBJECT);
|
||||
|
||||
static guint signals[SIGNAL_LAST] = { 0, };
|
||||
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
|
||||
|
||||
static void gst_clapper_playlist_item_set_property (GObject * object,
|
||||
guint prop_id, const GValue * value, GParamSpec * pspec);
|
||||
static void gst_clapper_playlist_item_get_property (GObject * object,
|
||||
guint prop_id, GValue * value, GParamSpec * pspec);
|
||||
static void gst_clapper_playlist_item_dispose (GObject * object);
|
||||
static void gst_clapper_playlist_item_finalize (GObject * object);
|
||||
|
||||
static void
|
||||
gst_clapper_playlist_item_init (GstClapperPlaylistItem * self)
|
||||
{
|
||||
self->owner_uuid = NULL;
|
||||
self->id = -1;
|
||||
|
||||
self->uri = NULL;
|
||||
self->suburi = NULL;
|
||||
self->custom_title = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_playlist_item_class_init (GstClapperPlaylistItemClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
|
||||
gobject_class->set_property = gst_clapper_playlist_item_set_property;
|
||||
gobject_class->get_property = gst_clapper_playlist_item_get_property;
|
||||
gobject_class->dispose = gst_clapper_playlist_item_dispose;
|
||||
gobject_class->finalize = gst_clapper_playlist_item_finalize;
|
||||
|
||||
param_specs[PROP_URI] = g_param_spec_string ("uri",
|
||||
"URI", "Playlist Item URI", NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
param_specs[PROP_SUBURI] = g_param_spec_string ("suburi",
|
||||
"Subtitle URI", "Playlist Item Subtitle URI", NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
param_specs[PROP_CUSTOM_TITLE] = g_param_spec_string ("custom-title",
|
||||
"Custom Title", "Playlist Item Custom Title", NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
|
||||
G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
|
||||
|
||||
signals[SIGNAL_ACTIVATED] =
|
||||
g_signal_new ("activated", 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, G_TYPE_INVALID);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_playlist_item_set_property (GObject * object,
|
||||
guint prop_id, const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstClapperPlaylistItem *self = GST_CLAPPER_PLAYLIST_ITEM (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_URI:
|
||||
self->uri = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_SUBURI:
|
||||
g_free (self->suburi);
|
||||
self->suburi = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_CUSTOM_TITLE:
|
||||
self->custom_title = g_value_dup_string (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_playlist_item_get_property (GObject * object,
|
||||
guint prop_id, GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstClapperPlaylistItem *self = GST_CLAPPER_PLAYLIST_ITEM (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_URI:
|
||||
g_value_set_string (value, self->uri);
|
||||
break;
|
||||
case PROP_SUBURI:
|
||||
g_value_set_string (value, self->suburi);
|
||||
break;
|
||||
case PROP_CUSTOM_TITLE:
|
||||
g_value_set_string (value, self->custom_title);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_playlist_item_dispose (GObject * object)
|
||||
{
|
||||
GstClapperPlaylistItem *self = GST_CLAPPER_PLAYLIST_ITEM (object);
|
||||
|
||||
if (self->activated_signal_id) {
|
||||
g_signal_handler_disconnect (self, self->activated_signal_id);
|
||||
self->activated_signal_id = 0;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_playlist_item_finalize (GObject * object)
|
||||
{
|
||||
GstClapperPlaylistItem *self = GST_CLAPPER_PLAYLIST_ITEM (object);
|
||||
|
||||
g_free (self->owner_uuid);
|
||||
|
||||
g_free (self->uri);
|
||||
g_free (self->suburi);
|
||||
g_free (self->custom_title);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
item_activate_cb (GstClapperPlaylistItem * self, GParamSpec * pspec,
|
||||
GstClapperPlaylist * playlist)
|
||||
{
|
||||
gst_clapper_playlist_emit_item_activated (playlist, self);
|
||||
}
|
||||
|
||||
void
|
||||
gst_clapper_playlist_item_mark_added (GstClapperPlaylistItem * self,
|
||||
GstClapperPlaylist * playlist)
|
||||
{
|
||||
GST_OBJECT_LOCK (self);
|
||||
|
||||
self->owner_uuid = g_strdup (playlist->uuid);
|
||||
self->id = playlist->id_count;
|
||||
|
||||
self->activated_signal_id = g_signal_connect (self, "activated",
|
||||
G_CALLBACK (item_activate_cb), playlist);
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_clapper_playlist_item_new:
|
||||
*
|
||||
* Creates a new #GstClapperPlaylistItem.
|
||||
*
|
||||
* Returns: (transfer full): a new #GstClapperPlaylistItem object.
|
||||
*/
|
||||
GstClapperPlaylistItem *
|
||||
gst_clapper_playlist_item_new (const gchar * uri)
|
||||
{
|
||||
return g_object_new (GST_TYPE_CLAPPER_PLAYLIST_ITEM, "uri", uri, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_clapper_playlist_item_new_titled:
|
||||
* @uri: An URI pointing to media
|
||||
* @custom_title: A custom title for this item
|
||||
*
|
||||
* Creates a new #GstClapperPlaylistItem with a custom title.
|
||||
*
|
||||
* Normally item title is obtained from media info or local filename,
|
||||
* use this function for online sources where media title cannot be
|
||||
* determined or if you want to override original title for some reason.
|
||||
*
|
||||
* Returns: (transfer full): a new #GstClapperPlaylistItem object.
|
||||
*/
|
||||
GstClapperPlaylistItem *
|
||||
gst_clapper_playlist_item_new_titled (const gchar * uri,
|
||||
const gchar * custom_title)
|
||||
{
|
||||
return g_object_new (GST_TYPE_CLAPPER_PLAYLIST_ITEM, "uri", uri,
|
||||
"custom_title", custom_title, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_clapper_playlist_item_copy:
|
||||
* @item: #GstClapperPlaylistItem
|
||||
*
|
||||
* Duplicates a #GstClapperPlaylistItem.
|
||||
*
|
||||
* Duplicated items do not belong to any playlist.
|
||||
* Use this function if you want to append the same
|
||||
* media into another #GstClapperPlaylist instance.
|
||||
*
|
||||
* Returns: (transfer full): a new #GstClapperPlaylistItem object.
|
||||
*/
|
||||
GstClapperPlaylistItem *
|
||||
gst_clapper_playlist_item_copy (GstClapperPlaylistItem * source)
|
||||
{
|
||||
g_return_val_if_fail (GST_IS_CLAPPER_PLAYLIST_ITEM (source), NULL);
|
||||
|
||||
return g_object_new (GST_TYPE_CLAPPER_PLAYLIST_ITEM, "uri", source->uri,
|
||||
"suburi", source->suburi, "custom-title", source->custom_title, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_clapper_playlist_item_set_suburi:
|
||||
* @item: #GstClapperPlaylistItem
|
||||
* @suburi: subtitle URI
|
||||
*
|
||||
* Sets the external subtitle URI.
|
||||
*/
|
||||
void
|
||||
gst_clapper_playlist_item_set_suburi (GstClapperPlaylistItem * self,
|
||||
const gchar * suburi)
|
||||
{
|
||||
/* TODO: When setting this property for an item that is currently active,
|
||||
* it should be combined with a call to
|
||||
* gst_clapper_set_subtitle_track_enabled(Clapper, TRUE),
|
||||
* so the subtitles are actually rendered.
|
||||
*/
|
||||
g_return_if_fail (GST_IS_CLAPPER_PLAYLIST_ITEM (self));
|
||||
|
||||
g_object_set (self, "suburi", suburi, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_clapper_playlist_item_activate:
|
||||
* @item: #GstClapperPlaylistItem
|
||||
*
|
||||
* Activates the #GstClapperPlaylistItem.
|
||||
*/
|
||||
void
|
||||
gst_clapper_playlist_item_activate (GstClapperPlaylistItem * self)
|
||||
{
|
||||
g_return_if_fail (GST_IS_CLAPPER_PLAYLIST_ITEM (self));
|
||||
|
||||
g_signal_emit (self, signals[SIGNAL_ACTIVATED], 0);
|
||||
}
|
62
lib/gst/clapper/gstclapper-playlist-item.h
vendored
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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 Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
*/
|
||||
|
||||
#ifndef __GST_CLAPPER_PLAYLIST_ITEM_H__
|
||||
#define __GST_CLAPPER_PLAYLIST_ITEM_H__
|
||||
|
||||
#include <gst/clapper/clapper-prelude.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GstClapperPlaylistItem GstClapperPlaylistItem;
|
||||
typedef struct _GstClapperPlaylistItemClass GstClapperPlaylistItemClass;
|
||||
|
||||
#define GST_TYPE_CLAPPER_PLAYLIST_ITEM (gst_clapper_playlist_item_get_type ())
|
||||
#define GST_IS_CLAPPER_PLAYLIST_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CLAPPER_PLAYLIST_ITEM))
|
||||
#define GST_IS_CLAPPER_PLAYLIST_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_CLAPPER_PLAYLIST_ITEM))
|
||||
#define GST_CLAPPER_PLAYLIST_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CLAPPER_PLAYLIST_ITEM, GstClapperPlaylistItemClass))
|
||||
#define GST_CLAPPER_PLAYLIST_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_CLAPPER_PLAYLIST_ITEM, GstClapperPlaylistItem))
|
||||
#define GST_CLAPPER_PLAYLIST_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_CLAPPER_PLAYLIST_ITEM, GstClapperPlaylistItemClass))
|
||||
#define GST_CLAPPER_PLAYLIST_ITEM_CAST(obj) ((GstClapperPlaylistItem*)(obj))
|
||||
|
||||
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstClapperPlaylistItem, gst_object_unref)
|
||||
#endif
|
||||
|
||||
GST_CLAPPER_API
|
||||
GType gst_clapper_playlist_item_get_type (void);
|
||||
|
||||
GST_CLAPPER_API
|
||||
GstClapperPlaylistItem * gst_clapper_playlist_item_new (const gchar *uri);
|
||||
|
||||
GST_CLAPPER_API
|
||||
GstClapperPlaylistItem * gst_clapper_playlist_item_new_titled (const gchar *uri, const gchar *custom_title);
|
||||
|
||||
GST_CLAPPER_API
|
||||
GstClapperPlaylistItem * gst_clapper_playlist_item_copy (GstClapperPlaylistItem *item);
|
||||
|
||||
GST_CLAPPER_API
|
||||
void gst_clapper_playlist_item_set_suburi (GstClapperPlaylistItem *item, const gchar *suburi);
|
||||
|
||||
GST_CLAPPER_API
|
||||
void gst_clapper_playlist_item_activate (GstClapperPlaylistItem *item);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_CLAPPER_PLAYLIST_ITEM_H__ */
|
275
lib/gst/clapper/gstclapper-playlist.c
vendored
@@ -1,275 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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 Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstclapper-playlist.h"
|
||||
#include "gstclapper-playlist-private.h"
|
||||
#include "gstclapper-playlist-item.h"
|
||||
#include "gstclapper-playlist-item-private.h"
|
||||
|
||||
enum
|
||||
{
|
||||
SIGNAL_ITEM_ACTIVATED,
|
||||
SIGNAL_LAST
|
||||
};
|
||||
|
||||
#define parent_class gst_clapper_playlist_parent_class
|
||||
G_DEFINE_TYPE (GstClapperPlaylist, gst_clapper_playlist, GST_TYPE_OBJECT);
|
||||
|
||||
static guint signals[SIGNAL_LAST] = { 0, };
|
||||
|
||||
static void gst_clapper_playlist_dispose (GObject * object);
|
||||
static void gst_clapper_playlist_finalize (GObject * object);
|
||||
|
||||
static void
|
||||
gst_clapper_playlist_init (GstClapperPlaylist * self)
|
||||
{
|
||||
self->uuid = g_uuid_string_random ();
|
||||
self->id_count = 0;
|
||||
self->items = g_array_new (FALSE, FALSE, sizeof (GstClapperPlaylistItem));
|
||||
self->active_index = -1;
|
||||
|
||||
g_array_set_clear_func (self->items, (GDestroyNotify) gst_object_unref);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_playlist_class_init (GstClapperPlaylistClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
|
||||
gobject_class->dispose = gst_clapper_playlist_dispose;
|
||||
gobject_class->finalize = gst_clapper_playlist_finalize;
|
||||
|
||||
signals[SIGNAL_ITEM_ACTIVATED] =
|
||||
g_signal_new ("item-activated", G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
|
||||
NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_CLAPPER_PLAYLIST_ITEM);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_playlist_dispose (GObject * object)
|
||||
{
|
||||
GstClapperPlaylist *self = GST_CLAPPER_PLAYLIST (object);
|
||||
|
||||
/* FIXME: Need this for something? */
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_playlist_finalize (GObject * object)
|
||||
{
|
||||
GstClapperPlaylist *self = GST_CLAPPER_PLAYLIST (object);
|
||||
|
||||
g_free (self->uuid);
|
||||
g_array_unref (self->items);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
void
|
||||
gst_clapper_playlist_emit_item_activated (GstClapperPlaylist * self,
|
||||
GstClapperPlaylistItem * item)
|
||||
{
|
||||
g_signal_emit (self, signals[SIGNAL_ITEM_ACTIVATED], 0, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_clapper_playlist_new:
|
||||
*
|
||||
* Creates a new #GstClapperPlaylist.
|
||||
*
|
||||
* Returns: (transfer full): a new #GstClapperPlaylist instance.
|
||||
*/
|
||||
GstClapperPlaylist *
|
||||
gst_clapper_playlist_new (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_CLAPPER_PLAYLIST, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_clapper_playlist_append:
|
||||
* @playlist: #GstClapperPlaylist
|
||||
* @item: #GstClapperPlaylistItem to append
|
||||
*
|
||||
* Adds a new #GstClapperPlaylistItem to the end of playlist.
|
||||
*
|
||||
* Returns: %TRUE if the item was added successfully.
|
||||
*/
|
||||
gboolean
|
||||
gst_clapper_playlist_append (GstClapperPlaylist * self, GstClapperPlaylistItem * item)
|
||||
{
|
||||
gboolean added = FALSE;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CLAPPER_PLAYLIST (self), FALSE);
|
||||
g_return_val_if_fail (GST_IS_CLAPPER_PLAYLIST_ITEM (item), FALSE);
|
||||
g_return_val_if_fail (item->owner_uuid == NULL, FALSE);
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
|
||||
added = g_array_append_val (self->items, item) != NULL;
|
||||
if (added) {
|
||||
gst_clapper_playlist_item_mark_added (item, self);
|
||||
self->id_count++;
|
||||
}
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_clapper_playlist_get_length:
|
||||
* @playlist: #GstClapperPlaylist
|
||||
*
|
||||
* Returns: Amount of items in playlist.
|
||||
*/
|
||||
guint
|
||||
gst_clapper_playlist_get_length (GstClapperPlaylist * self)
|
||||
{
|
||||
guint len;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CLAPPER_PLAYLIST (self), 0);
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
len = self->items->len;
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_clapper_playlist_get_item_at_index:
|
||||
* @playlist: #GstClapperPlaylist
|
||||
*
|
||||
* Returns: (transfer none): A #GstClapperPlaylistItem at given index.
|
||||
*/
|
||||
GstClapperPlaylistItem *
|
||||
gst_clapper_playlist_get_item_at_index (GstClapperPlaylist * self, gint index)
|
||||
{
|
||||
GstClapperPlaylistItem *item = NULL;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CLAPPER_PLAYLIST (self), NULL);
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
|
||||
if (index < self->items->len)
|
||||
goto out;
|
||||
|
||||
item = &g_array_index (self->items, GstClapperPlaylistItem, index);
|
||||
|
||||
out:
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_clapper_playlist_get_active_item:
|
||||
* @playlist: #GstClapperPlaylist
|
||||
*
|
||||
* Returns: (transfer none): A #GstClapperPlaylistItem that is
|
||||
* currently playing.
|
||||
*/
|
||||
GstClapperPlaylistItem *
|
||||
gst_clapper_playlist_get_active_item (GstClapperPlaylist * self)
|
||||
{
|
||||
gint active_index;
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
active_index = self->active_index;
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
return gst_clapper_playlist_get_item_at_index (self, active_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_clapper_playlist_remove_item_at_index:
|
||||
* @playlist: #GstClapperPlaylist
|
||||
* @index: Index of #GstClapperPlaylistItem to remove
|
||||
*
|
||||
* Removes item at given index from playlist.
|
||||
*
|
||||
* Returns: %TRUE if the item was removed successfully.
|
||||
*/
|
||||
gboolean
|
||||
gst_clapper_playlist_remove_item_at_index (GstClapperPlaylist * self, guint index)
|
||||
{
|
||||
gboolean removed = FALSE;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CLAPPER_PLAYLIST (self), FALSE);
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
|
||||
if (index >= self->items->len || index == self->active_index)
|
||||
goto out;
|
||||
|
||||
removed = g_array_remove_index (self->items, index) != NULL;
|
||||
|
||||
out:
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_clapper_playlist_remove_item:
|
||||
* @playlist: #GstClapperPlaylist
|
||||
* @item: #GstClapperPlaylistItem object to remove
|
||||
*
|
||||
* Removes given playlist item from playlist.
|
||||
*
|
||||
* Returns: %TRUE if the item was removed successfully.
|
||||
*/
|
||||
gboolean
|
||||
gst_clapper_playlist_remove_item (GstClapperPlaylist * self,
|
||||
GstClapperPlaylistItem * item)
|
||||
{
|
||||
gint i;
|
||||
gboolean removed = FALSE;
|
||||
|
||||
g_return_val_if_fail (GST_IS_CLAPPER_PLAYLIST (self), FALSE);
|
||||
g_return_val_if_fail (GST_IS_CLAPPER_PLAYLIST_ITEM (item), FALSE);
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
|
||||
if (strcmp (self->uuid, item->owner_uuid) != 0)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < self->items->len; i++) {
|
||||
GstClapperPlaylistItem *curr_item;
|
||||
|
||||
curr_item = &g_array_index (self->items, GstClapperPlaylistItem, i);
|
||||
if (!curr_item)
|
||||
goto out;
|
||||
|
||||
if (item->id == curr_item->id) {
|
||||
removed = g_array_remove_index (self->items, i) != NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
return removed;
|
||||
}
|
71
lib/gst/clapper/gstclapper-playlist.h
vendored
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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 Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
*/
|
||||
|
||||
#ifndef __GST_CLAPPER_PLAYLIST_H__
|
||||
#define __GST_CLAPPER_PLAYLIST_H__
|
||||
|
||||
#include <gst/clapper/clapper-prelude.h>
|
||||
#include <gst/clapper/gstclapper-playlist-item.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GstClapperPlaylist GstClapperPlaylist;
|
||||
typedef struct _GstClapperPlaylistClass GstClapperPlaylistClass;
|
||||
|
||||
#define GST_TYPE_CLAPPER_PLAYLIST (gst_clapper_playlist_get_type ())
|
||||
#define GST_IS_CLAPPER_PLAYLIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CLAPPER_PLAYLIST))
|
||||
#define GST_IS_CLAPPER_PLAYLIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_CLAPPER_PLAYLIST))
|
||||
#define GST_CLAPPER_PLAYLIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CLAPPER_PLAYLIST, GstClapperPlaylistClass))
|
||||
#define GST_CLAPPER_PLAYLIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_CLAPPER_PLAYLIST, GstClapperPlaylist))
|
||||
#define GST_CLAPPER_PLAYLIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_CLAPPER_PLAYLIST, GstClapperPlaylistClass))
|
||||
#define GST_CLAPPER_PLAYLIST_CAST(obj) ((GstClapperPlaylist*)(obj))
|
||||
|
||||
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstClapperPlaylist, g_object_unref)
|
||||
#endif
|
||||
|
||||
GST_CLAPPER_API
|
||||
GType gst_clapper_playlist_get_type (void);
|
||||
|
||||
GST_CLAPPER_API
|
||||
GstClapperPlaylist * gst_clapper_playlist_new (void);
|
||||
|
||||
GST_CLAPPER_API
|
||||
gboolean gst_clapper_playlist_append (GstClapperPlaylist *playlist, GstClapperPlaylistItem *item);
|
||||
|
||||
GST_CLAPPER_API
|
||||
guint gst_clapper_playlist_get_length (GstClapperPlaylist *playlist);
|
||||
|
||||
GST_CLAPPER_API
|
||||
GstClapperPlaylistItem *
|
||||
gst_clapper_playlist_get_item_at_index (GstClapperPlaylist *playlist, gint index);
|
||||
|
||||
GST_CLAPPER_API
|
||||
GstClapperPlaylistItem *
|
||||
gst_clapper_playlist_get_active_item (GstClapperPlaylist *playlist);
|
||||
|
||||
GST_CLAPPER_API
|
||||
gboolean gst_clapper_playlist_remove_item_at_index (GstClapperPlaylist *playlist, guint index);
|
||||
|
||||
GST_CLAPPER_API
|
||||
gboolean gst_clapper_playlist_remove_item (GstClapperPlaylist *playlist, GstClapperPlaylistItem *item);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_CLAPPER_PLAYLIST_H__ */
|
915
lib/gst/clapper/gstclapper.c
vendored
16
lib/gst/clapper/gstclapper.h
vendored
@@ -30,8 +30,6 @@
|
||||
#include <gst/clapper/gstclapper-signal-dispatcher.h>
|
||||
#include <gst/clapper/gstclapper-video-renderer.h>
|
||||
#include <gst/clapper/gstclapper-media-info.h>
|
||||
#include <gst/clapper/gstclapper-playlist.h>
|
||||
#include <gst/clapper/gstclapper-playlist-item.h>
|
||||
#include <gst/clapper/gstclapper-mpris.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
@@ -155,6 +153,9 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstClapper, gst_object_unref)
|
||||
GST_CLAPPER_API
|
||||
GType gst_clapper_get_type (void);
|
||||
|
||||
GST_CLAPPER_API
|
||||
void gst_clapper_gst_init (int *argc, char **argv[]);
|
||||
|
||||
GST_CLAPPER_API
|
||||
GstClapper * gst_clapper_new (GstClapperVideoRenderer *video_renderer, GstClapperSignalDispatcher *signal_dispatcher,
|
||||
GstClapperMpris *mpris);
|
||||
@@ -195,7 +196,16 @@ GST_CLAPPER_API
|
||||
gdouble gst_clapper_get_rate (GstClapper *clapper);
|
||||
|
||||
GST_CLAPPER_API
|
||||
void gst_clapper_set_playlist (GstClapper *clapper, GstClapperPlaylist *playlist);
|
||||
gchar * gst_clapper_get_uri (GstClapper *clapper);
|
||||
|
||||
GST_CLAPPER_API
|
||||
void gst_clapper_set_uri (GstClapper *clapper, const gchar *uri);
|
||||
|
||||
GST_CLAPPER_API
|
||||
gchar * gst_clapper_get_subtitle_uri (GstClapper *clapper);
|
||||
|
||||
GST_CLAPPER_API
|
||||
void gst_clapper_set_subtitle_uri (GstClapper *clapper, const gchar *uri);
|
||||
|
||||
GST_CLAPPER_API
|
||||
GstClockTime gst_clapper_get_position (GstClapper *clapper);
|
||||
|
@@ -37,19 +37,27 @@
|
||||
GST_DEBUG_CATEGORY (gst_debug_clapper_gl_sink);
|
||||
#define GST_CAT_DEFAULT gst_debug_clapper_gl_sink
|
||||
|
||||
#define DEFAULT_FORCE_ASPECT_RATIO TRUE
|
||||
#define DEFAULT_PAR_N 0
|
||||
#define DEFAULT_PAR_D 1
|
||||
#define GST_CLAPPER_GL_SINK_CAPS \
|
||||
"video/x-raw(" GST_CAPS_FEATURE_MEMORY_GL_MEMORY "), " \
|
||||
"format = (string) RGBA, " \
|
||||
"width = " GST_VIDEO_SIZE_RANGE ", " \
|
||||
"height = " GST_VIDEO_SIZE_RANGE ", " \
|
||||
"framerate = " GST_VIDEO_FPS_RANGE ", " \
|
||||
"texture-target = (string) { 2D, external-oes } " \
|
||||
" ; " \
|
||||
"video/x-raw(" GST_CAPS_FEATURE_MEMORY_GL_MEMORY "," \
|
||||
GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION "), " \
|
||||
"format = (string) RGBA, " \
|
||||
"width = " GST_VIDEO_SIZE_RANGE ", " \
|
||||
"height = " GST_VIDEO_SIZE_RANGE ", " \
|
||||
"framerate = " GST_VIDEO_FPS_RANGE ", " \
|
||||
"texture-target = (string) { 2D, external-oes } "
|
||||
|
||||
static GstStaticPadTemplate gst_clapper_gl_sink_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
|
||||
(GST_CAPS_FEATURE_MEMORY_GL_MEMORY, "RGBA") "; "
|
||||
GST_VIDEO_CAPS_MAKE_WITH_FEATURES
|
||||
(GST_CAPS_FEATURE_MEMORY_GL_MEMORY ", "
|
||||
GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, "RGBA")));
|
||||
GST_STATIC_CAPS (GST_CLAPPER_GL_SINK_CAPS));
|
||||
|
||||
static void gst_clapper_gl_sink_finalize (GObject * object);
|
||||
static void gst_clapper_gl_sink_set_property (GObject * object, guint prop_id,
|
||||
@@ -62,6 +70,7 @@ static gboolean gst_clapper_gl_sink_propose_allocation (GstBaseSink * bsink,
|
||||
static gboolean gst_clapper_gl_sink_query (GstBaseSink * bsink, GstQuery * query);
|
||||
static gboolean gst_clapper_gl_sink_start (GstBaseSink * bsink);
|
||||
static gboolean gst_clapper_gl_sink_stop (GstBaseSink * bsink);
|
||||
static GstFlowReturn gst_clapper_gl_sink_wait_event (GstBaseSink * bsink, GstEvent * event);
|
||||
|
||||
static GstStateChangeReturn
|
||||
gst_clapper_gl_sink_change_state (GstElement * element,
|
||||
@@ -79,14 +88,6 @@ static GstFlowReturn gst_clapper_gl_sink_show_frame (GstVideoSink * bsink,
|
||||
static void
|
||||
gst_clapper_gl_sink_navigation_interface_init (GstNavigationInterface * iface);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_WIDGET,
|
||||
PROP_FORCE_ASPECT_RATIO,
|
||||
PROP_PIXEL_ASPECT_RATIO,
|
||||
};
|
||||
|
||||
#define gst_clapper_gl_sink_parent_class parent_class
|
||||
G_DEFINE_TYPE_WITH_CODE (GstClapperGLSink, gst_clapper_gl_sink,
|
||||
GST_TYPE_VIDEO_SINK,
|
||||
@@ -112,6 +113,7 @@ gst_clapper_gl_sink_class_init (GstClapperGLSinkClass * klass)
|
||||
|
||||
gobject_class->set_property = gst_clapper_gl_sink_set_property;
|
||||
gobject_class->get_property = gst_clapper_gl_sink_get_property;
|
||||
gobject_class->finalize = gst_clapper_gl_sink_finalize;
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_WIDGET,
|
||||
g_param_spec_object ("widget", "GTK Widget",
|
||||
@@ -119,19 +121,7 @@ gst_clapper_gl_sink_class_init (GstClapperGLSinkClass * klass)
|
||||
"(must only be get from the GTK main thread)",
|
||||
GTK_TYPE_WIDGET, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
|
||||
g_param_spec_boolean ("force-aspect-ratio",
|
||||
"Force aspect ratio",
|
||||
"When enabled, scaling will respect original aspect ratio",
|
||||
DEFAULT_FORCE_ASPECT_RATIO,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
|
||||
gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio",
|
||||
"The pixel aspect ratio of the device", DEFAULT_PAR_N, DEFAULT_PAR_D,
|
||||
G_MAXINT, 1, 1, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
gobject_class->finalize = gst_clapper_gl_sink_finalize;
|
||||
gst_gtk_install_shared_properties (gobject_class);
|
||||
|
||||
gstelement_class->change_state = gst_clapper_gl_sink_change_state;
|
||||
|
||||
@@ -142,6 +132,7 @@ gst_clapper_gl_sink_class_init (GstClapperGLSinkClass * klass)
|
||||
gstbasesink_class->query = gst_clapper_gl_sink_query;
|
||||
gstbasesink_class->start = gst_clapper_gl_sink_start;
|
||||
gstbasesink_class->stop = gst_clapper_gl_sink_stop;
|
||||
gstbasesink_class->wait_event = gst_clapper_gl_sink_wait_event;
|
||||
|
||||
gstvideosink_class->show_frame = gst_clapper_gl_sink_show_frame;
|
||||
|
||||
@@ -166,6 +157,9 @@ gst_clapper_gl_sink_init (GstClapperGLSink * clapper_sink)
|
||||
clapper_sink->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
|
||||
clapper_sink->par_n = DEFAULT_PAR_N;
|
||||
clapper_sink->par_d = DEFAULT_PAR_D;
|
||||
clapper_sink->keep_last_frame = DEFAULT_KEEP_LAST_FRAME;
|
||||
|
||||
clapper_sink->had_eos = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -228,12 +222,12 @@ gst_clapper_gl_sink_get_widget (GstClapperGLSink * clapper_sink)
|
||||
clapper_sink->widget = (GtkClapperGLWidget *)
|
||||
GST_CLAPPER_GL_SINK_GET_CLASS (clapper_sink)->create_widget ();
|
||||
|
||||
clapper_sink->bind_aspect_ratio =
|
||||
g_object_bind_property (clapper_sink, "force-aspect-ratio", clapper_sink->widget,
|
||||
g_object_bind_property (clapper_sink, "force-aspect-ratio", clapper_sink->widget,
|
||||
"force-aspect-ratio", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
|
||||
clapper_sink->bind_pixel_aspect_ratio =
|
||||
g_object_bind_property (clapper_sink, "pixel-aspect-ratio", clapper_sink->widget,
|
||||
g_object_bind_property (clapper_sink, "pixel-aspect-ratio", clapper_sink->widget,
|
||||
"pixel-aspect-ratio", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
|
||||
g_object_bind_property (clapper_sink, "keep-last-frame", clapper_sink->widget,
|
||||
"keep-last-frame", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
|
||||
|
||||
/* Take the floating ref, other wise the destruction of the container will
|
||||
* make this widget disappear possibly before we are done. */
|
||||
@@ -279,6 +273,9 @@ gst_clapper_gl_sink_get_property (GObject * object, guint prop_id,
|
||||
case PROP_PIXEL_ASPECT_RATIO:
|
||||
gst_value_set_fraction (value, clapper_sink->par_n, clapper_sink->par_d);
|
||||
break;
|
||||
case PROP_KEEP_LAST_FRAME:
|
||||
g_value_set_boolean (value, clapper_sink->keep_last_frame);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
@@ -299,6 +296,9 @@ gst_clapper_gl_sink_set_property (GObject * object, guint prop_id,
|
||||
clapper_sink->par_n = gst_value_get_fraction_numerator (value);
|
||||
clapper_sink->par_d = gst_value_get_fraction_denominator (value);
|
||||
break;
|
||||
case PROP_KEEP_LAST_FRAME:
|
||||
clapper_sink->keep_last_frame = g_value_get_boolean (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
@@ -372,8 +372,10 @@ gst_clapper_gl_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
|
||||
gst_buffer_pool_config_add_option (config,
|
||||
GST_BUFFER_POOL_OPTION_GL_SYNC_META);
|
||||
|
||||
if (!gst_buffer_pool_set_config (pool, config))
|
||||
if (!gst_buffer_pool_set_config (pool, config)) {
|
||||
gst_object_unref (pool);
|
||||
goto config_failed;
|
||||
}
|
||||
}
|
||||
|
||||
/* we need at least 2 buffer because we hold on to the last one */
|
||||
@@ -434,12 +436,9 @@ gst_clapper_gl_sink_query (GstBaseSink * bsink, GstQuery * query)
|
||||
|
||||
switch (GST_QUERY_TYPE (query)) {
|
||||
case GST_QUERY_CONTEXT:
|
||||
{
|
||||
if (gst_gl_handle_context_query ((GstElement *) clapper_sink, query,
|
||||
clapper_sink->display, clapper_sink->context, clapper_sink->gtk_context))
|
||||
return TRUE;
|
||||
res = gst_gl_handle_context_query ((GstElement *) clapper_sink, query,
|
||||
clapper_sink->display, clapper_sink->context, clapper_sink->gtk_context);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
|
||||
break;
|
||||
@@ -589,6 +588,7 @@ gst_clapper_gl_sink_change_state (GstElement * element, GstStateChange transitio
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_NULL_TO_READY:
|
||||
GST_OBJECT_LOCK (clapper_sink);
|
||||
clapper_sink->had_eos = FALSE;
|
||||
if (clapper_sink->widget) {
|
||||
GTK_CLAPPER_GL_WIDGET_LOCK (clapper_sink->widget);
|
||||
clapper_sink->widget->ignore_buffers = FALSE;
|
||||
@@ -614,7 +614,8 @@ gst_clapper_gl_sink_change_state (GstElement * element, GstStateChange transitio
|
||||
GST_OBJECT_LOCK (clapper_sink);
|
||||
if (clapper_sink->widget) {
|
||||
GTK_CLAPPER_GL_WIDGET_LOCK (clapper_sink->widget);
|
||||
clapper_sink->widget->ignore_buffers = TRUE;
|
||||
clapper_sink->widget->ignore_buffers =
|
||||
!clapper_sink->had_eos || !clapper_sink->keep_last_frame;
|
||||
GTK_CLAPPER_GL_WIDGET_UNLOCK (clapper_sink->widget);
|
||||
}
|
||||
GST_OBJECT_UNLOCK (clapper_sink);
|
||||
@@ -682,6 +683,7 @@ static gboolean
|
||||
gst_clapper_gl_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
|
||||
{
|
||||
GstClapperGLSink *clapper_sink = GST_CLAPPER_GL_SINK (bsink);
|
||||
gboolean res = FALSE;
|
||||
|
||||
GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps);
|
||||
|
||||
@@ -701,9 +703,34 @@ gst_clapper_gl_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
|
||||
GST_OBJECT_UNLOCK (clapper_sink);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
res = gtk_clapper_gl_widget_update_output_format (clapper_sink->widget, caps);
|
||||
GST_OBJECT_UNLOCK (clapper_sink);
|
||||
|
||||
return TRUE;
|
||||
return res;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_clapper_gl_sink_wait_event (GstBaseSink * bsink, GstEvent * event)
|
||||
{
|
||||
GstClapperGLSink *clapper_sink = GST_CLAPPER_GL_SINK (bsink);
|
||||
GstFlowReturn ret;
|
||||
|
||||
ret = GST_BASE_SINK_CLASS (parent_class)->wait_event (bsink, event);
|
||||
|
||||
switch (event->type) {
|
||||
case GST_EVENT_EOS:
|
||||
if (ret == GST_FLOW_OK) {
|
||||
GST_OBJECT_LOCK (clapper_sink);
|
||||
clapper_sink->had_eos = TRUE;
|
||||
GST_OBJECT_UNLOCK (clapper_sink);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
|
@@ -58,15 +58,14 @@ struct _GstClapperGLSink
|
||||
|
||||
GtkClapperGLWidget *widget;
|
||||
|
||||
gboolean had_eos;
|
||||
|
||||
/* properties */
|
||||
gboolean force_aspect_ratio;
|
||||
GBinding *bind_aspect_ratio;
|
||||
|
||||
gint par_n, par_d;
|
||||
GBinding *bind_pixel_aspect_ratio;
|
||||
gboolean keep_last_frame;
|
||||
|
||||
gboolean ignore_textures;
|
||||
GBinding *bind_ignore_textures;
|
||||
|
||||
GtkWidget *window;
|
||||
gulong widget_destroy_id;
|
||||
|
103
lib/gst/clapper/gtk4/gstclapperglutils.c
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* GStreamer
|
||||
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
||||
* Copyright (C) 2015 Thibault Saunier <tsaunier@gnome.org>
|
||||
* Copyright (C) 2021 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 Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
*/
|
||||
|
||||
/* FIXME: remove these once their headers are public in gstreamer:
|
||||
* https://gitlab.freedesktop.org/gstreamer/gst-plugins-base/-/merge_requests/804
|
||||
*/
|
||||
|
||||
#include "gstclapperglutils.h"
|
||||
|
||||
static const gfloat identity_matrix[] = {
|
||||
1.0, 0.0, 0.0, 0.0,
|
||||
0.0, 1.0, 0.0, 0.0,
|
||||
0.0, 0.0, 1.0, 0.0,
|
||||
0.0, 0.0, 0.0, 1.0,
|
||||
};
|
||||
|
||||
static const gfloat from_ndc_matrix[] = {
|
||||
0.5, 0.0, 0.0, 0.0,
|
||||
0.0, 0.5, 0.0, 0.0,
|
||||
0.0, 0.0, 0.5, 0.0,
|
||||
0.5, 0.5, 0.5, 1.0,
|
||||
};
|
||||
|
||||
static const gfloat to_ndc_matrix[] = {
|
||||
2.0, 0.0, 0.0, 0.0,
|
||||
0.0, 2.0, 0.0, 0.0,
|
||||
0.0, 0.0, 2.0, 0.0,
|
||||
-1.0, -1.0, -1.0, 1.0,
|
||||
};
|
||||
|
||||
/* multiplies two 4x4 matrices, @a X @b, and stores the result in @result
|
||||
* https://en.wikipedia.org/wiki/Matrix_multiplication
|
||||
*/
|
||||
static void
|
||||
gst_gl_multiply_matrix4 (const gfloat * a, const gfloat * b, gfloat * result)
|
||||
{
|
||||
int i, j, k;
|
||||
gfloat tmp[16] = { 0.0f };
|
||||
|
||||
if (!a || !b || !result)
|
||||
return;
|
||||
for (i = 0; i < 4; i++) { /* column */
|
||||
for (j = 0; j < 4; j++) { /* row */
|
||||
for (k = 0; k < 4; k++) {
|
||||
tmp[j + (i * 4)] += a[k + (i * 4)] * b[j + (k * 4)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
result[i] = tmp[i];
|
||||
}
|
||||
|
||||
/*
|
||||
* gst_clapper_gl_get_affine_transformation_meta_as_ndc:
|
||||
* @meta: (nullable): a #GstVideoAffineTransformationMeta
|
||||
* @matrix: (out): result of the 4x4 matrix
|
||||
*
|
||||
* Retrieves the stored 4x4 affine transformation matrix stored in @meta in
|
||||
* NDC coordinates. if @meta is NULL, an identity matrix is returned.
|
||||
*
|
||||
* NDC is a left-handed coordinate system
|
||||
* - x - [-1, 1] - +ve X moves right
|
||||
* - y - [-1, 1] - +ve Y moves up
|
||||
* - z - [-1, 1] - +ve Z moves into
|
||||
*/
|
||||
void
|
||||
gst_clapper_gl_get_affine_transformation_meta_as_ndc (GstVideoAffineTransformationMeta *
|
||||
meta, gfloat * matrix)
|
||||
{
|
||||
if (!meta) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
matrix[i] = identity_matrix[i];
|
||||
}
|
||||
} else {
|
||||
float tmp[16];
|
||||
|
||||
/* change of basis multiplications */
|
||||
gst_gl_multiply_matrix4 (from_ndc_matrix, meta->matrix, tmp);
|
||||
gst_gl_multiply_matrix4 (tmp, to_ndc_matrix, matrix);
|
||||
}
|
||||
}
|
@@ -1,4 +1,6 @@
|
||||
/*
|
||||
* GStreamer
|
||||
* Copyright (C) 2016 Matthew Waters <matthew@centricular.com>
|
||||
* Copyright (C) 2021 Rafał Dzięgiel <rafostar.github@gmail.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
@@ -17,27 +19,11 @@
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_CLAPPER_PLAYLIST_PRIVATE_H__
|
||||
#define __GST_CLAPPER_PLAYLIST_PRIVATE_H__
|
||||
#ifndef __GST_CLAPPER_GL_UTILS_H__
|
||||
#define __GST_CLAPPER_GL_UTILS_H__
|
||||
|
||||
#include "gstclapper-playlist.h"
|
||||
#include <gst/video/gstvideoaffinetransformationmeta.h>
|
||||
|
||||
struct _GstClapperPlaylist
|
||||
{
|
||||
GstObject parent;
|
||||
void gst_clapper_gl_get_affine_transformation_meta_as_ndc (GstVideoAffineTransformationMeta *meta, gfloat *matrix);
|
||||
|
||||
gchar *uuid;
|
||||
gint id_count;
|
||||
GArray *items;
|
||||
gint active_index;
|
||||
};
|
||||
|
||||
struct _GstClapperPlaylistClass
|
||||
{
|
||||
GstObjectClass parent_class;
|
||||
};
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
void gst_clapper_playlist_emit_item_activated (GstClapperPlaylist *playlist, GstClapperPlaylistItem *item);
|
||||
|
||||
#endif /* __GST_CLAPPER_PLAYLIST_PRIVATE_H__ */
|
||||
#endif /* __GST_CLAPPER_GL_UTILS_H__ */
|
@@ -19,6 +19,7 @@
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gstgtkutils.h"
|
||||
|
||||
struct invoke_context
|
||||
@@ -69,3 +70,26 @@ gst_gtk_invoke_on_main (GThreadFunc func, gpointer data)
|
||||
|
||||
return info.res;
|
||||
}
|
||||
|
||||
void
|
||||
gst_gtk_install_shared_properties (GObjectClass *gobject_class)
|
||||
{
|
||||
g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
|
||||
g_param_spec_boolean ("force-aspect-ratio",
|
||||
"Force aspect ratio",
|
||||
"When enabled, scaling will respect original aspect ratio",
|
||||
DEFAULT_FORCE_ASPECT_RATIO,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
|
||||
gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio",
|
||||
"The pixel aspect ratio of the device", DEFAULT_PAR_N, DEFAULT_PAR_D,
|
||||
G_MAXINT, 1, 1, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_KEEP_LAST_FRAME,
|
||||
g_param_spec_boolean ("keep-last-frame",
|
||||
"Keep last frame",
|
||||
"Keep showing last video frame after playback instead of black screen",
|
||||
DEFAULT_KEEP_LAST_FRAME,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
@@ -22,8 +22,25 @@
|
||||
#ifndef __GST_GTK_UTILS_H__
|
||||
#define __GST_GTK_UTILS_H__
|
||||
|
||||
#define DEFAULT_FORCE_ASPECT_RATIO TRUE
|
||||
#define DEFAULT_PAR_N 0
|
||||
#define DEFAULT_PAR_D 1
|
||||
#define DEFAULT_KEEP_LAST_FRAME FALSE
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_WIDGET,
|
||||
PROP_FORCE_ASPECT_RATIO,
|
||||
PROP_PIXEL_ASPECT_RATIO,
|
||||
PROP_KEEP_LAST_FRAME
|
||||
};
|
||||
|
||||
gpointer gst_gtk_invoke_on_main (GThreadFunc func, gpointer data);
|
||||
|
||||
void gst_gtk_install_shared_properties (GObjectClass *gobject_class);
|
||||
|
||||
#endif /* __GST_GTK_UTILS_H__ */
|
||||
|
@@ -26,9 +26,11 @@
|
||||
#include <stdio.h>
|
||||
#include <gst/gl/gstglfuncs.h>
|
||||
#include <gst/video/video.h>
|
||||
#include <gst/video/gstvideoaffinetransformationmeta.h>
|
||||
|
||||
#include "gtkclapperglwidget.h"
|
||||
#include "gstgtkutils.h"
|
||||
#include "gstclapperglutils.h"
|
||||
|
||||
#if GST_GL_HAVE_WINDOW_X11 && defined (GDK_WINDOWING_X11)
|
||||
#include <gdk/x11/gdkx.h>
|
||||
@@ -57,24 +59,27 @@
|
||||
GST_DEBUG_CATEGORY (gst_debug_clapper_gl_widget);
|
||||
#define GST_CAT_DEFAULT gst_debug_clapper_gl_widget
|
||||
|
||||
#define DEFAULT_FORCE_ASPECT_RATIO TRUE
|
||||
#define DEFAULT_PAR_N 0
|
||||
#define DEFAULT_PAR_D 1
|
||||
|
||||
struct _GtkClapperGLWidgetPrivate
|
||||
{
|
||||
gboolean initiated;
|
||||
|
||||
GstGLDisplay *display;
|
||||
GdkGLContext *gdk_context;
|
||||
GstGLContext *other_context;
|
||||
GstGLContext *context;
|
||||
|
||||
GstGLTextureTarget texture_target;
|
||||
guint gl_target;
|
||||
|
||||
GstGLUpload *upload;
|
||||
GstGLShader *shader;
|
||||
|
||||
GLuint vao;
|
||||
GLuint vertex_buffer;
|
||||
GLint attr_position;
|
||||
GLint attr_texture;
|
||||
GLuint current_tex;
|
||||
|
||||
GstGLOverlayCompositor *overlay_compositor;
|
||||
};
|
||||
|
||||
@@ -94,13 +99,6 @@ G_DEFINE_TYPE_WITH_CODE (GtkClapperGLWidget, gtk_clapper_gl_widget, GTK_TYPE_GL_
|
||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gtkclapperglwidget", 0,
|
||||
"GTK Clapper GL Widget"));
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_FORCE_ASPECT_RATIO,
|
||||
PROP_PIXEL_ASPECT_RATIO,
|
||||
};
|
||||
|
||||
static void
|
||||
gtk_clapper_gl_widget_get_preferred_width (GtkWidget * widget, gint * min,
|
||||
gint * natural)
|
||||
@@ -174,6 +172,9 @@ gtk_clapper_gl_widget_set_property (GObject * object, guint prop_id,
|
||||
clapper_widget->par_n = gst_value_get_fraction_numerator (value);
|
||||
clapper_widget->par_d = gst_value_get_fraction_denominator (value);
|
||||
break;
|
||||
case PROP_KEEP_LAST_FRAME:
|
||||
clapper_widget->keep_last_frame = g_value_get_boolean (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
@@ -193,6 +194,9 @@ gtk_clapper_gl_widget_get_property (GObject * object, guint prop_id,
|
||||
case PROP_PIXEL_ASPECT_RATIO:
|
||||
gst_value_set_fraction (value, clapper_widget->par_n, clapper_widget->par_d);
|
||||
break;
|
||||
case PROP_KEEP_LAST_FRAME:
|
||||
g_value_set_boolean (value, clapper_widget->keep_last_frame);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
@@ -316,6 +320,22 @@ _gdk_key_to_navigation_string (guint keyval)
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_get_is_navigation_allowed (GstElement * element, GstState min_state)
|
||||
{
|
||||
if (GST_IS_NAVIGATION (element)) {
|
||||
GstState nav_state;
|
||||
|
||||
GST_OBJECT_LOCK (element);
|
||||
nav_state = element->current_state;
|
||||
GST_OBJECT_UNLOCK (element);
|
||||
|
||||
return nav_state >= min_state;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_clapper_gl_widget_key_event (GtkEventControllerKey * key_controller,
|
||||
guint keyval, guint keycode, GdkModifierType state)
|
||||
@@ -326,7 +346,7 @@ gtk_clapper_gl_widget_key_event (GtkEventControllerKey * key_controller,
|
||||
GstElement *element;
|
||||
|
||||
if ((element = g_weak_ref_get (&clapper_widget->element))) {
|
||||
if (GST_IS_NAVIGATION (element)) {
|
||||
if (_get_is_navigation_allowed (element, GST_STATE_PAUSED)) {
|
||||
GdkEvent *event = gtk_event_controller_get_current_event (controller);
|
||||
const gchar *str = _gdk_key_to_navigation_string (keyval);
|
||||
|
||||
@@ -415,8 +435,11 @@ gtk_clapper_gl_widget_button_event (GtkGestureClick * gesture,
|
||||
GtkClapperGLWidget *clapper_widget = GTK_CLAPPER_GL_WIDGET (widget);
|
||||
GstElement *element;
|
||||
|
||||
if (clapper_widget->display_width == 0 || clapper_widget->display_height == 0)
|
||||
return FALSE;
|
||||
|
||||
if ((element = g_weak_ref_get (&clapper_widget->element))) {
|
||||
if (GST_IS_NAVIGATION (element)) {
|
||||
if (_get_is_navigation_allowed (element, GST_STATE_PLAYING)) {
|
||||
GdkEvent *event = gtk_event_controller_get_current_event (controller);
|
||||
const gchar *key_type =
|
||||
gdk_event_get_event_type (event) == GDK_BUTTON_PRESS
|
||||
@@ -445,9 +468,12 @@ gtk_clapper_gl_widget_motion_event (GtkEventControllerMotion * motion_controller
|
||||
GtkClapperGLWidget *clapper_widget = GTK_CLAPPER_GL_WIDGET (widget);
|
||||
GstElement *element;
|
||||
|
||||
if (x != clapper_widget->last_pos_x && y != clapper_widget->last_pos_y &&
|
||||
(element = g_weak_ref_get (&clapper_widget->element))) {
|
||||
if (GST_IS_NAVIGATION (element)) {
|
||||
if ((x == clapper_widget->last_pos_x && y == clapper_widget->last_pos_y) ||
|
||||
clapper_widget->display_width == 0 || clapper_widget->display_height == 0)
|
||||
return FALSE;
|
||||
|
||||
if ((element = g_weak_ref_get (&clapper_widget->element))) {
|
||||
if (_get_is_navigation_allowed (element, GST_STATE_PLAYING)) {
|
||||
gdouble stream_x, stream_y;
|
||||
|
||||
clapper_widget->last_pos_x = x;
|
||||
@@ -508,9 +534,42 @@ gtk_clapper_gl_widget_init_redisplay (GtkClapperGLWidget * clapper_widget)
|
||||
GtkClapperGLWidgetPrivate *priv = clapper_widget->priv;
|
||||
const GstGLFuncs *gl = priv->context->gl_vtable;
|
||||
GError *error = NULL;
|
||||
GstGLSLStage *frag_stage, *vert_stage;
|
||||
|
||||
gst_gl_insert_debug_marker (priv->other_context, "initializing redisplay");
|
||||
if (!(priv->shader = gst_gl_shader_new_default (priv->context, &error))) {
|
||||
|
||||
vert_stage = gst_glsl_stage_new_with_string (priv->context,
|
||||
GL_VERTEX_SHADER, GST_GLSL_VERSION_NONE,
|
||||
GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY,
|
||||
gst_gl_shader_string_vertex_mat4_vertex_transform);
|
||||
if (priv->texture_target == GST_GL_TEXTURE_TARGET_EXTERNAL_OES) {
|
||||
gchar *frag_str;
|
||||
|
||||
frag_str =
|
||||
gst_gl_shader_string_fragment_external_oes_get_default
|
||||
(priv->context, GST_GLSL_VERSION_NONE,
|
||||
GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY);
|
||||
frag_stage = gst_glsl_stage_new_with_string (priv->context,
|
||||
GL_FRAGMENT_SHADER, GST_GLSL_VERSION_NONE,
|
||||
GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY, frag_str);
|
||||
|
||||
g_free (frag_str);
|
||||
} else {
|
||||
frag_stage = gst_glsl_stage_new_default_fragment (priv->context);
|
||||
}
|
||||
|
||||
if (!vert_stage || !frag_stage) {
|
||||
GST_ERROR ("Failed to retrieve fragment shader for texture target");
|
||||
if (vert_stage)
|
||||
gst_object_unref (vert_stage);
|
||||
if (frag_stage)
|
||||
gst_object_unref (frag_stage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(priv->shader =
|
||||
gst_gl_shader_new_link_with_stages (priv->context, &error,
|
||||
vert_stage, frag_stage, NULL))) {
|
||||
GST_ERROR ("Failed to initialize shader: %s", error->message);
|
||||
return;
|
||||
}
|
||||
@@ -537,8 +596,9 @@ gtk_clapper_gl_widget_init_redisplay (GtkClapperGLWidget * clapper_widget)
|
||||
|
||||
gl->BindBuffer (GL_ARRAY_BUFFER, 0);
|
||||
|
||||
priv->overlay_compositor =
|
||||
gst_gl_overlay_compositor_new (priv->other_context);
|
||||
if (!priv->overlay_compositor)
|
||||
priv->overlay_compositor =
|
||||
gst_gl_overlay_compositor_new (priv->other_context);
|
||||
|
||||
priv->initiated = TRUE;
|
||||
}
|
||||
@@ -569,6 +629,9 @@ gtk_clapper_gl_widget_render (GtkGLArea * widget, GdkGLContext * context)
|
||||
GtkClapperGLWidgetPrivate *priv = clapper_widget->priv;
|
||||
const GstGLFuncs *gl;
|
||||
|
||||
GstVideoAffineTransformationMeta *af_meta;
|
||||
gfloat matrix[16];
|
||||
|
||||
GTK_CLAPPER_GL_WIDGET_LOCK (clapper_widget);
|
||||
|
||||
/* Draw black with GDK context when priv is not available yet.
|
||||
@@ -656,9 +719,15 @@ gtk_clapper_gl_widget_render (GtkGLArea * widget, GdkGLContext * context)
|
||||
gtk_clapper_gl_widget_bind_buffer (clapper_widget);
|
||||
|
||||
gl->ActiveTexture (GL_TEXTURE0);
|
||||
gl->BindTexture (GL_TEXTURE_2D, priv->current_tex);
|
||||
gl->BindTexture (priv->gl_target, priv->current_tex);
|
||||
gst_gl_shader_set_uniform_1i (priv->shader, "tex", 0);
|
||||
|
||||
af_meta = gst_buffer_get_video_affine_transformation_meta (
|
||||
clapper_widget->buffer);
|
||||
gst_clapper_gl_get_affine_transformation_meta_as_ndc (af_meta, matrix);
|
||||
gst_gl_shader_set_uniform_matrix_4fv (priv->shader,
|
||||
"u_transformation", 1, FALSE, matrix);
|
||||
|
||||
gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
|
||||
|
||||
if (gl->BindVertexArray)
|
||||
@@ -666,7 +735,7 @@ gtk_clapper_gl_widget_render (GtkGLArea * widget, GdkGLContext * context)
|
||||
else
|
||||
gtk_clapper_gl_widget_unbind_buffer (clapper_widget);
|
||||
|
||||
gl->BindTexture (GL_TEXTURE_2D, 0);
|
||||
gl->BindTexture (priv->gl_target, 0);
|
||||
|
||||
/* Draw subtitles */
|
||||
gst_gl_overlay_compositor_draw_overlays (priv->overlay_compositor);
|
||||
@@ -680,10 +749,34 @@ done:
|
||||
}
|
||||
|
||||
static void
|
||||
_reset_gl (GtkClapperGLWidget * clapper_widget)
|
||||
_cleanup_gl_private (GtkClapperGLWidgetPrivate * priv)
|
||||
{
|
||||
const GstGLFuncs *gl = priv->other_context->gl_vtable;
|
||||
|
||||
if (priv->vao) {
|
||||
gl->DeleteVertexArrays (1, &priv->vao);
|
||||
priv->vao = 0;
|
||||
}
|
||||
if (priv->vertex_buffer) {
|
||||
gl->DeleteBuffers (1, &priv->vertex_buffer);
|
||||
priv->vertex_buffer = 0;
|
||||
}
|
||||
if (priv->upload) {
|
||||
gst_object_unref (priv->upload);
|
||||
priv->upload = NULL;
|
||||
}
|
||||
if (priv->shader) {
|
||||
gst_object_unref (priv->shader);
|
||||
priv->shader = NULL;
|
||||
}
|
||||
if (priv->overlay_compositor)
|
||||
gst_gl_overlay_compositor_free_overlays (priv->overlay_compositor);
|
||||
}
|
||||
|
||||
static void
|
||||
_cleanup_gl_thread (GtkClapperGLWidget * clapper_widget)
|
||||
{
|
||||
GtkClapperGLWidgetPrivate *priv = clapper_widget->priv;
|
||||
const GstGLFuncs *gl = priv->other_context->gl_vtable;
|
||||
|
||||
if (!priv->gdk_context)
|
||||
priv->gdk_context = gtk_gl_area_get_context (GTK_GL_AREA (clapper_widget));
|
||||
@@ -694,25 +787,29 @@ _reset_gl (GtkClapperGLWidget * clapper_widget)
|
||||
gdk_gl_context_make_current (priv->gdk_context);
|
||||
gst_gl_context_activate (priv->other_context, TRUE);
|
||||
|
||||
if (priv->vao) {
|
||||
gl->DeleteVertexArrays (1, &priv->vao);
|
||||
priv->vao = 0;
|
||||
}
|
||||
_cleanup_gl_private (priv);
|
||||
|
||||
if (priv->vertex_buffer) {
|
||||
gl->DeleteBuffers (1, &priv->vertex_buffer);
|
||||
priv->vertex_buffer = 0;
|
||||
}
|
||||
gst_gl_context_activate (priv->other_context, FALSE);
|
||||
gdk_gl_context_clear_current ();
|
||||
|
||||
if (priv->upload) {
|
||||
gst_object_unref (priv->upload);
|
||||
priv->upload = NULL;
|
||||
}
|
||||
priv->initiated = FALSE;
|
||||
}
|
||||
|
||||
if (priv->shader) {
|
||||
gst_object_unref (priv->shader);
|
||||
priv->shader = NULL;
|
||||
}
|
||||
static void
|
||||
_reset_gl (GtkClapperGLWidget * clapper_widget)
|
||||
{
|
||||
GtkClapperGLWidgetPrivate *priv = clapper_widget->priv;
|
||||
|
||||
if (!priv->gdk_context)
|
||||
priv->gdk_context = gtk_gl_area_get_context (GTK_GL_AREA (clapper_widget));
|
||||
|
||||
if (priv->gdk_context == NULL)
|
||||
return;
|
||||
|
||||
gdk_gl_context_make_current (priv->gdk_context);
|
||||
gst_gl_context_activate (priv->other_context, TRUE);
|
||||
|
||||
_cleanup_gl_private (priv);
|
||||
|
||||
if (priv->overlay_compositor)
|
||||
gst_object_unref (priv->overlay_compositor);
|
||||
@@ -803,8 +900,9 @@ gtk_clapper_gl_widget_set_buffer (GtkClapperGLWidget * clapper_widget,
|
||||
GTK_CLAPPER_GL_WIDGET_UNLOCK (clapper_widget);
|
||||
}
|
||||
|
||||
static GstGLAPI
|
||||
_get_current_gl_api (GstGLPlatform platform)
|
||||
static gboolean
|
||||
_wrap_current_gl (GstGLDisplay * display, GstGLPlatform platform,
|
||||
GstGLContext ** other_context)
|
||||
{
|
||||
GstGLAPI gl_api = GST_GL_API_NONE;
|
||||
guint gl_major = 0, gl_minor = 0;
|
||||
@@ -813,11 +911,26 @@ _get_current_gl_api (GstGLPlatform platform)
|
||||
|
||||
if (gl_api) {
|
||||
const gboolean is_es = gl_api & (GST_GL_API_GLES1 | GST_GL_API_GLES2);
|
||||
gchar *gl_api_str = gst_gl_api_to_string (gl_api);
|
||||
guintptr gl_handle = 0;
|
||||
|
||||
GST_INFO ("Using OpenGL%s %d.%d", is_es ? " ES" : "", gl_major, gl_minor);
|
||||
GST_INFO ("Using GL API: %s, ver: %d.%d", gl_api_str, gl_major, gl_minor);
|
||||
g_free (gl_api_str);
|
||||
|
||||
if (is_es && platform == GST_GL_PLATFORM_EGL && !g_getenv ("GST_GL_API")) {
|
||||
GST_DEBUG ("No GST_GL_API env and GTK is using EGL GLES2, enforcing it");
|
||||
gst_gl_display_filter_gl_api (display, GST_GL_API_GLES2);
|
||||
}
|
||||
|
||||
gl_handle = gst_gl_context_get_current_gl_context (platform);
|
||||
if (gl_handle) {
|
||||
if ((*other_context = gst_gl_context_new_wrapped (display,
|
||||
gl_handle, platform, gl_api)))
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return gl_api;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -825,8 +938,6 @@ _get_gl_context (GtkClapperGLWidget * clapper_widget)
|
||||
{
|
||||
GtkClapperGLWidgetPrivate *priv = clapper_widget->priv;
|
||||
GstGLPlatform platform = GST_GL_PLATFORM_NONE;
|
||||
GstGLAPI gl_api = GST_GL_API_NONE;
|
||||
guintptr gl_handle = 0;
|
||||
|
||||
gtk_widget_realize (GTK_WIDGET (clapper_widget));
|
||||
|
||||
@@ -848,53 +959,45 @@ _get_gl_context (GtkClapperGLWidget * clapper_widget)
|
||||
}
|
||||
|
||||
g_object_ref (priv->gdk_context);
|
||||
|
||||
gdk_gl_context_make_current (priv->gdk_context);
|
||||
|
||||
#if GST_GL_HAVE_WINDOW_WAYLAND && GST_GL_HAVE_PLATFORM_EGL && defined (GDK_WINDOWING_WAYLAND)
|
||||
if (GST_IS_GL_DISPLAY_WAYLAND (priv->display)) {
|
||||
platform = GST_GL_PLATFORM_EGL;
|
||||
GST_DEBUG ("Using EGL on Wayland");
|
||||
goto have_platform;
|
||||
}
|
||||
#endif
|
||||
#if GST_GL_HAVE_WINDOW_X11 && defined (GDK_WINDOWING_X11)
|
||||
#if GST_GL_HAVE_PLATFORM_EGL
|
||||
if (GST_IS_GL_DISPLAY_EGL (priv->display)) {
|
||||
platform = GST_GL_PLATFORM_EGL;
|
||||
gl_handle = gst_gl_context_get_current_gl_context (platform);
|
||||
GST_DEBUG ("Using EGL on x11");
|
||||
goto have_platform;
|
||||
}
|
||||
#endif
|
||||
#if GST_GL_HAVE_PLATFORM_GLX
|
||||
if (!gl_handle && GST_IS_GL_DISPLAY_X11 (priv->display)) {
|
||||
if (GST_IS_GL_DISPLAY_X11 (priv->display)) {
|
||||
platform = GST_GL_PLATFORM_GLX;
|
||||
gl_handle = gst_gl_context_get_current_gl_context (platform);
|
||||
GST_DEBUG ("Using GLX on x11");
|
||||
goto have_platform;
|
||||
}
|
||||
#endif
|
||||
if (gl_handle) {
|
||||
gl_api = _get_current_gl_api (platform);
|
||||
priv->other_context =
|
||||
gst_gl_context_new_wrapped (priv->display, gl_handle,
|
||||
platform, gl_api);
|
||||
}
|
||||
#endif
|
||||
#if GST_GL_HAVE_WINDOW_WAYLAND && GST_GL_HAVE_PLATFORM_EGL && defined (GDK_WINDOWING_WAYLAND)
|
||||
if (GST_IS_GL_DISPLAY_WAYLAND (priv->display)) {
|
||||
platform = GST_GL_PLATFORM_EGL;
|
||||
gl_api = _get_current_gl_api (platform);
|
||||
gl_handle = gst_gl_context_get_current_gl_context (platform);
|
||||
if (gl_handle)
|
||||
priv->other_context =
|
||||
gst_gl_context_new_wrapped (priv->display, gl_handle,
|
||||
platform, gl_api);
|
||||
}
|
||||
#endif
|
||||
|
||||
(void) platform;
|
||||
(void) gl_api;
|
||||
(void) gl_handle;
|
||||
GST_ERROR ("Unknown GL platform");
|
||||
return;
|
||||
|
||||
if (priv->other_context) {
|
||||
have_platform:
|
||||
|
||||
if (_wrap_current_gl (priv->display, platform, &priv->other_context)) {
|
||||
GError *error = NULL;
|
||||
|
||||
GST_INFO ("Retrieved Gdk OpenGL context %" GST_PTR_FORMAT,
|
||||
priv->other_context);
|
||||
gst_gl_context_activate (priv->other_context, TRUE);
|
||||
if (!gst_gl_context_fill_info (priv->other_context, &error)) {
|
||||
GST_ERROR ("failed to retrieve gdk context info: %s", error->message);
|
||||
GST_ERROR ("Failed to retrieve gdk context info: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
g_object_unref (priv->other_context);
|
||||
priv->other_context = NULL;
|
||||
@@ -909,30 +1012,20 @@ _get_gl_context (GtkClapperGLWidget * clapper_widget)
|
||||
static void
|
||||
gtk_clapper_gl_widget_class_init (GtkClapperGLWidgetClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_klass = (GObjectClass *) klass;
|
||||
GtkWidgetClass *widget_klass = (GtkWidgetClass *) klass;
|
||||
GtkGLAreaClass *gl_area_klass = (GtkGLAreaClass *) klass;
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
|
||||
GtkGLAreaClass *gl_area_class = (GtkGLAreaClass *) klass;
|
||||
|
||||
gobject_klass->set_property = gtk_clapper_gl_widget_set_property;
|
||||
gobject_klass->get_property = gtk_clapper_gl_widget_get_property;
|
||||
gobject_klass->finalize = gtk_clapper_gl_widget_finalize;
|
||||
gobject_class->set_property = gtk_clapper_gl_widget_set_property;
|
||||
gobject_class->get_property = gtk_clapper_gl_widget_get_property;
|
||||
gobject_class->finalize = gtk_clapper_gl_widget_finalize;
|
||||
|
||||
g_object_class_install_property (gobject_klass, PROP_FORCE_ASPECT_RATIO,
|
||||
g_param_spec_boolean ("force-aspect-ratio",
|
||||
"Force aspect ratio",
|
||||
"When enabled, scaling will respect original aspect ratio",
|
||||
DEFAULT_FORCE_ASPECT_RATIO,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
gst_gtk_install_shared_properties (gobject_class);
|
||||
|
||||
g_object_class_install_property (gobject_klass, PROP_PIXEL_ASPECT_RATIO,
|
||||
gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio",
|
||||
"The pixel aspect ratio of the device", DEFAULT_PAR_N, DEFAULT_PAR_D,
|
||||
G_MAXINT, 1, 1, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
widget_class->measure = gtk_clapper_gl_widget_measure;
|
||||
widget_class->size_allocate = gtk_clapper_gl_widget_size_allocate;
|
||||
|
||||
widget_klass->measure = gtk_clapper_gl_widget_measure;
|
||||
widget_klass->size_allocate = gtk_clapper_gl_widget_size_allocate;
|
||||
|
||||
gl_area_klass->render = gtk_clapper_gl_widget_render;
|
||||
gl_area_class->render = gtk_clapper_gl_widget_render;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -945,6 +1038,7 @@ gtk_clapper_gl_widget_init (GtkClapperGLWidget * clapper_widget)
|
||||
clapper_widget->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
|
||||
clapper_widget->par_n = DEFAULT_PAR_N;
|
||||
clapper_widget->par_d = DEFAULT_PAR_D;
|
||||
clapper_widget->keep_last_frame = DEFAULT_KEEP_LAST_FRAME;
|
||||
clapper_widget->ignore_buffers = FALSE;
|
||||
clapper_widget->last_pos_x = 0;
|
||||
clapper_widget->last_pos_y = 0;
|
||||
@@ -993,7 +1087,7 @@ gtk_clapper_gl_widget_init (GtkClapperGLWidget * clapper_widget)
|
||||
#if GST_GL_HAVE_WINDOW_X11 && defined (GDK_WINDOWING_X11)
|
||||
if (GDK_IS_X11_DISPLAY (display)) {
|
||||
gpointer display_ptr;
|
||||
#if GST_GL_HAVE_PLATFORM_EGL && GTK_CHECK_VERSION(4,4,0)
|
||||
#if GST_GL_HAVE_PLATFORM_EGL && GTK_CHECK_VERSION(4,3,1)
|
||||
display_ptr = gdk_x11_display_get_egl_display (display);
|
||||
if (display_ptr)
|
||||
priv->display = (GstGLDisplay *)
|
||||
@@ -1024,6 +1118,9 @@ gtk_clapper_gl_widget_init (GtkClapperGLWidget * clapper_widget)
|
||||
|
||||
GST_INFO ("Created %" GST_PTR_FORMAT, priv->display);
|
||||
|
||||
priv->texture_target = GST_GL_TEXTURE_TARGET_NONE;
|
||||
priv->gl_target = 0;
|
||||
|
||||
gtk_gl_area_set_auto_render (GTK_GL_AREA (widget), FALSE);
|
||||
|
||||
g_signal_connect_swapped (gtk_widget_get_settings (widget), "notify",
|
||||
@@ -1108,3 +1205,44 @@ gtk_clapper_gl_widget_get_display (GtkClapperGLWidget * clapper_widget)
|
||||
|
||||
return gst_object_ref (clapper_widget->priv->display);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_clapper_gl_widget_update_output_format (GtkClapperGLWidget * clapper_widget,
|
||||
GstCaps * caps)
|
||||
{
|
||||
GtkClapperGLWidgetPrivate *priv;
|
||||
GstGLTextureTarget previous_target;
|
||||
GstStructure *structure;
|
||||
const gchar *target_str;
|
||||
gboolean cleanup_gl;
|
||||
|
||||
GTK_CLAPPER_GL_WIDGET_LOCK (clapper_widget);
|
||||
priv = clapper_widget->priv;
|
||||
|
||||
previous_target = priv->texture_target;
|
||||
structure = gst_caps_get_structure (caps, 0);
|
||||
target_str = gst_structure_get_string (structure, "texture-target");
|
||||
|
||||
if (!target_str)
|
||||
target_str = GST_GL_TEXTURE_TARGET_2D_STR;
|
||||
|
||||
priv->texture_target = gst_gl_texture_target_from_string (target_str);
|
||||
if (!priv->texture_target)
|
||||
goto fail;
|
||||
|
||||
GST_DEBUG_OBJECT (clapper_widget, "Using texture-target: %s", target_str);
|
||||
priv->gl_target = gst_gl_texture_target_to_gl (priv->texture_target);
|
||||
|
||||
cleanup_gl = (previous_target != GST_GL_TEXTURE_TARGET_NONE &&
|
||||
priv->texture_target != previous_target);
|
||||
|
||||
GTK_CLAPPER_GL_WIDGET_UNLOCK (clapper_widget);
|
||||
if (cleanup_gl)
|
||||
gst_gtk_invoke_on_main ((GThreadFunc) (GCallback) _cleanup_gl_thread, clapper_widget);
|
||||
|
||||
return TRUE;
|
||||
|
||||
fail:
|
||||
GTK_CLAPPER_GL_WIDGET_UNLOCK (clapper_widget);
|
||||
return FALSE;
|
||||
}
|
||||
|
@@ -52,6 +52,7 @@ struct _GtkClapperGLWidget
|
||||
/* properties */
|
||||
gboolean force_aspect_ratio;
|
||||
gint par_n, par_d;
|
||||
gboolean keep_last_frame;
|
||||
|
||||
gint display_width;
|
||||
gint display_height;
|
||||
@@ -99,12 +100,13 @@ gboolean gtk_clapper_gl_widget_set_format (GtkClapperGLWidget *
|
||||
void gtk_clapper_gl_widget_set_buffer (GtkClapperGLWidget * widget, GstBuffer * buffer);
|
||||
void gtk_clapper_gl_widget_set_element (GtkClapperGLWidget * widget, GstElement * element);
|
||||
|
||||
GtkWidget * gtk_clapper_gl_widget_new (void);
|
||||
GtkWidget * gtk_clapper_gl_widget_new (void);
|
||||
|
||||
gboolean gtk_clapper_gl_widget_init_winsys (GtkClapperGLWidget * widget);
|
||||
GstGLDisplay * gtk_clapper_gl_widget_get_display (GtkClapperGLWidget * widget);
|
||||
GstGLContext * gtk_clapper_gl_widget_get_context (GtkClapperGLWidget * widget);
|
||||
GstGLContext * gtk_clapper_gl_widget_get_gtk_context (GtkClapperGLWidget * widget);
|
||||
gboolean gtk_clapper_gl_widget_update_output_format (GtkClapperGLWidget * widget, GstCaps * caps);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
67
lib/gst/clapper/meson.build
vendored
@@ -1,5 +1,3 @@
|
||||
gnome = import('gnome')
|
||||
|
||||
gstclapper_sources = [
|
||||
'gstclapper.c',
|
||||
'gstclapper-signal-dispatcher.c',
|
||||
@@ -8,14 +6,13 @@ gstclapper_sources = [
|
||||
'gstclapper-g-main-context-signal-dispatcher.c',
|
||||
'gstclapper-video-overlay-video-renderer.c',
|
||||
'gstclapper-visualization.c',
|
||||
'gstclapper-playlist.c',
|
||||
'gstclapper-playlist-item.c',
|
||||
'gstclapper-mpris.c',
|
||||
'gstclapper-gtk4-plugin.c',
|
||||
|
||||
'gtk4/gstclapperglsink.c',
|
||||
'gtk4/gstgtkutils.c',
|
||||
'gtk4/gtkclapperglwidget.c',
|
||||
'gtk4/gstclapperglutils.c',
|
||||
]
|
||||
gstclapper_headers = [
|
||||
'clapper.h',
|
||||
@@ -28,8 +25,6 @@ gstclapper_headers = [
|
||||
'gstclapper-g-main-context-signal-dispatcher.h',
|
||||
'gstclapper-video-overlay-video-renderer.h',
|
||||
'gstclapper-visualization.h',
|
||||
'gstclapper-playlist.h',
|
||||
'gstclapper-playlist-item.h',
|
||||
'gstclapper-mpris.h',
|
||||
'gstclapper-gtk4-plugin.h',
|
||||
]
|
||||
@@ -42,14 +37,18 @@ gstclapper_defines = [
|
||||
gtk_deps = [gstgl_dep, gstglproto_dep]
|
||||
have_gtk_gl_windowing = false
|
||||
|
||||
gtk4_dep = dependency('gtk4', required : true)
|
||||
gtk4_dep = dependency('gtk4', required: true)
|
||||
|
||||
if not gtk4_dep.version().version_compare('>=4.0.0')
|
||||
error('GTK4 version on this system is too old')
|
||||
endif
|
||||
|
||||
if not gir.found()
|
||||
error('Cannot build lib without GIR support')
|
||||
endif
|
||||
|
||||
if gst_gl_have_window_x11 and (gst_gl_have_platform_egl or gst_gl_have_platform_glx)
|
||||
gtk_x11_dep = dependency('gtk4-x11', required : false)
|
||||
gtk_x11_dep = dependency('gtk4-x11', required: false)
|
||||
if gtk_x11_dep.found()
|
||||
gtk_deps += gtk_x11_dep
|
||||
if gst_gl_have_platform_glx
|
||||
@@ -60,7 +59,7 @@ if gst_gl_have_window_x11 and (gst_gl_have_platform_egl or gst_gl_have_platform_
|
||||
endif
|
||||
|
||||
if gst_gl_have_window_wayland and gst_gl_have_platform_egl
|
||||
gtk_wayland_dep = dependency('gtk4-wayland', required : false)
|
||||
gtk_wayland_dep = dependency('gtk4-wayland', required: false)
|
||||
if gtk_wayland_dep.found()
|
||||
gtk_deps += [gtk_wayland_dep, gstglwayland_dep]
|
||||
have_gtk_gl_windowing = true
|
||||
@@ -81,31 +80,41 @@ gstclapper_mpris_gdbus = gnome.gdbus_codegen('gstclapper-mpris-gdbus',
|
||||
namespace: 'GstClapperMpris'
|
||||
)
|
||||
|
||||
gstclapper_deps = [
|
||||
gtk4_dep, glib_dep, gio_dep,
|
||||
gstbase_dep, gstvideo_dep, gstaudio_dep,
|
||||
gsttag_dep, gstpbutils_dep, libm
|
||||
] + gtk_deps
|
||||
|
||||
if os_unix
|
||||
gstclapper_deps += giounix_dep
|
||||
else
|
||||
gstclapper_deps += giowin_dep
|
||||
endif
|
||||
|
||||
gstclapper = library('gstclapper-' + api_version,
|
||||
gstclapper_sources + gstclapper_mpris_gdbus,
|
||||
c_args : gstclapper_defines,
|
||||
link_args : noseh_link_args,
|
||||
include_directories : [configinc, libsinc],
|
||||
version : libversion,
|
||||
install : true,
|
||||
install_dir : clapper_libdir,
|
||||
dependencies : [gtk4_dep, glib_dep, gio_dep,
|
||||
gstbase_dep, gstvideo_dep, gstaudio_dep,
|
||||
gsttag_dep, gstpbutils_dep, libm] + gtk_deps,
|
||||
c_args: gstclapper_defines,
|
||||
link_args: noseh_link_args,
|
||||
include_directories: [configinc, libsinc],
|
||||
version: libversion,
|
||||
install: true,
|
||||
install_dir: pkglibdir,
|
||||
dependencies: gstclapper_deps,
|
||||
)
|
||||
|
||||
clapper_gir = gnome.generate_gir(gstclapper,
|
||||
sources : gstclapper_sources + gstclapper_headers,
|
||||
namespace : 'GstClapper',
|
||||
nsversion : api_version,
|
||||
identifier_prefix : 'Gst',
|
||||
symbol_prefix : 'gst',
|
||||
export_packages : 'gstreamer-clapper-1.0',
|
||||
includes : ['Gst-1.0', 'GstPbutils-1.0', 'GstBase-1.0', 'GstVideo-1.0',
|
||||
sources: gstclapper_sources + gstclapper_headers,
|
||||
namespace: 'GstClapper',
|
||||
nsversion: api_version,
|
||||
identifier_prefix: 'Gst',
|
||||
symbol_prefix: 'gst',
|
||||
export_packages: 'gstreamer-clapper-1.0',
|
||||
includes: ['Gst-1.0', 'GstPbutils-1.0', 'GstBase-1.0', 'GstVideo-1.0',
|
||||
'GstAudio-1.0', 'GstTag-1.0'],
|
||||
install : true,
|
||||
install_dir_typelib : join_paths(clapper_libdir, 'girepository-1.0'),
|
||||
extra_args : gir_init_section + ['-DGST_USE_UNSTABLE_API'],
|
||||
dependencies : [gstbase_dep, gstvideo_dep, gstaudio_dep,
|
||||
install: true,
|
||||
install_dir_typelib: join_paths(pkglibdir, 'girepository-1.0'),
|
||||
extra_args: gir_init_section + ['-DGST_USE_UNSTABLE_API'],
|
||||
dependencies: [gstbase_dep, gstvideo_dep, gstaudio_dep,
|
||||
gsttag_dep, gstpbutils_dep]
|
||||
)
|
||||
|
@@ -1 +1,5 @@
|
||||
subdir('clapper')
|
||||
if get_option('lib')
|
||||
subdir('clapper')
|
||||
endif
|
||||
|
||||
subdir('plugin')
|
||||
|
86
lib/gst/plugin/gstclappercontexthandler.c
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstclappercontexthandler.h"
|
||||
|
||||
#define parent_class gst_clapper_context_handler_parent_class
|
||||
G_DEFINE_TYPE (GstClapperContextHandler, gst_clapper_context_handler, GST_TYPE_OBJECT);
|
||||
|
||||
static gboolean
|
||||
_default_handle_context_query (GstClapperContextHandler *self,
|
||||
GstBaseSink *bsink, GstQuery *query)
|
||||
{
|
||||
GST_FIXME_OBJECT (self, "Need to handle context query");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_context_handler_init (GstClapperContextHandler *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_context_handler_finalize (GObject *object)
|
||||
{
|
||||
GstClapperContextHandler *self = GST_CLAPPER_CONTEXT_HANDLER_CAST (object);
|
||||
|
||||
GST_TRACE_OBJECT (self, "Finalize");
|
||||
|
||||
GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_context_handler_class_init (GstClapperContextHandlerClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
GstClapperContextHandlerClass *handler_class = (GstClapperContextHandlerClass *) klass;
|
||||
|
||||
gobject_class->finalize = gst_clapper_context_handler_finalize;
|
||||
|
||||
handler_class->handle_context_query = _default_handle_context_query;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_clapper_context_handler_handle_context_query (GstClapperContextHandler *self,
|
||||
GstBaseSink *bsink, GstQuery *query)
|
||||
{
|
||||
GstClapperContextHandlerClass *handler_class = GST_CLAPPER_CONTEXT_HANDLER_GET_CLASS (self);
|
||||
|
||||
return handler_class->handle_context_query (self, bsink, query);
|
||||
}
|
||||
|
||||
GstClapperContextHandler *
|
||||
gst_clapper_context_handler_obtain_with_type (GPtrArray *context_handlers, GType type)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < context_handlers->len; i++) {
|
||||
GstClapperContextHandler *handler = g_ptr_array_index (context_handlers, i);
|
||||
|
||||
if (G_TYPE_CHECK_INSTANCE_TYPE (handler, type))
|
||||
return gst_object_ref (handler);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
62
lib/gst/plugin/gstclappercontexthandler.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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 <gst/gst.h>
|
||||
#include <gst/base/gstbasesink.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_CLAPPER_CONTEXT_HANDLER (gst_clapper_context_handler_get_type())
|
||||
#define GST_IS_CLAPPER_CONTEXT_HANDLER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CLAPPER_CONTEXT_HANDLER))
|
||||
#define GST_IS_CLAPPER_CONTEXT_HANDLER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_CLAPPER_CONTEXT_HANDLER))
|
||||
#define GST_CLAPPER_CONTEXT_HANDLER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CLAPPER_CONTEXT_HANDLER, GstClapperContextHandlerClass))
|
||||
#define GST_CLAPPER_CONTEXT_HANDLER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_CLAPPER_CONTEXT_HANDLER, GstClapperContextHandler))
|
||||
#define GST_CLAPPER_CONTEXT_HANDLER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_CLAPPER_CONTEXT_HANDLER, GstClapperContextHandlerClass))
|
||||
#define GST_CLAPPER_CONTEXT_HANDLER_CAST(obj) ((GstClapperContextHandler *)(obj))
|
||||
|
||||
typedef struct _GstClapperContextHandler GstClapperContextHandler;
|
||||
typedef struct _GstClapperContextHandlerClass GstClapperContextHandlerClass;
|
||||
|
||||
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GstClapperContextHandler, gst_object_unref)
|
||||
#endif
|
||||
|
||||
struct _GstClapperContextHandler
|
||||
{
|
||||
GstObject parent;
|
||||
};
|
||||
|
||||
struct _GstClapperContextHandlerClass
|
||||
{
|
||||
GstObjectClass parent_class;
|
||||
|
||||
gboolean (* handle_context_query) (GstClapperContextHandler *handler,
|
||||
GstBaseSink *bsink,
|
||||
GstQuery *query);
|
||||
};
|
||||
|
||||
GType gst_clapper_context_handler_get_type (void);
|
||||
|
||||
gboolean gst_clapper_context_handler_handle_context_query (GstClapperContextHandler *handler, GstBaseSink *bsink, GstQuery *query);
|
||||
|
||||
GstClapperContextHandler * gst_clapper_context_handler_obtain_with_type (GPtrArray *context_handlers, GType type);
|
||||
|
||||
G_END_DECLS
|
427
lib/gst/plugin/gstclapperimporter.c
Normal file
@@ -0,0 +1,427 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstclapperimporter.h"
|
||||
#include "gstgtkutils.h"
|
||||
|
||||
#define GST_CAT_DEFAULT gst_clapper_importer_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
#define parent_class gst_clapper_importer_parent_class
|
||||
G_DEFINE_TYPE (GstClapperImporter, gst_clapper_importer, GST_TYPE_OBJECT);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GdkTexture *texture;
|
||||
GstVideoOverlayRectangle *rectangle;
|
||||
|
||||
gint x, y;
|
||||
guint width, height;
|
||||
|
||||
gint index;
|
||||
gatomicrefcount ref_count;
|
||||
} GstClapperGdkOverlay;
|
||||
|
||||
static GstClapperGdkOverlay *
|
||||
gst_clapper_gdk_overlay_new (GdkTexture *texture, GstVideoOverlayRectangle *rectangle,
|
||||
gint x, gint y, guint width, guint height, guint index)
|
||||
{
|
||||
GstClapperGdkOverlay *overlay = g_slice_new (GstClapperGdkOverlay);
|
||||
|
||||
overlay->texture = g_object_ref (texture);
|
||||
overlay->rectangle = gst_video_overlay_rectangle_ref (rectangle);
|
||||
overlay->x = x;
|
||||
overlay->y = y;
|
||||
overlay->width = width;
|
||||
overlay->height = height;
|
||||
overlay->index = index;
|
||||
|
||||
g_atomic_ref_count_init (&overlay->ref_count);
|
||||
|
||||
return overlay;
|
||||
}
|
||||
|
||||
static GstClapperGdkOverlay *
|
||||
gst_clapper_gdk_overlay_ref (GstClapperGdkOverlay *overlay)
|
||||
{
|
||||
g_atomic_ref_count_inc (&overlay->ref_count);
|
||||
|
||||
return overlay;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gdk_overlay_unref (GstClapperGdkOverlay *overlay)
|
||||
{
|
||||
if (g_atomic_ref_count_dec (&overlay->ref_count)) {
|
||||
GST_TRACE ("Freeing overlay: %" GST_PTR_FORMAT, overlay);
|
||||
|
||||
g_object_unref (overlay->texture);
|
||||
gst_video_overlay_rectangle_unref (overlay->rectangle);
|
||||
g_slice_free (GstClapperGdkOverlay, overlay);
|
||||
}
|
||||
}
|
||||
|
||||
static GstBufferPool *
|
||||
_default_create_pool (GstClapperImporter *self, GstStructure **config)
|
||||
{
|
||||
GST_FIXME_OBJECT (self, "Need to create buffer pool");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
_default_add_allocation_metas (GstClapperImporter *importer, GstQuery *query)
|
||||
{
|
||||
/* Importer base class handles GstVideoOverlayCompositionMeta */
|
||||
gst_query_add_allocation_meta (query, GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, NULL);
|
||||
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
|
||||
}
|
||||
|
||||
static GdkTexture *
|
||||
_default_generate_texture (GstClapperImporter *self,
|
||||
GstBuffer *buffer, GstVideoInfo *v_info)
|
||||
{
|
||||
GST_FIXME_OBJECT (self, "GdkTexture generation not implemented");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_importer_init (GstClapperImporter *self)
|
||||
{
|
||||
gst_video_info_init (&self->pending_v_info);
|
||||
gst_video_info_init (&self->v_info);
|
||||
|
||||
self->pending_overlays = g_ptr_array_new_with_free_func (
|
||||
(GDestroyNotify) gst_clapper_gdk_overlay_unref);
|
||||
self->overlays = g_ptr_array_new_with_free_func (
|
||||
(GDestroyNotify) gst_clapper_gdk_overlay_unref);
|
||||
|
||||
gdk_rgba_parse (&self->bg, "black");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_importer_finalize (GObject *object)
|
||||
{
|
||||
GstClapperImporter *self = GST_CLAPPER_IMPORTER_CAST (object);
|
||||
|
||||
GST_TRACE ("Finalize");
|
||||
|
||||
gst_clear_caps (&self->pending_caps);
|
||||
|
||||
gst_clear_buffer (&self->pending_buffer);
|
||||
gst_clear_buffer (&self->buffer);
|
||||
|
||||
g_ptr_array_unref (self->pending_overlays);
|
||||
g_ptr_array_unref (self->overlays);
|
||||
|
||||
g_clear_object (&self->texture);
|
||||
|
||||
GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_importer_class_init (GstClapperImporterClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
GstClapperImporterClass *importer_class = (GstClapperImporterClass *) klass;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperimporter", 0,
|
||||
"Clapper Importer");
|
||||
|
||||
gobject_class->finalize = gst_clapper_importer_finalize;
|
||||
|
||||
importer_class->create_pool = _default_create_pool;
|
||||
importer_class->add_allocation_metas = _default_add_allocation_metas;
|
||||
importer_class->generate_texture = _default_generate_texture;
|
||||
}
|
||||
|
||||
static GstClapperGdkOverlay *
|
||||
_get_cached_overlay (GPtrArray *overlays, GstVideoOverlayRectangle *rectangle)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < overlays->len; i++) {
|
||||
GstClapperGdkOverlay *overlay = g_ptr_array_index (overlays, i);
|
||||
|
||||
if (overlay->rectangle == rectangle)
|
||||
return overlay;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gint
|
||||
_sort_overlays_cb (gconstpointer a, gconstpointer b)
|
||||
{
|
||||
GstClapperGdkOverlay *overlay_a, *overlay_b;
|
||||
|
||||
overlay_a = *((GstClapperGdkOverlay **) a);
|
||||
overlay_b = *((GstClapperGdkOverlay **) b);
|
||||
|
||||
return (overlay_a->index - overlay_b->index);
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepares overlays to show with the next rendered buffer.
|
||||
*
|
||||
* In order for overlays caching to work correctly, this should be called for
|
||||
* every received buffer (even if its going to be disgarded), also must be
|
||||
* called together with pending buffer replacement within a single importer
|
||||
* locking, to make sure prepared overlays always match the pending buffer.
|
||||
*/
|
||||
static void
|
||||
gst_clapper_importer_prepare_overlays_locked (GstClapperImporter *self)
|
||||
{
|
||||
GstVideoOverlayCompositionMeta *comp_meta;
|
||||
guint num_overlays, i;
|
||||
|
||||
if (G_UNLIKELY (!self->pending_buffer)
|
||||
|| !(comp_meta = gst_buffer_get_video_overlay_composition_meta (self->pending_buffer))) {
|
||||
guint n_pending = self->pending_overlays->len;
|
||||
|
||||
/* Remove all cached overlays if new buffer does not have any */
|
||||
if (n_pending > 0) {
|
||||
GST_TRACE ("No overlays in buffer, removing all cached ones");
|
||||
g_ptr_array_remove_range (self->pending_overlays, 0, n_pending);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (self, "Preparing overlays...");
|
||||
|
||||
/* Mark all old overlays as unused by giving them negative index */
|
||||
for (i = 0; i < self->pending_overlays->len; i++) {
|
||||
GstClapperGdkOverlay *overlay = g_ptr_array_index (self->pending_overlays, i);
|
||||
overlay->index = -1;
|
||||
}
|
||||
|
||||
num_overlays = gst_video_overlay_composition_n_rectangles (comp_meta->overlay);
|
||||
|
||||
for (i = 0; i < num_overlays; i++) {
|
||||
GdkTexture *texture;
|
||||
GstBuffer *comp_buffer;
|
||||
GstVideoFrame comp_frame;
|
||||
GstVideoMeta *v_meta;
|
||||
GstVideoInfo v_info;
|
||||
GstVideoOverlayRectangle *rectangle;
|
||||
GstClapperGdkOverlay *overlay;
|
||||
GstVideoOverlayFormatFlags flags, alpha_flags = 0;
|
||||
gint comp_x, comp_y;
|
||||
guint comp_width, comp_height;
|
||||
|
||||
rectangle = gst_video_overlay_composition_get_rectangle (comp_meta->overlay, i);
|
||||
|
||||
if ((overlay = _get_cached_overlay (self->pending_overlays, rectangle))) {
|
||||
overlay->index = i;
|
||||
|
||||
GST_TRACE ("Reusing cached overlay: %" GST_PTR_FORMAT, overlay);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (G_UNLIKELY (!gst_video_overlay_rectangle_get_render_rectangle (rectangle,
|
||||
&comp_x, &comp_y, &comp_width, &comp_height))) {
|
||||
GST_WARNING ("Invalid overlay rectangle dimensions: %" GST_PTR_FORMAT, rectangle);
|
||||
continue;
|
||||
}
|
||||
|
||||
flags = gst_video_overlay_rectangle_get_flags (rectangle);
|
||||
|
||||
if (flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA)
|
||||
alpha_flags |= GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA;
|
||||
|
||||
comp_buffer = gst_video_overlay_rectangle_get_pixels_unscaled_argb (rectangle, alpha_flags);
|
||||
|
||||
/* Update overlay video info from video meta */
|
||||
if ((v_meta = gst_buffer_get_video_meta (comp_buffer))) {
|
||||
gst_video_info_set_format (&v_info, v_meta->format, v_meta->width, v_meta->height);
|
||||
v_info.stride[0] = v_meta->stride[0];
|
||||
|
||||
if (alpha_flags & GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA)
|
||||
v_info.flags |= GST_VIDEO_FLAG_PREMULTIPLIED_ALPHA;
|
||||
}
|
||||
|
||||
if (G_UNLIKELY (!gst_video_frame_map (&comp_frame, &v_info, comp_buffer, GST_MAP_READ)))
|
||||
return;
|
||||
|
||||
if ((texture = gst_video_frame_into_gdk_texture (&comp_frame))) {
|
||||
overlay = gst_clapper_gdk_overlay_new (texture, rectangle, comp_x, comp_y,
|
||||
comp_width, comp_height, i);
|
||||
g_object_unref (texture);
|
||||
|
||||
GST_TRACE_OBJECT (self, "Created overlay: %"
|
||||
GST_PTR_FORMAT ", x: %i, y: %i, width: %u, height: %u",
|
||||
overlay, overlay->x, overlay->y, overlay->width, overlay->height);
|
||||
|
||||
g_ptr_array_insert (self->pending_overlays, i, overlay);
|
||||
}
|
||||
|
||||
gst_video_frame_unmap (&comp_frame);
|
||||
}
|
||||
|
||||
/* Remove all overlays that are not going to be used */
|
||||
for (i = self->pending_overlays->len; i > 0; i--) {
|
||||
GstClapperGdkOverlay *overlay = g_ptr_array_index (self->pending_overlays, i - 1);
|
||||
|
||||
if (overlay->index < 0) {
|
||||
GST_TRACE ("Removing unused cached overlay: %" GST_PTR_FORMAT, overlay);
|
||||
g_ptr_array_remove (self->pending_overlays, overlay);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sort remaining overlays */
|
||||
if (self->pending_overlays->len > 1) {
|
||||
GST_LOG_OBJECT (self, "Sorting overlays");
|
||||
g_ptr_array_sort (self->pending_overlays, (GCompareFunc) _sort_overlays_cb);
|
||||
}
|
||||
|
||||
if (G_UNLIKELY (num_overlays != self->pending_overlays->len)) {
|
||||
GST_WARNING_OBJECT (self, "Some overlays could not be prepared, %u != %u",
|
||||
num_overlays, self->pending_overlays->len);
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (self, "Prepared overlays: %u", self->pending_overlays->len);
|
||||
}
|
||||
|
||||
void
|
||||
gst_clapper_importer_set_caps (GstClapperImporter *self, GstCaps *caps)
|
||||
{
|
||||
GstClapperImporterClass *importer_class = GST_CLAPPER_IMPORTER_GET_CLASS (self);
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
gst_caps_replace (&self->pending_caps, caps);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
if (importer_class->set_caps)
|
||||
importer_class->set_caps (self, caps);
|
||||
}
|
||||
|
||||
void
|
||||
gst_clapper_importer_set_buffer (GstClapperImporter *self, GstBuffer *buffer)
|
||||
{
|
||||
GST_OBJECT_LOCK (self);
|
||||
|
||||
/* Pending v_info, buffer and overlays must be
|
||||
* set within a single importer locking */
|
||||
if (self->pending_caps) {
|
||||
self->has_pending_v_info = gst_video_info_from_caps (&self->pending_v_info, self->pending_caps);
|
||||
gst_clear_caps (&self->pending_caps);
|
||||
}
|
||||
gst_buffer_replace (&self->pending_buffer, buffer);
|
||||
gst_clapper_importer_prepare_overlays_locked (self);
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
}
|
||||
|
||||
GstBufferPool *
|
||||
gst_clapper_importer_create_pool (GstClapperImporter *self, GstStructure **config)
|
||||
{
|
||||
GstClapperImporterClass *importer_class = GST_CLAPPER_IMPORTER_GET_CLASS (self);
|
||||
|
||||
return importer_class->create_pool (self, config);
|
||||
}
|
||||
|
||||
void
|
||||
gst_clapper_importer_add_allocation_metas (GstClapperImporter *self, GstQuery *query)
|
||||
{
|
||||
GstClapperImporterClass *importer_class = GST_CLAPPER_IMPORTER_GET_CLASS (self);
|
||||
|
||||
importer_class->add_allocation_metas (self, query);
|
||||
}
|
||||
|
||||
void
|
||||
gst_clapper_importer_snapshot (GstClapperImporter *self, GdkSnapshot *snapshot,
|
||||
gdouble width, gdouble height)
|
||||
{
|
||||
guint i;
|
||||
gboolean buffer_changed;
|
||||
|
||||
/* Collect all data that we need to snapshot pending buffer,
|
||||
* lock ourselves to make sure everything matches */
|
||||
GST_OBJECT_LOCK (self);
|
||||
|
||||
if (self->has_pending_v_info) {
|
||||
self->v_info = self->pending_v_info;
|
||||
self->has_pending_v_info = FALSE;
|
||||
}
|
||||
|
||||
buffer_changed = gst_buffer_replace (&self->buffer, self->pending_buffer);
|
||||
|
||||
/* Ref overlays associated with current buffer */
|
||||
for (i = 0; i < self->pending_overlays->len; i++) {
|
||||
GstClapperGdkOverlay *overlay = g_ptr_array_index (self->pending_overlays, i);
|
||||
|
||||
g_ptr_array_insert (self->overlays, i, gst_clapper_gdk_overlay_ref (overlay));
|
||||
}
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
/* Draw black BG when no buffer or imported format has alpha */
|
||||
if (!self->buffer || GST_VIDEO_INFO_HAS_ALPHA (&self->v_info))
|
||||
gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
|
||||
if (self->buffer) {
|
||||
if (buffer_changed || !self->texture) {
|
||||
GstClapperImporterClass *importer_class = GST_CLAPPER_IMPORTER_GET_CLASS (self);
|
||||
|
||||
GST_TRACE_OBJECT (self, "Importing %" GST_PTR_FORMAT, self->buffer);
|
||||
|
||||
g_clear_object (&self->texture);
|
||||
self->texture = importer_class->generate_texture (self, self->buffer, &self->v_info);
|
||||
} else {
|
||||
GST_TRACE_OBJECT (self, "Reusing texture from %" GST_PTR_FORMAT, self->buffer);
|
||||
}
|
||||
|
||||
if (G_LIKELY (self->texture)) {
|
||||
gtk_snapshot_append_texture (snapshot, self->texture, &GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
|
||||
if (self->overlays->len > 0) {
|
||||
gfloat scale_x, scale_y;
|
||||
|
||||
/* FIXME: GStreamer scales subtitles without considering pixel aspect ratio.
|
||||
* See: https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/issues/20 */
|
||||
scale_x = (gfloat) width / GST_VIDEO_INFO_WIDTH (&self->v_info);
|
||||
scale_y = (gfloat) height / GST_VIDEO_INFO_HEIGHT (&self->v_info);
|
||||
|
||||
for (i = 0; i < self->overlays->len; i++) {
|
||||
GstClapperGdkOverlay *overlay = g_ptr_array_index (self->overlays, i);
|
||||
|
||||
gtk_snapshot_append_texture (snapshot, overlay->texture,
|
||||
&GRAPHENE_RECT_INIT (overlay->x * scale_x, overlay->y * scale_y,
|
||||
overlay->width * scale_x, overlay->height * scale_y));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
GST_ERROR_OBJECT (self, "Failed import of %" GST_PTR_FORMAT, self->buffer);
|
||||
|
||||
/* Draw black instead of texture on failure if not drawn already */
|
||||
if (!GST_VIDEO_INFO_HAS_ALPHA (&self->v_info))
|
||||
gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
}
|
||||
}
|
||||
|
||||
/* Unref all used overlays */
|
||||
if (self->overlays->len > 0)
|
||||
g_ptr_array_remove_range (self->overlays, 0, self->overlays->len);
|
||||
}
|
92
lib/gst/plugin/gstclapperimporter.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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 <gtk/gtk.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gst/video/video.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_CLAPPER_IMPORTER (gst_clapper_importer_get_type())
|
||||
#define GST_IS_CLAPPER_IMPORTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CLAPPER_IMPORTER))
|
||||
#define GST_IS_CLAPPER_IMPORTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_CLAPPER_IMPORTER))
|
||||
#define GST_CLAPPER_IMPORTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CLAPPER_IMPORTER, GstClapperImporterClass))
|
||||
#define GST_CLAPPER_IMPORTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_CLAPPER_IMPORTER, GstClapperImporter))
|
||||
#define GST_CLAPPER_IMPORTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_CLAPPER_IMPORTER, GstClapperImporterClass))
|
||||
#define GST_CLAPPER_IMPORTER_CAST(obj) ((GstClapperImporter *)(obj))
|
||||
|
||||
#define GST_CLAPPER_IMPORTER_DEFINE(camel,lower,type) \
|
||||
G_DEFINE_TYPE (camel, lower, type) \
|
||||
G_MODULE_EXPORT GstClapperImporter *make_importer (GPtrArray *context_handlers); \
|
||||
G_MODULE_EXPORT GstCaps *make_caps (gboolean is_template, \
|
||||
GstRank *rank, GPtrArray *context_handlers);
|
||||
|
||||
typedef struct _GstClapperImporter GstClapperImporter;
|
||||
typedef struct _GstClapperImporterClass GstClapperImporterClass;
|
||||
|
||||
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GstClapperImporter, gst_object_unref)
|
||||
#endif
|
||||
|
||||
struct _GstClapperImporter
|
||||
{
|
||||
GstObject parent;
|
||||
|
||||
GstCaps *pending_caps;
|
||||
GstBuffer *pending_buffer, *buffer;
|
||||
GPtrArray *pending_overlays, *overlays;
|
||||
GstVideoInfo pending_v_info, v_info;
|
||||
gboolean has_pending_v_info;
|
||||
|
||||
GdkTexture *texture;
|
||||
|
||||
GdkRGBA bg;
|
||||
};
|
||||
|
||||
struct _GstClapperImporterClass
|
||||
{
|
||||
GstObjectClass parent_class;
|
||||
|
||||
void (* set_caps) (GstClapperImporter *importer,
|
||||
GstCaps *caps);
|
||||
|
||||
GstBufferPool * (* create_pool) (GstClapperImporter *importer,
|
||||
GstStructure **config);
|
||||
|
||||
void (* add_allocation_metas) (GstClapperImporter *importer,
|
||||
GstQuery *query);
|
||||
|
||||
GdkTexture * (* generate_texture) (GstClapperImporter *importer,
|
||||
GstBuffer *buffer,
|
||||
GstVideoInfo *v_info);
|
||||
};
|
||||
|
||||
GType gst_clapper_importer_get_type (void);
|
||||
|
||||
GstBufferPool * gst_clapper_importer_create_pool (GstClapperImporter *importer, GstStructure **config);
|
||||
void gst_clapper_importer_add_allocation_metas (GstClapperImporter *importer, GstQuery *query);
|
||||
|
||||
void gst_clapper_importer_set_caps (GstClapperImporter *importer, GstCaps *caps);
|
||||
void gst_clapper_importer_set_buffer (GstClapperImporter *importer, GstBuffer *buffer);
|
||||
|
||||
void gst_clapper_importer_snapshot (GstClapperImporter *importer, GdkSnapshot *snapshot, gdouble width, gdouble height);
|
||||
|
||||
G_END_DECLS
|
385
lib/gst/plugin/gstclapperimporterloader.c
Normal file
@@ -0,0 +1,385 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gmodule.h>
|
||||
|
||||
#include "gstclapperimporterloader.h"
|
||||
#include "gstclapperimporter.h"
|
||||
#include "gstclappercontexthandler.h"
|
||||
|
||||
#define GST_CAT_DEFAULT gst_clapper_importer_loader_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
#define parent_class gst_clapper_importer_loader_parent_class
|
||||
G_DEFINE_TYPE (GstClapperImporterLoader, gst_clapper_importer_loader, GST_TYPE_OBJECT);
|
||||
|
||||
typedef GstClapperImporter* (* MakeImporter) (GPtrArray *context_handlers);
|
||||
typedef GstCaps* (* MakeCaps) (gboolean is_template, GstRank *rank, GPtrArray *context_handlers);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GModule *module;
|
||||
GstCaps *caps;
|
||||
GstRank rank;
|
||||
} GstClapperImporterData;
|
||||
|
||||
static void
|
||||
gst_clapper_importer_data_free (GstClapperImporterData *data)
|
||||
{
|
||||
GST_TRACE ("Freeing importer data: %" GST_PTR_FORMAT, data);
|
||||
|
||||
gst_clear_caps (&data->caps);
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
static GstClapperImporterData *
|
||||
_obtain_importer_data (GModule *module, gboolean is_template, GPtrArray *context_handlers)
|
||||
{
|
||||
MakeCaps make_caps;
|
||||
GstClapperImporterData *data;
|
||||
|
||||
GST_DEBUG ("Found importer: %s", g_module_name (module));
|
||||
|
||||
if (!g_module_symbol (module, "make_caps", (gpointer *) &make_caps)
|
||||
|| make_caps == NULL) {
|
||||
GST_WARNING ("Make caps function missing in importer");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data = g_new0 (GstClapperImporterData, 1);
|
||||
data->module = module;
|
||||
data->caps = make_caps (is_template, &data->rank, context_handlers);
|
||||
|
||||
GST_TRACE ("Created importer data: %" GST_PTR_FORMAT, data);
|
||||
|
||||
if (G_UNLIKELY (!data->caps)) {
|
||||
if (!is_template) {
|
||||
GST_ERROR ("Invalid importer without caps: %s",
|
||||
g_module_name (data->module));
|
||||
} else {
|
||||
/* When importer cannot be actually used, due to e.g. unsupported HW */
|
||||
GST_DEBUG ("No actual caps returned from importer");
|
||||
}
|
||||
gst_clapper_importer_data_free (data);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GST_DEBUG ("Importer caps: %" GST_PTR_FORMAT, data->caps);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static GstClapperImporter *
|
||||
_obtain_importer_internal (GModule *module, GPtrArray *context_handlers)
|
||||
{
|
||||
MakeImporter make_importer;
|
||||
GstClapperImporter *importer;
|
||||
|
||||
if (!g_module_symbol (module, "make_importer", (gpointer *) &make_importer)
|
||||
|| make_importer == NULL) {
|
||||
GST_WARNING ("Make function missing in importer");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
importer = make_importer (context_handlers);
|
||||
GST_TRACE ("Created importer: %" GST_PTR_FORMAT, importer);
|
||||
|
||||
return importer;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
_obtain_available_modules_once (G_GNUC_UNUSED gpointer data)
|
||||
{
|
||||
GPtrArray *modules;
|
||||
GFile *dir;
|
||||
GFileEnumerator *dir_enum;
|
||||
GError *error = NULL;
|
||||
|
||||
GST_INFO ("Preparing modules");
|
||||
|
||||
modules = g_ptr_array_new ();
|
||||
dir = g_file_new_for_path (CLAPPER_SINK_IMPORTER_PATH);
|
||||
|
||||
if ((dir_enum = g_file_enumerate_children (dir,
|
||||
G_FILE_ATTRIBUTE_STANDARD_NAME,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &error))) {
|
||||
while (TRUE) {
|
||||
GFileInfo *info = NULL;
|
||||
GModule *module;
|
||||
gchar *module_path;
|
||||
const gchar *module_name;
|
||||
|
||||
if (!g_file_enumerator_iterate (dir_enum, &info,
|
||||
NULL, NULL, &error) || !info)
|
||||
break;
|
||||
|
||||
module_name = g_file_info_get_name (info);
|
||||
|
||||
if (!g_str_has_suffix (module_name, G_MODULE_SUFFIX))
|
||||
continue;
|
||||
|
||||
module_path = g_module_build_path (CLAPPER_SINK_IMPORTER_PATH, module_name);
|
||||
module = g_module_open (module_path, G_MODULE_BIND_LAZY);
|
||||
g_free (module_path);
|
||||
|
||||
if (!module) {
|
||||
GST_WARNING ("Could not read module: %s, reason: %s",
|
||||
module_name, g_module_error ());
|
||||
continue;
|
||||
}
|
||||
|
||||
GST_INFO ("Found module: %s", module_name);
|
||||
g_ptr_array_add (modules, module);
|
||||
}
|
||||
|
||||
g_object_unref (dir_enum);
|
||||
}
|
||||
|
||||
g_object_unref (dir);
|
||||
|
||||
if (error) {
|
||||
GST_ERROR ("Could not load module, reason: %s",
|
||||
(error->message) ? error->message : "unknown");
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
return modules;
|
||||
}
|
||||
|
||||
static const GPtrArray *
|
||||
gst_clapper_importer_loader_get_available_modules (void)
|
||||
{
|
||||
static GOnce once = G_ONCE_INIT;
|
||||
|
||||
g_once (&once, _obtain_available_modules_once, NULL);
|
||||
return (const GPtrArray *) once.retval;
|
||||
}
|
||||
|
||||
static gint
|
||||
_sort_importers_cb (gconstpointer a, gconstpointer b)
|
||||
{
|
||||
GstClapperImporterData *data_a, *data_b;
|
||||
|
||||
data_a = *((GstClapperImporterData **) a);
|
||||
data_b = *((GstClapperImporterData **) b);
|
||||
|
||||
return (data_b->rank - data_a->rank);
|
||||
}
|
||||
|
||||
static GPtrArray *
|
||||
_obtain_importers (gboolean is_template, GPtrArray *context_handlers)
|
||||
{
|
||||
const GPtrArray *modules;
|
||||
GPtrArray *importers;
|
||||
guint i;
|
||||
|
||||
GST_DEBUG ("Checking %s importers",
|
||||
(is_template) ? "available" : "usable");
|
||||
|
||||
modules = gst_clapper_importer_loader_get_available_modules ();
|
||||
importers = g_ptr_array_new_with_free_func (
|
||||
(GDestroyNotify) gst_clapper_importer_data_free);
|
||||
|
||||
for (i = 0; i < modules->len; i++) {
|
||||
GModule *module = g_ptr_array_index (modules, i);
|
||||
GstClapperImporterData *data;
|
||||
|
||||
if ((data = _obtain_importer_data (module, is_template, context_handlers)))
|
||||
g_ptr_array_add (importers, data);
|
||||
}
|
||||
|
||||
g_ptr_array_sort (importers, (GCompareFunc) _sort_importers_cb);
|
||||
|
||||
GST_DEBUG ("Found %i %s importers", importers->len,
|
||||
(is_template) ? "available" : "usable");
|
||||
|
||||
return importers;
|
||||
}
|
||||
|
||||
GstClapperImporterLoader *
|
||||
gst_clapper_importer_loader_new (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_CLAPPER_IMPORTER_LOADER, NULL);
|
||||
}
|
||||
|
||||
static GstCaps *
|
||||
_make_caps_for_importers (const GPtrArray *importers)
|
||||
{
|
||||
GstCaps *caps = gst_caps_new_empty ();
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < importers->len; i++) {
|
||||
GstClapperImporterData *data = g_ptr_array_index (importers, i);
|
||||
|
||||
gst_caps_append (caps, gst_caps_ref (data->caps));
|
||||
}
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
GstPadTemplate *
|
||||
gst_clapper_importer_loader_make_sink_pad_template (void)
|
||||
{
|
||||
GPtrArray *importers;
|
||||
GstCaps *caps;
|
||||
GstPadTemplate *templ;
|
||||
|
||||
/* This is only called once from sink class init function */
|
||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperimporterloader", 0,
|
||||
"Clapper Importer Loader");
|
||||
|
||||
GST_DEBUG ("Making sink pad template");
|
||||
|
||||
importers = _obtain_importers (TRUE, NULL);
|
||||
caps = _make_caps_for_importers (importers);
|
||||
g_ptr_array_unref (importers);
|
||||
|
||||
if (G_UNLIKELY (gst_caps_is_empty (caps)))
|
||||
gst_caps_append (caps, gst_caps_new_any ());
|
||||
|
||||
templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, caps);
|
||||
gst_caps_unref (caps);
|
||||
|
||||
GST_TRACE ("Created sink pad template");
|
||||
|
||||
return templ;
|
||||
}
|
||||
|
||||
GstCaps *
|
||||
gst_clapper_importer_loader_make_actual_caps (GstClapperImporterLoader *self)
|
||||
{
|
||||
return _make_caps_for_importers (self->importers);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_clapper_importer_loader_handle_context_query (GstClapperImporterLoader *self,
|
||||
GstBaseSink *bsink, GstQuery *query)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < self->context_handlers->len; i++) {
|
||||
GstClapperContextHandler *handler = g_ptr_array_index (self->context_handlers, i);
|
||||
|
||||
if (gst_clapper_context_handler_handle_context_query (handler, bsink, query))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static const GstClapperImporterData *
|
||||
_get_importer_data_for_caps (const GPtrArray *importers, const GstCaps *caps)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < importers->len; i++) {
|
||||
GstClapperImporterData *data = g_ptr_array_index (importers, i);
|
||||
|
||||
if (!gst_caps_is_always_compatible (caps, data->caps))
|
||||
continue;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_clapper_importer_loader_find_importer_for_caps (GstClapperImporterLoader *self,
|
||||
GstCaps *caps, GstClapperImporter **importer)
|
||||
{
|
||||
const GstClapperImporterData *data = NULL;
|
||||
GstClapperImporter *found_importer = NULL;
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Requested importer for caps: %" GST_PTR_FORMAT, caps);
|
||||
data = _get_importer_data_for_caps (self->importers, caps);
|
||||
|
||||
GST_LOG_OBJECT (self, "Old importer path: %s, new path: %s",
|
||||
(self->last_module) ? g_module_name (self->last_module) : NULL,
|
||||
(data) ? g_module_name (data->module) : NULL);
|
||||
|
||||
if (G_UNLIKELY (!data)) {
|
||||
gst_clear_object (importer);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (*importer && (self->last_module == data->module)) {
|
||||
GST_DEBUG_OBJECT (self, "No importer change");
|
||||
|
||||
gst_clapper_importer_set_caps (*importer, caps);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
found_importer = _obtain_importer_internal (data->module, self->context_handlers);
|
||||
gst_clear_object (importer);
|
||||
|
||||
if (!found_importer)
|
||||
goto finish;
|
||||
|
||||
gst_clapper_importer_set_caps (found_importer, caps);
|
||||
|
||||
*importer = found_importer;
|
||||
|
||||
finish:
|
||||
self->last_module = (*importer && data)
|
||||
? data->module
|
||||
: NULL;
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
return (*importer != NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_importer_loader_init (GstClapperImporterLoader *self)
|
||||
{
|
||||
self->context_handlers = g_ptr_array_new_with_free_func (
|
||||
(GDestroyNotify) gst_object_unref);
|
||||
self->importers = _obtain_importers (FALSE, self->context_handlers);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_importer_loader_finalize (GObject *object)
|
||||
{
|
||||
GstClapperImporterLoader *self = GST_CLAPPER_IMPORTER_LOADER_CAST (object);
|
||||
|
||||
GST_TRACE ("Finalize");
|
||||
|
||||
if (self->importers)
|
||||
g_ptr_array_unref (self->importers);
|
||||
|
||||
g_ptr_array_unref (self->context_handlers);
|
||||
|
||||
GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_importer_loader_class_init (GstClapperImporterLoaderClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
|
||||
gobject_class->finalize = gst_clapper_importer_loader_finalize;
|
||||
}
|
54
lib/gst/plugin/gstclapperimporterloader.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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 <gst/gst.h>
|
||||
#include <gst/base/gstbasesink.h>
|
||||
|
||||
#include "gstclapperimporter.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_CLAPPER_IMPORTER_LOADER (gst_clapper_importer_loader_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstClapperImporterLoader, gst_clapper_importer_loader, GST, CLAPPER_IMPORTER_LOADER, GstObject)
|
||||
|
||||
#define GST_CLAPPER_IMPORTER_LOADER_CAST(obj) ((GstClapperImporterLoader *)(obj))
|
||||
|
||||
struct _GstClapperImporterLoader
|
||||
{
|
||||
GstObject parent;
|
||||
|
||||
GModule *last_module;
|
||||
|
||||
GPtrArray *importers;
|
||||
GPtrArray *context_handlers;
|
||||
};
|
||||
|
||||
GstClapperImporterLoader * gst_clapper_importer_loader_new (void);
|
||||
|
||||
GstPadTemplate * gst_clapper_importer_loader_make_sink_pad_template (void);
|
||||
|
||||
GstCaps * gst_clapper_importer_loader_make_actual_caps (GstClapperImporterLoader *loader);
|
||||
|
||||
gboolean gst_clapper_importer_loader_handle_context_query (GstClapperImporterLoader *loader, GstBaseSink *bsink, GstQuery *query);
|
||||
|
||||
gboolean gst_clapper_importer_loader_find_importer_for_caps (GstClapperImporterLoader *loader, GstCaps *caps, GstClapperImporter **importer);
|
||||
|
||||
G_END_DECLS
|
435
lib/gst/plugin/gstclapperpaintable.c
Normal file
@@ -0,0 +1,435 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstclapperpaintable.h"
|
||||
|
||||
#define DEFAULT_PAR_N 1
|
||||
#define DEFAULT_PAR_D 1
|
||||
|
||||
#define GST_CAT_DEFAULT gst_clapper_paintable_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
static void gst_clapper_paintable_iface_init (GdkPaintableInterface *iface);
|
||||
static void gst_clapper_paintable_dispose (GObject *object);
|
||||
static void gst_clapper_paintable_finalize (GObject *object);
|
||||
|
||||
#define parent_class gst_clapper_paintable_parent_class
|
||||
G_DEFINE_TYPE_WITH_CODE (GstClapperPaintable, gst_clapper_paintable, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
|
||||
gst_clapper_paintable_iface_init));
|
||||
|
||||
static void
|
||||
gst_clapper_paintable_class_init (GstClapperPaintableClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperpaintable", 0,
|
||||
"Clapper Paintable");
|
||||
|
||||
gobject_class->dispose = gst_clapper_paintable_dispose;
|
||||
gobject_class->finalize = gst_clapper_paintable_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_paintable_init (GstClapperPaintable *self)
|
||||
{
|
||||
self->display_width = 1;
|
||||
self->display_height = 1;
|
||||
self->display_aspect_ratio = 1.0;
|
||||
|
||||
self->par_n = DEFAULT_PAR_N;
|
||||
self->par_d = DEFAULT_PAR_D;
|
||||
|
||||
g_mutex_init (&self->lock);
|
||||
g_mutex_init (&self->importer_lock);
|
||||
|
||||
gst_video_info_init (&self->v_info);
|
||||
g_weak_ref_init (&self->widget, NULL);
|
||||
|
||||
gdk_rgba_parse (&self->bg, "black");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_paintable_dispose (GObject *object)
|
||||
{
|
||||
GstClapperPaintable *self = GST_CLAPPER_PAINTABLE (object);
|
||||
|
||||
GST_CLAPPER_PAINTABLE_LOCK (self);
|
||||
|
||||
if (self->draw_id > 0) {
|
||||
g_source_remove (self->draw_id);
|
||||
self->draw_id = 0;
|
||||
}
|
||||
|
||||
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||
|
||||
GST_CLAPPER_PAINTABLE_IMPORTER_LOCK (self);
|
||||
gst_clear_object (&self->importer);
|
||||
GST_CLAPPER_PAINTABLE_IMPORTER_UNLOCK (self);
|
||||
|
||||
GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_paintable_finalize (GObject *object)
|
||||
{
|
||||
GstClapperPaintable *self = GST_CLAPPER_PAINTABLE (object);
|
||||
|
||||
GST_TRACE ("Finalize");
|
||||
|
||||
g_weak_ref_clear (&self->widget);
|
||||
|
||||
g_mutex_clear (&self->lock);
|
||||
g_mutex_clear (&self->importer_lock);
|
||||
|
||||
GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
calculate_display_par (GstClapperPaintable *self, const GstVideoInfo *info)
|
||||
{
|
||||
gint width, height, par_n, par_d, req_par_n, req_par_d;
|
||||
gboolean success;
|
||||
|
||||
width = GST_VIDEO_INFO_WIDTH (info);
|
||||
height = GST_VIDEO_INFO_HEIGHT (info);
|
||||
|
||||
/* Cannot apply aspect ratio if there is no video */
|
||||
if (width == 0 || height == 0)
|
||||
return FALSE;
|
||||
|
||||
par_n = GST_VIDEO_INFO_PAR_N (info);
|
||||
par_d = GST_VIDEO_INFO_PAR_D (info);
|
||||
|
||||
req_par_n = self->par_n;
|
||||
req_par_d = self->par_d;
|
||||
|
||||
if (par_n == 0)
|
||||
par_n = 1;
|
||||
|
||||
/* Use defaults if user set zero */
|
||||
if (req_par_n == 0 || req_par_d == 0) {
|
||||
req_par_n = DEFAULT_PAR_N;
|
||||
req_par_d = DEFAULT_PAR_D;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (self, "PAR: %u/%u, DAR: %u/%u", par_n, par_d, req_par_n, req_par_d);
|
||||
|
||||
if (!(success = gst_video_calculate_display_ratio (&self->display_ratio_num,
|
||||
&self->display_ratio_den, width, height, par_n, par_d,
|
||||
req_par_n, req_par_d))) {
|
||||
GST_ERROR_OBJECT (self, "Could not calculate display ratio values");
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static void
|
||||
invalidate_paintable_size_internal (GstClapperPaintable *self)
|
||||
{
|
||||
gint video_width, video_height;
|
||||
guint display_ratio_num, display_ratio_den;
|
||||
|
||||
GST_CLAPPER_PAINTABLE_LOCK (self);
|
||||
|
||||
video_width = GST_VIDEO_INFO_WIDTH (&self->v_info);
|
||||
video_height = GST_VIDEO_INFO_HEIGHT (&self->v_info);
|
||||
|
||||
display_ratio_num = self->display_ratio_num;
|
||||
display_ratio_den = self->display_ratio_den;
|
||||
|
||||
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||
|
||||
if (video_height % display_ratio_den == 0) {
|
||||
GST_LOG ("Keeping video height");
|
||||
|
||||
self->display_width = (guint)
|
||||
gst_util_uint64_scale_int (video_height, display_ratio_num, display_ratio_den);
|
||||
self->display_height = video_height;
|
||||
} else if (video_width % display_ratio_num == 0) {
|
||||
GST_LOG ("Keeping video width");
|
||||
|
||||
self->display_width = video_width;
|
||||
self->display_height = (guint)
|
||||
gst_util_uint64_scale_int (video_width, display_ratio_den, display_ratio_num);
|
||||
} else {
|
||||
GST_LOG ("Approximating while keeping video height");
|
||||
|
||||
self->display_width = (guint)
|
||||
gst_util_uint64_scale_int (video_height, display_ratio_num, display_ratio_den);
|
||||
self->display_height = video_height;
|
||||
}
|
||||
|
||||
self->display_aspect_ratio = ((gdouble) self->display_width
|
||||
/ (gdouble) self->display_height);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Invalidate paintable size, display: %dx%d",
|
||||
self->display_width, self->display_height);
|
||||
gdk_paintable_invalidate_size ((GdkPaintable *) self);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
invalidate_paintable_size_on_main_cb (GstClapperPaintable *self)
|
||||
{
|
||||
GST_CLAPPER_PAINTABLE_LOCK (self);
|
||||
self->draw_id = 0;
|
||||
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||
|
||||
invalidate_paintable_size_internal (self);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
update_paintable_on_main_cb (GstClapperPaintable *self)
|
||||
{
|
||||
gboolean size_changed;
|
||||
|
||||
GST_CLAPPER_PAINTABLE_LOCK (self);
|
||||
|
||||
/* Check if we will need to invalidate size */
|
||||
if ((size_changed = self->pending_resize))
|
||||
self->pending_resize = FALSE;
|
||||
|
||||
self->draw_id = 0;
|
||||
|
||||
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||
|
||||
if (size_changed)
|
||||
invalidate_paintable_size_internal (self);
|
||||
|
||||
GST_LOG_OBJECT (self, "Invalidate paintable contents");
|
||||
gdk_paintable_invalidate_contents ((GdkPaintable *) self);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
GstClapperPaintable *
|
||||
gst_clapper_paintable_new (void)
|
||||
{
|
||||
return g_object_new (GST_TYPE_CLAPPER_PAINTABLE, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gst_clapper_paintable_set_widget (GstClapperPaintable *self, GtkWidget *widget)
|
||||
{
|
||||
g_weak_ref_set (&self->widget, widget);
|
||||
}
|
||||
|
||||
void
|
||||
gst_clapper_paintable_set_importer (GstClapperPaintable *self, GstClapperImporter *importer)
|
||||
{
|
||||
GST_CLAPPER_PAINTABLE_IMPORTER_LOCK (self);
|
||||
gst_object_replace ((GstObject **) &self->importer, GST_OBJECT_CAST (importer));
|
||||
GST_CLAPPER_PAINTABLE_IMPORTER_UNLOCK (self);
|
||||
}
|
||||
|
||||
void
|
||||
gst_clapper_paintable_queue_draw (GstClapperPaintable *self)
|
||||
{
|
||||
GST_CLAPPER_PAINTABLE_LOCK (self);
|
||||
|
||||
if (self->draw_id > 0) {
|
||||
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||
GST_TRACE ("Already have pending draw");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
self->draw_id = g_idle_add_full (G_PRIORITY_DEFAULT,
|
||||
(GSourceFunc) update_paintable_on_main_cb, self, NULL);
|
||||
|
||||
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_clapper_paintable_set_video_info (GstClapperPaintable *self, const GstVideoInfo *v_info)
|
||||
{
|
||||
GST_CLAPPER_PAINTABLE_LOCK (self);
|
||||
|
||||
if (gst_video_info_is_equal (&self->v_info, v_info)) {
|
||||
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Reject info if values would cause integer overflow */
|
||||
if (G_UNLIKELY (!calculate_display_par (self, v_info))) {
|
||||
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
self->pending_resize = TRUE;
|
||||
self->v_info = *v_info;
|
||||
|
||||
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
gst_clapper_paintable_set_pixel_aspect_ratio (GstClapperPaintable *self,
|
||||
gint par_n, gint par_d)
|
||||
{
|
||||
gboolean success;
|
||||
|
||||
GST_CLAPPER_PAINTABLE_LOCK (self);
|
||||
|
||||
/* No change */
|
||||
if (self->par_n == par_n && self->par_d == par_d) {
|
||||
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||
return;
|
||||
}
|
||||
|
||||
self->par_n = par_n;
|
||||
self->par_d = par_d;
|
||||
|
||||
/* Check if we can accept new values. This will update
|
||||
* display `ratio_num` and `ratio_den` only when successful */
|
||||
success = calculate_display_par (self, &self->v_info);
|
||||
|
||||
/* If paintable update is queued, wait for it, otherwise invalidate
|
||||
* size only for change to be applied even when paused */
|
||||
if (!success || self->draw_id > 0) {
|
||||
self->pending_resize = success;
|
||||
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
self->draw_id = g_idle_add_full (G_PRIORITY_DEFAULT,
|
||||
(GSourceFunc) invalidate_paintable_size_on_main_cb, self, NULL);
|
||||
|
||||
GST_CLAPPER_PAINTABLE_UNLOCK (self);
|
||||
}
|
||||
|
||||
/*
|
||||
* GdkPaintableInterface
|
||||
*/
|
||||
static void
|
||||
gst_clapper_paintable_snapshot_internal (GstClapperPaintable *self,
|
||||
GdkSnapshot *snapshot, gdouble width, gdouble height,
|
||||
gint widget_width, gint widget_height)
|
||||
{
|
||||
gfloat scale_x, scale_y;
|
||||
|
||||
GST_LOG_OBJECT (self, "Snapshot");
|
||||
|
||||
scale_x = (gfloat) width / self->display_width;
|
||||
scale_y = (gfloat) height / self->display_height;
|
||||
|
||||
/* Apply black borders when keeping aspect ratio */
|
||||
if (scale_x == scale_y || abs (scale_x - scale_y) <= FLT_EPSILON) {
|
||||
if (widget_height - height > 0) {
|
||||
/* XXX: Top uses integer to work with GTK rounding (not going offscreen) */
|
||||
gint top_bar_height = (widget_height - height) / 2;
|
||||
gdouble bottom_bar_height = (widget_height - top_bar_height - height);
|
||||
|
||||
gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (0, 0, width, -top_bar_height));
|
||||
gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (0, height, width, bottom_bar_height));
|
||||
} else if (widget_width - width > 0) {
|
||||
gint left_bar_width = (widget_width - width) / 2;
|
||||
gdouble right_bar_width = (widget_width - left_bar_width - width);
|
||||
|
||||
gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (0, 0, -left_bar_width, height));
|
||||
gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (width, 0, right_bar_width, height));
|
||||
}
|
||||
}
|
||||
|
||||
GST_CLAPPER_PAINTABLE_IMPORTER_LOCK (self);
|
||||
|
||||
if (self->importer) {
|
||||
gst_clapper_importer_snapshot (self->importer, snapshot, width, height);
|
||||
} else {
|
||||
GST_LOG_OBJECT (self, "No texture importer, drawing black");
|
||||
gtk_snapshot_append_color (snapshot, &self->bg, &GRAPHENE_RECT_INIT (0, 0, width, height));
|
||||
}
|
||||
|
||||
GST_CLAPPER_PAINTABLE_IMPORTER_UNLOCK (self);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_paintable_snapshot (GdkPaintable *paintable,
|
||||
GdkSnapshot *snapshot, gdouble width, gdouble height)
|
||||
{
|
||||
GstClapperPaintable *self = GST_CLAPPER_PAINTABLE_CAST (paintable);
|
||||
GtkWidget *widget;
|
||||
gint widget_width = 0, widget_height = 0;
|
||||
|
||||
if ((widget = g_weak_ref_get (&self->widget))) {
|
||||
widget_width = gtk_widget_get_width (widget);
|
||||
widget_height = gtk_widget_get_height (widget);
|
||||
|
||||
g_object_unref (widget);
|
||||
}
|
||||
|
||||
gst_clapper_paintable_snapshot_internal (self, snapshot,
|
||||
width, height, widget_width, widget_height);
|
||||
}
|
||||
|
||||
static GdkPaintable *
|
||||
gst_clapper_paintable_get_current_image (GdkPaintable *paintable)
|
||||
{
|
||||
GstClapperPaintable *self = GST_CLAPPER_PAINTABLE_CAST (paintable);
|
||||
GtkSnapshot *snapshot = gtk_snapshot_new ();
|
||||
|
||||
/* Snapshot without widget size in order to get
|
||||
* paintable without black borders */
|
||||
gst_clapper_paintable_snapshot_internal (self, snapshot,
|
||||
self->display_width, self->display_height, 0, 0);
|
||||
|
||||
return gtk_snapshot_free_to_paintable (snapshot, NULL);
|
||||
}
|
||||
|
||||
static gint
|
||||
gst_clapper_paintable_get_intrinsic_width (GdkPaintable *paintable)
|
||||
{
|
||||
GstClapperPaintable *self = GST_CLAPPER_PAINTABLE_CAST (paintable);
|
||||
|
||||
return self->display_width;
|
||||
}
|
||||
|
||||
static gint
|
||||
gst_clapper_paintable_get_intrinsic_height (GdkPaintable *paintable)
|
||||
{
|
||||
GstClapperPaintable *self = GST_CLAPPER_PAINTABLE_CAST (paintable);
|
||||
|
||||
return self->display_height;
|
||||
}
|
||||
|
||||
static gdouble
|
||||
gst_clapper_paintable_get_intrinsic_aspect_ratio (GdkPaintable *paintable)
|
||||
{
|
||||
GstClapperPaintable *self = GST_CLAPPER_PAINTABLE_CAST (paintable);
|
||||
|
||||
return self->display_aspect_ratio;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_paintable_iface_init (GdkPaintableInterface *iface)
|
||||
{
|
||||
iface->snapshot = gst_clapper_paintable_snapshot;
|
||||
iface->get_current_image = gst_clapper_paintable_get_current_image;
|
||||
iface->get_intrinsic_width = gst_clapper_paintable_get_intrinsic_width;
|
||||
iface->get_intrinsic_height = gst_clapper_paintable_get_intrinsic_height;
|
||||
iface->get_intrinsic_aspect_ratio = gst_clapper_paintable_get_intrinsic_aspect_ratio;
|
||||
}
|
81
lib/gst/plugin/gstclapperpaintable.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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 <gtk/gtk.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gst/video/video.h>
|
||||
|
||||
#include "gstclapperimporter.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_CLAPPER_PAINTABLE (gst_clapper_paintable_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstClapperPaintable, gst_clapper_paintable, GST, CLAPPER_PAINTABLE, GObject)
|
||||
|
||||
#define GST_CLAPPER_PAINTABLE_CAST(obj) ((GstClapperPaintable *)(obj))
|
||||
|
||||
#define GST_CLAPPER_PAINTABLE_GET_LOCK(obj) (&GST_CLAPPER_PAINTABLE_CAST(obj)->lock)
|
||||
#define GST_CLAPPER_PAINTABLE_LOCK(obj) g_mutex_lock (GST_CLAPPER_PAINTABLE_GET_LOCK(obj))
|
||||
#define GST_CLAPPER_PAINTABLE_UNLOCK(obj) g_mutex_unlock (GST_CLAPPER_PAINTABLE_GET_LOCK(obj))
|
||||
|
||||
#define GST_CLAPPER_PAINTABLE_IMPORTER_GET_LOCK(obj) (&GST_CLAPPER_PAINTABLE_CAST(obj)->importer_lock)
|
||||
#define GST_CLAPPER_PAINTABLE_IMPORTER_LOCK(obj) g_mutex_lock (GST_CLAPPER_PAINTABLE_IMPORTER_GET_LOCK(obj))
|
||||
#define GST_CLAPPER_PAINTABLE_IMPORTER_UNLOCK(obj) g_mutex_unlock (GST_CLAPPER_PAINTABLE_IMPORTER_GET_LOCK(obj))
|
||||
|
||||
struct _GstClapperPaintable
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
GMutex lock;
|
||||
GMutex importer_lock;
|
||||
|
||||
GstVideoInfo v_info;
|
||||
|
||||
GdkRGBA bg;
|
||||
|
||||
GWeakRef widget;
|
||||
GstClapperImporter *importer;
|
||||
|
||||
/* Sink properties */
|
||||
gint par_n, par_d;
|
||||
|
||||
/* Resize */
|
||||
gboolean pending_resize;
|
||||
guint display_ratio_num;
|
||||
guint display_ratio_den;
|
||||
|
||||
/* GdkPaintableInterface */
|
||||
gint display_width;
|
||||
gint display_height;
|
||||
gdouble display_aspect_ratio;
|
||||
|
||||
/* Pending draw signal id */
|
||||
guint draw_id;
|
||||
};
|
||||
|
||||
GstClapperPaintable * gst_clapper_paintable_new (void);
|
||||
void gst_clapper_paintable_queue_draw (GstClapperPaintable *paintable);
|
||||
void gst_clapper_paintable_set_widget (GstClapperPaintable *paintable, GtkWidget *widget);
|
||||
void gst_clapper_paintable_set_importer (GstClapperPaintable *paintable, GstClapperImporter *importer);
|
||||
gboolean gst_clapper_paintable_set_video_info (GstClapperPaintable *paintable, const GstVideoInfo *v_info);
|
||||
void gst_clapper_paintable_set_pixel_aspect_ratio (GstClapperPaintable *paintable, gint par_n, gint par_d);
|
||||
|
||||
G_END_DECLS
|
911
lib/gst/plugin/gstclappersink.c
Normal file
@@ -0,0 +1,911 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstclappersink.h"
|
||||
#include "gstgtkutils.h"
|
||||
|
||||
#define DEFAULT_FORCE_ASPECT_RATIO TRUE
|
||||
#define DEFAULT_PAR_N 1
|
||||
#define DEFAULT_PAR_D 1
|
||||
#define DEFAULT_KEEP_LAST_FRAME FALSE
|
||||
|
||||
#define WINDOW_CSS_CLASS_NAME "clappersinkwindow"
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_WIDGET,
|
||||
PROP_FORCE_ASPECT_RATIO,
|
||||
PROP_PIXEL_ASPECT_RATIO,
|
||||
PROP_KEEP_LAST_FRAME,
|
||||
PROP_LAST
|
||||
};
|
||||
|
||||
#define GST_CAT_DEFAULT gst_clapper_sink_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
static void gst_clapper_sink_navigation_interface_init (
|
||||
GstNavigationInterface *iface);
|
||||
|
||||
#define parent_class gst_clapper_sink_parent_class
|
||||
G_DEFINE_TYPE_WITH_CODE (GstClapperSink, gst_clapper_sink, GST_TYPE_VIDEO_SINK,
|
||||
G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
|
||||
gst_clapper_sink_navigation_interface_init));
|
||||
GST_ELEMENT_REGISTER_DEFINE (clappersink, "clappersink", GST_RANK_NONE,
|
||||
GST_TYPE_CLAPPER_SINK);
|
||||
|
||||
static void
|
||||
window_clear_no_lock (GstClapperSink *self)
|
||||
{
|
||||
if (!self->window)
|
||||
return;
|
||||
|
||||
GST_TRACE_OBJECT (self, "Window clear");
|
||||
|
||||
if (self->window_destroy_id) {
|
||||
g_signal_handler_disconnect (self->window, self->window_destroy_id);
|
||||
self->window_destroy_id = 0;
|
||||
}
|
||||
self->window = NULL;
|
||||
self->presented_window = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
widget_clear_no_lock (GstClapperSink *self)
|
||||
{
|
||||
if (!self->widget)
|
||||
return;
|
||||
|
||||
GST_TRACE_OBJECT (self, "Widget clear");
|
||||
|
||||
if (self->widget_destroy_id) {
|
||||
g_signal_handler_disconnect (self->widget, self->widget_destroy_id);
|
||||
self->widget_destroy_id = 0;
|
||||
}
|
||||
g_clear_object (&self->widget);
|
||||
}
|
||||
|
||||
static void
|
||||
widget_destroy_cb (GtkWidget *widget, GstClapperSink *self)
|
||||
{
|
||||
GST_CLAPPER_SINK_LOCK (self);
|
||||
widget_clear_no_lock (self);
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
}
|
||||
|
||||
static void
|
||||
window_destroy_cb (GtkWidget *window, GstClapperSink *self)
|
||||
{
|
||||
GST_DEBUG_OBJECT (self, "Window destroy");
|
||||
|
||||
GST_CLAPPER_SINK_LOCK (self);
|
||||
|
||||
widget_clear_no_lock (self);
|
||||
window_clear_no_lock (self);
|
||||
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
}
|
||||
|
||||
static void
|
||||
calculate_stream_coords (GstClapperSink *self, GtkWidget *widget,
|
||||
gdouble x, gdouble y, gdouble *stream_x, gdouble *stream_y)
|
||||
{
|
||||
GstVideoRectangle result;
|
||||
gint scaled_width, scaled_height, scale_factor;
|
||||
gint video_width, video_height;
|
||||
gboolean force_aspect_ratio;
|
||||
|
||||
GST_CLAPPER_SINK_LOCK (self);
|
||||
|
||||
video_width = GST_VIDEO_INFO_WIDTH (&self->v_info);
|
||||
video_height = GST_VIDEO_INFO_HEIGHT (&self->v_info);
|
||||
force_aspect_ratio = self->force_aspect_ratio;
|
||||
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
|
||||
scale_factor = gtk_widget_get_scale_factor (widget);
|
||||
scaled_width = gtk_widget_get_width (widget) * scale_factor;
|
||||
scaled_height = gtk_widget_get_height (widget) * scale_factor;
|
||||
|
||||
if (force_aspect_ratio) {
|
||||
GstVideoRectangle src, dst;
|
||||
|
||||
src.x = 0;
|
||||
src.y = 0;
|
||||
src.w = gdk_paintable_get_intrinsic_width ((GdkPaintable *) self->paintable);
|
||||
src.h = gdk_paintable_get_intrinsic_height ((GdkPaintable *) self->paintable);
|
||||
|
||||
dst.x = 0;
|
||||
dst.y = 0;
|
||||
dst.w = scaled_width;
|
||||
dst.h = scaled_height;
|
||||
|
||||
gst_video_center_rect (&src, &dst, &result, TRUE);
|
||||
} else {
|
||||
result.x = 0;
|
||||
result.y = 0;
|
||||
result.w = scaled_width;
|
||||
result.h = scaled_height;
|
||||
}
|
||||
|
||||
/* Display coordinates to stream coordinates */
|
||||
*stream_x = (result.w > 0)
|
||||
? (x - result.x) / result.w * video_width
|
||||
: 0;
|
||||
*stream_y = (result.h > 0)
|
||||
? (y - result.y) / result.h * video_height
|
||||
: 0;
|
||||
|
||||
/* Clip to stream size */
|
||||
*stream_x = CLAMP (*stream_x, 0, video_width);
|
||||
*stream_y = CLAMP (*stream_y, 0, video_height);
|
||||
|
||||
GST_LOG ("Transform coords %fx%f => %fx%f", x, y, *stream_x, *stream_y);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_sink_widget_motion_event (GtkEventControllerMotion *motion,
|
||||
gdouble x, gdouble y, GstClapperSink *self)
|
||||
{
|
||||
GtkWidget *widget;
|
||||
gdouble stream_x, stream_y;
|
||||
gboolean is_inactive;
|
||||
|
||||
if (x == self->last_pos_x && y == self->last_pos_y)
|
||||
return;
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
is_inactive = (GST_STATE (self) < GST_STATE_PLAYING);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
if (is_inactive)
|
||||
return;
|
||||
|
||||
self->last_pos_x = x;
|
||||
self->last_pos_y = y;
|
||||
|
||||
widget = gtk_event_controller_get_widget ((GtkEventController *) motion);
|
||||
calculate_stream_coords (self, widget, x, y, &stream_x, &stream_y);
|
||||
GST_LOG ("Event \"mouse-move\", x: %f, y: %f", stream_x, stream_y);
|
||||
|
||||
gst_navigation_send_mouse_event ((GstNavigation *) self, "mouse-move",
|
||||
0, stream_x, stream_y);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_sink_widget_button_event (GtkGestureClick *click,
|
||||
gint n_press, gdouble x, gdouble y, GstClapperSink *self)
|
||||
{
|
||||
GtkWidget *widget;
|
||||
GdkEvent *event;
|
||||
GdkEventType event_type;
|
||||
const gchar *event_name;
|
||||
gdouble stream_x, stream_y;
|
||||
gboolean is_inactive;
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
is_inactive = (GST_STATE (self) < GST_STATE_PLAYING);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
if (is_inactive)
|
||||
return;
|
||||
|
||||
event = gtk_event_controller_get_current_event ((GtkEventController *) click);
|
||||
event_type = gdk_event_get_event_type (event);
|
||||
|
||||
/* FIXME: Touchscreen handling should probably use new touch events from GStreamer 1.22 */
|
||||
event_name = (event_type == GDK_BUTTON_PRESS || event_type == GDK_TOUCH_BEGIN)
|
||||
? "mouse-button-press"
|
||||
: (event_type == GDK_BUTTON_RELEASE || event_type == GDK_TOUCH_END)
|
||||
? "mouse-button-release"
|
||||
: NULL;
|
||||
|
||||
/* Can be NULL on touch */
|
||||
if (!event_name)
|
||||
return;
|
||||
|
||||
widget = gtk_event_controller_get_widget ((GtkEventController *) click);
|
||||
calculate_stream_coords (self, widget, x, y, &stream_x, &stream_y);
|
||||
GST_LOG ("Event \"%s\", x: %f, y: %f", event_name, stream_x, stream_y);
|
||||
|
||||
/* Gesture is set to handle only primary button, so we do not have to check */
|
||||
gst_navigation_send_mouse_event ((GstNavigation *) self, event_name,
|
||||
1, stream_x, stream_y);
|
||||
}
|
||||
|
||||
/* Must call from main thread only with a lock */
|
||||
static GtkWidget *
|
||||
gst_clapper_sink_get_widget (GstClapperSink *self)
|
||||
{
|
||||
if (G_UNLIKELY (!self->widget)) {
|
||||
GtkEventController *controller;
|
||||
GtkGesture *gesture;
|
||||
|
||||
/* Make sure GTK is initialized */
|
||||
if (!gtk_init_check ()) {
|
||||
GST_ERROR_OBJECT (self, "Could not ensure GTK initialization");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->widget = gtk_picture_new ();
|
||||
|
||||
/* Otherwise widget in grid will appear as a 1x1px
|
||||
* video which might be misleading for users */
|
||||
gtk_widget_set_hexpand (self->widget, TRUE);
|
||||
gtk_widget_set_vexpand (self->widget, TRUE);
|
||||
|
||||
gtk_widget_set_focusable (self->widget, TRUE);
|
||||
gtk_widget_set_can_focus (self->widget, TRUE);
|
||||
|
||||
controller = gtk_event_controller_motion_new ();
|
||||
g_signal_connect (controller, "motion",
|
||||
G_CALLBACK (gst_clapper_sink_widget_motion_event), self);
|
||||
gtk_widget_add_controller (self->widget, controller);
|
||||
|
||||
gesture = gtk_gesture_click_new ();
|
||||
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), 1);
|
||||
g_signal_connect (gesture, "pressed",
|
||||
G_CALLBACK (gst_clapper_sink_widget_button_event), self);
|
||||
g_signal_connect (gesture, "released",
|
||||
G_CALLBACK (gst_clapper_sink_widget_button_event), self);
|
||||
gtk_widget_add_controller (self->widget, GTK_EVENT_CONTROLLER (gesture));
|
||||
|
||||
/* TODO: Implement touch events once we depend on GStreamer 1.22 */
|
||||
|
||||
/* Take floating ref */
|
||||
g_object_ref_sink (self->widget);
|
||||
|
||||
/* Set widget back pointer */
|
||||
gst_clapper_paintable_set_widget (self->paintable, self->widget);
|
||||
|
||||
/* Set earlier remembered property */
|
||||
#if GTK_CHECK_VERSION(4,8,0)
|
||||
if (self->force_aspect_ratio)
|
||||
gtk_picture_set_content_fit (GTK_PICTURE (self->widget), GTK_CONTENT_FIT_CONTAIN);
|
||||
else
|
||||
gtk_picture_set_content_fit (GTK_PICTURE (self->widget), GTK_CONTENT_FIT_FILL);
|
||||
#else
|
||||
gtk_picture_set_keep_aspect_ratio (GTK_PICTURE (self->widget),
|
||||
self->force_aspect_ratio);
|
||||
#endif
|
||||
|
||||
gtk_picture_set_paintable (GTK_PICTURE (self->widget), GDK_PAINTABLE (self->paintable));
|
||||
|
||||
self->widget_destroy_id = g_signal_connect (self->widget,
|
||||
"destroy", G_CALLBACK (widget_destroy_cb), self);
|
||||
}
|
||||
|
||||
return self->widget;
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
gst_clapper_sink_obtain_widget (GstClapperSink *self)
|
||||
{
|
||||
GtkWidget *widget;
|
||||
|
||||
GST_CLAPPER_SINK_LOCK (self);
|
||||
widget = gst_clapper_sink_get_widget (self);
|
||||
if (widget)
|
||||
g_object_ref (widget);
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
|
||||
return widget;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_sink_get_property (GObject *object, guint prop_id,
|
||||
GValue *value, GParamSpec *pspec)
|
||||
{
|
||||
GstClapperSink *self = GST_CLAPPER_SINK_CAST (object);
|
||||
|
||||
GST_CLAPPER_SINK_LOCK (self);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_WIDGET:
|
||||
if (self->widget) {
|
||||
g_value_set_object (value, self->widget);
|
||||
} else {
|
||||
GtkWidget *widget;
|
||||
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
widget = gst_gtk_invoke_on_main ((GThreadFunc) gst_clapper_sink_obtain_widget, self);
|
||||
GST_CLAPPER_SINK_LOCK (self);
|
||||
|
||||
g_value_set_object (value, widget);
|
||||
g_object_unref (widget);
|
||||
}
|
||||
break;
|
||||
case PROP_FORCE_ASPECT_RATIO:
|
||||
g_value_set_boolean (value, self->force_aspect_ratio);
|
||||
break;
|
||||
case PROP_PIXEL_ASPECT_RATIO:
|
||||
gst_value_set_fraction (value, self->par_n, self->par_d);
|
||||
break;
|
||||
case PROP_KEEP_LAST_FRAME:
|
||||
g_value_set_boolean (value, self->keep_last_frame);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_sink_set_property (GObject *object, guint prop_id,
|
||||
const GValue *value, GParamSpec *pspec)
|
||||
{
|
||||
GstClapperSink *self = GST_CLAPPER_SINK_CAST (object);
|
||||
|
||||
GST_CLAPPER_SINK_LOCK (self);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_FORCE_ASPECT_RATIO:
|
||||
self->force_aspect_ratio = g_value_get_boolean (value);
|
||||
|
||||
if (self->widget) {
|
||||
#if GTK_CHECK_VERSION(4,8,0)
|
||||
if (self->force_aspect_ratio)
|
||||
gtk_picture_set_content_fit (GTK_PICTURE (self->widget), GTK_CONTENT_FIT_CONTAIN);
|
||||
else
|
||||
gtk_picture_set_content_fit (GTK_PICTURE (self->widget), GTK_CONTENT_FIT_FILL);
|
||||
#else
|
||||
gtk_picture_set_keep_aspect_ratio (GTK_PICTURE (self->widget),
|
||||
self->force_aspect_ratio);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case PROP_PIXEL_ASPECT_RATIO:
|
||||
self->par_n = gst_value_get_fraction_numerator (value);
|
||||
self->par_d = gst_value_get_fraction_denominator (value);
|
||||
|
||||
gst_clapper_paintable_set_pixel_aspect_ratio (self->paintable,
|
||||
self->par_n, self->par_d);
|
||||
break;
|
||||
case PROP_KEEP_LAST_FRAME:
|
||||
self->keep_last_frame = g_value_get_boolean (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_sink_navigation_send_event (GstNavigation *navigation,
|
||||
GstStructure *structure)
|
||||
{
|
||||
GstClapperSink *sink = GST_CLAPPER_SINK_CAST (navigation);
|
||||
GstEvent *event;
|
||||
|
||||
GST_TRACE_OBJECT (sink, "Navigation event: %" GST_PTR_FORMAT, structure);
|
||||
event = gst_event_new_navigation (structure);
|
||||
|
||||
if (G_LIKELY (event)) {
|
||||
GstPad *pad;
|
||||
|
||||
pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (sink));
|
||||
|
||||
if (G_LIKELY (pad)) {
|
||||
if (!gst_pad_send_event (pad, gst_event_ref (event))) {
|
||||
/* If upstream didn't handle the event we'll post a message with it
|
||||
* for the application in case it wants to do something with it */
|
||||
gst_element_post_message (GST_ELEMENT_CAST (sink),
|
||||
gst_navigation_message_new_event (GST_OBJECT_CAST (sink), event));
|
||||
}
|
||||
gst_object_unref (pad);
|
||||
}
|
||||
gst_event_unref (event);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_clapper_sink_propose_allocation (GstBaseSink *bsink, GstQuery *query)
|
||||
{
|
||||
GstClapperSink *self = GST_CLAPPER_SINK_CAST (bsink);
|
||||
GstCaps *caps;
|
||||
GstVideoInfo info;
|
||||
guint size, min_buffers;
|
||||
gboolean need_pool;
|
||||
|
||||
gst_query_parse_allocation (query, &caps, &need_pool);
|
||||
|
||||
if (!caps) {
|
||||
GST_DEBUG_OBJECT (self, "No caps specified");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!gst_video_info_from_caps (&info, caps)) {
|
||||
GST_DEBUG_OBJECT (self, "Invalid caps specified");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Normal size of a frame */
|
||||
size = GST_VIDEO_INFO_SIZE (&info);
|
||||
|
||||
/* We keep around current buffer and a pending one */
|
||||
min_buffers = 3;
|
||||
|
||||
if (need_pool) {
|
||||
GstBufferPool *pool;
|
||||
GstStructure *config = NULL;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Need to create buffer pool");
|
||||
|
||||
GST_CLAPPER_SINK_LOCK (self);
|
||||
pool = gst_clapper_importer_create_pool (self->importer, &config);
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
|
||||
if (pool) {
|
||||
/* If we did not get config, use default one */
|
||||
if (!config)
|
||||
config = gst_buffer_pool_get_config (pool);
|
||||
|
||||
gst_buffer_pool_config_set_params (config, caps, size, min_buffers, 0);
|
||||
|
||||
if (!gst_buffer_pool_set_config (pool, config)) {
|
||||
gst_object_unref (pool);
|
||||
|
||||
GST_ERROR_OBJECT (self, "Failed to set config");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gst_query_add_allocation_pool (query, pool, size, min_buffers, 0);
|
||||
gst_object_unref (pool);
|
||||
} else if (config) {
|
||||
GST_WARNING_OBJECT (self, "Got config without a pool to apply it");
|
||||
gst_structure_free (config);
|
||||
}
|
||||
}
|
||||
|
||||
GST_CLAPPER_SINK_LOCK (self);
|
||||
gst_clapper_importer_add_allocation_metas (self->importer, query);
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_clapper_sink_query (GstBaseSink *bsink, GstQuery *query)
|
||||
{
|
||||
GstClapperSink *self = GST_CLAPPER_SINK_CAST (bsink);
|
||||
gboolean res = FALSE;
|
||||
|
||||
if (GST_QUERY_TYPE (query) == GST_QUERY_CONTEXT) {
|
||||
GST_CLAPPER_SINK_LOCK (self);
|
||||
res = gst_clapper_importer_loader_handle_context_query (self->loader, bsink, query);
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
}
|
||||
|
||||
if (res)
|
||||
return TRUE;
|
||||
|
||||
return GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_clapper_sink_start_on_main (GstClapperSink *self)
|
||||
{
|
||||
GtkWidget *widget;
|
||||
|
||||
GST_CLAPPER_SINK_LOCK (self);
|
||||
|
||||
/* Make sure widget is created */
|
||||
if (!(widget = gst_clapper_sink_get_widget (self))) {
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* When no toplevel window, make our own */
|
||||
if (G_UNLIKELY (!gtk_widget_get_root (widget) && !self->window)) {
|
||||
GtkWidget *toplevel, *parent;
|
||||
GtkCssProvider *provider;
|
||||
gchar *win_title;
|
||||
|
||||
if ((parent = gtk_widget_get_parent (widget))) {
|
||||
GtkWidget *temp_parent;
|
||||
|
||||
while ((temp_parent = gtk_widget_get_parent (parent)))
|
||||
parent = temp_parent;
|
||||
}
|
||||
toplevel = (parent) ? parent : widget;
|
||||
|
||||
self->window = (GtkWindow *) gtk_window_new ();
|
||||
gtk_widget_add_css_class (GTK_WIDGET (self->window), WINDOW_CSS_CLASS_NAME);
|
||||
|
||||
provider = gtk_css_provider_new ();
|
||||
gtk_css_provider_load_from_data (provider,
|
||||
"." WINDOW_CSS_CLASS_NAME " { background: none; }", -1);
|
||||
gtk_style_context_add_provider_for_display (
|
||||
gdk_display_get_default (), GTK_STYLE_PROVIDER (provider),
|
||||
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
|
||||
g_object_unref (provider);
|
||||
|
||||
win_title = g_strdup_printf ("Clapper Sink - GTK %u.%u.%u Window",
|
||||
gtk_get_major_version (),
|
||||
gtk_get_minor_version (),
|
||||
gtk_get_micro_version ());
|
||||
|
||||
/* Set some common default size, adding stock headerbar height
|
||||
* to it in order to display 4:3 aspect video widget */
|
||||
gtk_window_set_default_size (self->window, 640, 480 + 37);
|
||||
gtk_window_set_title (self->window, win_title);
|
||||
gtk_window_set_child (self->window, toplevel);
|
||||
|
||||
g_free (win_title);
|
||||
|
||||
self->window_destroy_id = g_signal_connect (self->window,
|
||||
"destroy", G_CALLBACK (window_destroy_cb), self);
|
||||
}
|
||||
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
window_present_on_main_idle (GtkWindow *window)
|
||||
{
|
||||
GST_INFO ("Presenting window");
|
||||
gtk_window_present (window);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_clapper_sink_start (GstBaseSink *bsink)
|
||||
{
|
||||
GstClapperSink *self = GST_CLAPPER_SINK_CAST (bsink);
|
||||
|
||||
GST_INFO_OBJECT (self, "Start");
|
||||
|
||||
if (G_UNLIKELY (!(! !gst_gtk_invoke_on_main ((GThreadFunc) (GCallback)
|
||||
gst_clapper_sink_start_on_main, self)))) {
|
||||
GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
|
||||
("GtkWidget could not be created"), (NULL));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_clapper_sink_stop_on_main (GstClapperSink *self)
|
||||
{
|
||||
GtkWindow *window = NULL;
|
||||
|
||||
GST_CLAPPER_SINK_LOCK (self);
|
||||
if (self->window)
|
||||
window = g_object_ref (self->window);
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
|
||||
if (window) {
|
||||
gtk_window_destroy (window);
|
||||
g_object_unref (window);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_clapper_sink_stop (GstBaseSink *bsink)
|
||||
{
|
||||
GstClapperSink *self = GST_CLAPPER_SINK_CAST (bsink);
|
||||
gboolean has_window;
|
||||
|
||||
GST_INFO_OBJECT (self, "Stop");
|
||||
|
||||
GST_CLAPPER_SINK_LOCK (self);
|
||||
has_window = (self->window != NULL);
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
|
||||
if (G_UNLIKELY (has_window)) {
|
||||
return (! !gst_gtk_invoke_on_main ((GThreadFunc) (GCallback)
|
||||
gst_clapper_sink_stop_on_main, self));
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
gst_clapper_sink_change_state (GstElement *element, GstStateChange transition)
|
||||
{
|
||||
GstClapperSink *self = GST_CLAPPER_SINK_CAST (element);
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Changing state: %s => %s",
|
||||
gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
|
||||
gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||
GST_CLAPPER_SINK_LOCK (self);
|
||||
if (!self->keep_last_frame && self->importer) {
|
||||
gst_clapper_importer_set_buffer (self->importer, NULL);
|
||||
gst_clapper_paintable_queue_draw (self->paintable);
|
||||
}
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
break;
|
||||
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
||||
GST_CLAPPER_SINK_LOCK (self);
|
||||
if (G_UNLIKELY (self->window && !self->presented_window)) {
|
||||
g_idle_add_full (G_PRIORITY_DEFAULT,
|
||||
(GSourceFunc) window_present_on_main_idle,
|
||||
g_object_ref (self->window), (GDestroyNotify) g_object_unref);
|
||||
self->presented_window = TRUE;
|
||||
}
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_sink_get_times (GstBaseSink *bsink, GstBuffer *buffer,
|
||||
GstClockTime *start, GstClockTime *end)
|
||||
{
|
||||
if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer))
|
||||
return;
|
||||
|
||||
*start = GST_BUFFER_TIMESTAMP (buffer);
|
||||
|
||||
if (GST_BUFFER_DURATION_IS_VALID (buffer)) {
|
||||
*end = *start + GST_BUFFER_DURATION (buffer);
|
||||
} else {
|
||||
GstClapperSink *self = GST_CLAPPER_SINK_CAST (bsink);
|
||||
gint fps_n, fps_d;
|
||||
|
||||
GST_CLAPPER_SINK_LOCK (self);
|
||||
fps_n = GST_VIDEO_INFO_FPS_N (&self->v_info);
|
||||
fps_d = GST_VIDEO_INFO_FPS_D (&self->v_info);
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
|
||||
if (fps_n > 0)
|
||||
*end = *start + gst_util_uint64_scale_int (GST_SECOND, fps_d, fps_n);
|
||||
}
|
||||
}
|
||||
|
||||
static GstCaps *
|
||||
gst_clapper_sink_get_caps (GstBaseSink *bsink, GstCaps *filter)
|
||||
{
|
||||
GstClapperSink *self = GST_CLAPPER_SINK_CAST (bsink);
|
||||
GstCaps *result, *tmp;
|
||||
|
||||
tmp = gst_clapper_importer_loader_make_actual_caps (self->loader);
|
||||
|
||||
if (filter) {
|
||||
GST_DEBUG ("Intersecting with filter caps: %" GST_PTR_FORMAT, filter);
|
||||
result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
|
||||
gst_caps_unref (tmp);
|
||||
} else {
|
||||
result = tmp;
|
||||
}
|
||||
GST_DEBUG ("Returning caps: %" GST_PTR_FORMAT, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_clapper_sink_set_caps (GstBaseSink *bsink, GstCaps *caps)
|
||||
{
|
||||
GstClapperSink *self = GST_CLAPPER_SINK_CAST (bsink);
|
||||
|
||||
GST_INFO_OBJECT (self, "Set caps: %" GST_PTR_FORMAT, caps);
|
||||
GST_CLAPPER_SINK_LOCK (self);
|
||||
|
||||
if (G_UNLIKELY (!self->widget)) {
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
|
||||
("Output widget was destroyed"), (NULL));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!gst_clapper_importer_loader_find_importer_for_caps (self->loader, caps, &self->importer)) {
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
|
||||
("No importer for given caps found"), (NULL));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
gst_clapper_paintable_set_importer (self->paintable, self->importer);
|
||||
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
|
||||
return GST_BASE_SINK_CLASS (parent_class)->set_caps (bsink, caps);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_clapper_sink_set_info (GstVideoSink *vsink, GstCaps *caps, const GstVideoInfo *info)
|
||||
{
|
||||
GstClapperSink *self = GST_CLAPPER_SINK_CAST (vsink);
|
||||
gboolean res;
|
||||
|
||||
GST_CLAPPER_SINK_LOCK (self);
|
||||
|
||||
self->v_info = *info;
|
||||
GST_DEBUG_OBJECT (self, "Video info changed");
|
||||
|
||||
res = gst_clapper_paintable_set_video_info (self->paintable, info);
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_clapper_sink_show_frame (GstVideoSink *vsink, GstBuffer *buffer)
|
||||
{
|
||||
GstClapperSink *self = GST_CLAPPER_SINK_CAST (vsink);
|
||||
|
||||
GST_TRACE ("Got %" GST_PTR_FORMAT, buffer);
|
||||
GST_CLAPPER_SINK_LOCK (self);
|
||||
|
||||
if (G_UNLIKELY (!self->widget)) {
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
|
||||
("Output widget was destroyed"), (NULL));
|
||||
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
gst_clapper_importer_set_buffer (self->importer, buffer);
|
||||
gst_clapper_paintable_queue_draw (self->paintable);
|
||||
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_sink_init (GstClapperSink *self)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
|
||||
gobject_class = (GObjectClass *) GST_CLAPPER_SINK_GET_CLASS (self);
|
||||
|
||||
/* HACK: install here instead of class init to avoid GStreamer
|
||||
* plugin scanner GObject type conflicts with older GTK versions */
|
||||
if (!g_object_class_find_property (gobject_class, "widget")) {
|
||||
g_object_class_install_property (gobject_class, PROP_WIDGET,
|
||||
g_param_spec_object ("widget", "GTK Widget",
|
||||
"The GtkWidget to place in the widget hierarchy",
|
||||
GTK_TYPE_WIDGET, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
self->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
|
||||
self->par_n = DEFAULT_PAR_N;
|
||||
self->par_d = DEFAULT_PAR_D;
|
||||
self->keep_last_frame = DEFAULT_KEEP_LAST_FRAME;
|
||||
|
||||
g_mutex_init (&self->lock);
|
||||
gst_video_info_init (&self->v_info);
|
||||
|
||||
self->paintable = gst_clapper_paintable_new ();
|
||||
self->loader = gst_clapper_importer_loader_new ();
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_sink_dispose (GObject *object)
|
||||
{
|
||||
GstClapperSink *self = GST_CLAPPER_SINK_CAST (object);
|
||||
|
||||
GST_CLAPPER_SINK_LOCK (self);
|
||||
|
||||
window_clear_no_lock (self);
|
||||
widget_clear_no_lock (self);
|
||||
|
||||
g_clear_object (&self->paintable);
|
||||
gst_clear_object (&self->importer);
|
||||
|
||||
GST_CLAPPER_SINK_UNLOCK (self);
|
||||
|
||||
GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_sink_finalize (GObject *object)
|
||||
{
|
||||
GstClapperSink *self = GST_CLAPPER_SINK_CAST (object);
|
||||
|
||||
GST_TRACE ("Finalize");
|
||||
|
||||
gst_clear_object (&self->loader);
|
||||
g_mutex_clear (&self->lock);
|
||||
|
||||
GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_sink_class_init (GstClapperSinkClass *klass)
|
||||
{
|
||||
GstPadTemplate *sink_pad_templ;
|
||||
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
GstElementClass *gstelement_class = (GstElementClass *) klass;
|
||||
GstBaseSinkClass *gstbasesink_class = (GstBaseSinkClass *) klass;
|
||||
GstVideoSinkClass *gstvideosink_class = (GstVideoSinkClass *) klass;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clappersink", 0,
|
||||
"Clapper Sink");
|
||||
|
||||
gobject_class->get_property = gst_clapper_sink_get_property;
|
||||
gobject_class->set_property = gst_clapper_sink_set_property;
|
||||
gobject_class->dispose = gst_clapper_sink_dispose;
|
||||
gobject_class->finalize = gst_clapper_sink_finalize;
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
|
||||
g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
|
||||
"When enabled, scaling will respect original aspect ratio",
|
||||
DEFAULT_FORCE_ASPECT_RATIO,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
|
||||
gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio",
|
||||
"The pixel aspect ratio of the device",
|
||||
DEFAULT_PAR_N, DEFAULT_PAR_D,
|
||||
G_MAXINT, 1, 1, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_KEEP_LAST_FRAME,
|
||||
g_param_spec_boolean ("keep-last-frame", "Keep last frame",
|
||||
"Keep showing last video frame after playback instead of black screen",
|
||||
DEFAULT_KEEP_LAST_FRAME,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
gstelement_class->change_state = gst_clapper_sink_change_state;
|
||||
|
||||
gstbasesink_class->get_caps = gst_clapper_sink_get_caps;
|
||||
gstbasesink_class->set_caps = gst_clapper_sink_set_caps;
|
||||
gstbasesink_class->get_times = gst_clapper_sink_get_times;
|
||||
gstbasesink_class->propose_allocation = gst_clapper_sink_propose_allocation;
|
||||
gstbasesink_class->query = gst_clapper_sink_query;
|
||||
gstbasesink_class->start = gst_clapper_sink_start;
|
||||
gstbasesink_class->stop = gst_clapper_sink_stop;
|
||||
|
||||
gstvideosink_class->set_info = gst_clapper_sink_set_info;
|
||||
gstvideosink_class->show_frame = gst_clapper_sink_show_frame;
|
||||
|
||||
gst_element_class_set_static_metadata (gstelement_class,
|
||||
"Clapper video sink",
|
||||
"Sink/Video", "A GTK4 video sink used by Clapper media player",
|
||||
"Rafał Dzięgiel <rafostar.github@gmail.com>");
|
||||
|
||||
sink_pad_templ = gst_clapper_importer_loader_make_sink_pad_template ();
|
||||
gst_element_class_add_pad_template (gstelement_class, sink_pad_templ);
|
||||
}
|
||||
|
||||
/*
|
||||
* GstNavigationInterface
|
||||
*/
|
||||
static void
|
||||
gst_clapper_sink_navigation_interface_init (GstNavigationInterface *iface)
|
||||
{
|
||||
/* TODO: Port to "send_event_simple" once we depend on GStreamer 1.22 */
|
||||
iface->send_event = gst_clapper_sink_navigation_send_event;
|
||||
}
|
74
lib/gst/plugin/gstclappersink.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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 <gtk/gtk.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gst/video/gstvideosink.h>
|
||||
#include <gst/video/video.h>
|
||||
|
||||
#include "gstclapperpaintable.h"
|
||||
#include "gstclapperimporterloader.h"
|
||||
#include "gstclapperimporter.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_CLAPPER_SINK (gst_clapper_sink_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstClapperSink, gst_clapper_sink, GST, CLAPPER_SINK, GstVideoSink)
|
||||
|
||||
#define GST_CLAPPER_SINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CLAPPER_SINK, GstClapperSinkClass))
|
||||
#define GST_CLAPPER_SINK_CAST(obj) ((GstClapperSink *)(obj))
|
||||
|
||||
#define GST_CLAPPER_SINK_GET_LOCK(obj) (&GST_CLAPPER_SINK_CAST(obj)->lock)
|
||||
#define GST_CLAPPER_SINK_LOCK(obj) g_mutex_lock (GST_CLAPPER_SINK_GET_LOCK(obj))
|
||||
#define GST_CLAPPER_SINK_UNLOCK(obj) g_mutex_unlock (GST_CLAPPER_SINK_GET_LOCK(obj))
|
||||
|
||||
struct _GstClapperSink
|
||||
{
|
||||
GstVideoSink parent;
|
||||
|
||||
GMutex lock;
|
||||
|
||||
GstClapperPaintable *paintable;
|
||||
GstClapperImporterLoader *loader;
|
||||
GstClapperImporter *importer;
|
||||
GstVideoInfo v_info;
|
||||
|
||||
GtkWidget *widget;
|
||||
GtkWindow *window;
|
||||
|
||||
gboolean presented_window;
|
||||
|
||||
/* Properties */
|
||||
gboolean force_aspect_ratio;
|
||||
gint par_n, par_d;
|
||||
gboolean keep_last_frame;
|
||||
|
||||
/* Position coords */
|
||||
gdouble last_pos_x;
|
||||
gdouble last_pos_y;
|
||||
|
||||
gulong widget_destroy_id;
|
||||
gulong window_destroy_id;
|
||||
};
|
||||
|
||||
GST_ELEMENT_REGISTER_DECLARE (clappersink);
|
||||
|
||||
G_END_DECLS
|
37
lib/gst/plugin/gstgdkformats.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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 <glib.h>
|
||||
|
||||
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
||||
#define GST_GDK_MEMORY_ENDIAN_FORMATS "RGBA64_LE"
|
||||
#define GST_GDK_GL_TEXTURE_ENDIAN_FORMATS "RGBA64_LE"
|
||||
#elif G_BYTE_ORDER == G_BIG_ENDIAN
|
||||
#define GST_GDK_MEMORY_ENDIAN_FORMATS "RGBA64_BE"
|
||||
#define GST_GDK_GL_TEXTURE_ENDIAN_FORMATS "RGBA64_BE"
|
||||
#endif
|
||||
|
||||
#define GST_GDK_MEMORY_FORMATS \
|
||||
GST_GDK_MEMORY_ENDIAN_FORMATS ", " \
|
||||
"ABGR, BGRA, ARGB, RGBA, BGRx, RGBx, BGR, RGB"
|
||||
|
||||
/* Formats that `GdkGLTexture` supports */
|
||||
#define GST_GDK_GL_TEXTURE_FORMATS \
|
||||
GST_GDK_GL_TEXTURE_ENDIAN_FORMATS ", " \
|
||||
"RGBA, RGBx, RGB"
|
142
lib/gst/plugin/gstgtkutils.c
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* GStreamer
|
||||
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
||||
* Copyright (C) 2015 Thibault Saunier <tsaunier@gnome.org>
|
||||
* Copyright (C) 2022 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 Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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 "gstgtkutils.h"
|
||||
|
||||
#define _IS_FRAME_PREMULTIPLIED(f) (GST_VIDEO_INFO_FLAG_IS_SET (&(f)->info, GST_VIDEO_FLAG_PREMULTIPLIED_ALPHA))
|
||||
|
||||
struct invoke_context
|
||||
{
|
||||
GThreadFunc func;
|
||||
gpointer data;
|
||||
GMutex lock;
|
||||
GCond cond;
|
||||
gboolean fired;
|
||||
|
||||
gpointer res;
|
||||
};
|
||||
|
||||
static gboolean
|
||||
gst_gtk_invoke_func (struct invoke_context *info)
|
||||
{
|
||||
g_mutex_lock (&info->lock);
|
||||
info->res = info->func (info->data);
|
||||
info->fired = TRUE;
|
||||
g_cond_signal (&info->cond);
|
||||
g_mutex_unlock (&info->lock);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
gpointer
|
||||
gst_gtk_invoke_on_main (GThreadFunc func, gpointer data)
|
||||
{
|
||||
GMainContext *main_context = g_main_context_default ();
|
||||
struct invoke_context info;
|
||||
|
||||
g_mutex_init (&info.lock);
|
||||
g_cond_init (&info.cond);
|
||||
info.fired = FALSE;
|
||||
info.func = func;
|
||||
info.data = data;
|
||||
|
||||
g_main_context_invoke (main_context, (GSourceFunc) gst_gtk_invoke_func,
|
||||
&info);
|
||||
|
||||
g_mutex_lock (&info.lock);
|
||||
while (!info.fired)
|
||||
g_cond_wait (&info.cond, &info.lock);
|
||||
g_mutex_unlock (&info.lock);
|
||||
|
||||
g_mutex_clear (&info.lock);
|
||||
g_cond_clear (&info.cond);
|
||||
|
||||
return info.res;
|
||||
}
|
||||
|
||||
static GdkMemoryFormat
|
||||
gst_gdk_memory_format_from_frame (GstVideoFrame *frame)
|
||||
{
|
||||
switch (GST_VIDEO_FRAME_FORMAT (frame)) {
|
||||
case GST_VIDEO_FORMAT_RGBA64_LE:
|
||||
case GST_VIDEO_FORMAT_RGBA64_BE:
|
||||
return (_IS_FRAME_PREMULTIPLIED (frame))
|
||||
? GDK_MEMORY_R16G16B16A16_PREMULTIPLIED
|
||||
: GDK_MEMORY_R16G16B16A16;
|
||||
case GST_VIDEO_FORMAT_RGBA:
|
||||
return (_IS_FRAME_PREMULTIPLIED (frame))
|
||||
? GDK_MEMORY_R8G8B8A8_PREMULTIPLIED
|
||||
: GDK_MEMORY_R8G8B8A8;
|
||||
case GST_VIDEO_FORMAT_BGRA:
|
||||
return (_IS_FRAME_PREMULTIPLIED (frame))
|
||||
? GDK_MEMORY_B8G8R8A8_PREMULTIPLIED
|
||||
: GDK_MEMORY_B8G8R8A8;
|
||||
case GST_VIDEO_FORMAT_ARGB:
|
||||
return (_IS_FRAME_PREMULTIPLIED (frame))
|
||||
? GDK_MEMORY_A8R8G8B8_PREMULTIPLIED
|
||||
: GDK_MEMORY_A8R8G8B8;
|
||||
case GST_VIDEO_FORMAT_ABGR:
|
||||
/* GTK is missing premultiplied ABGR support */
|
||||
return GDK_MEMORY_A8B8G8R8;
|
||||
case GST_VIDEO_FORMAT_RGBx:
|
||||
return GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
|
||||
case GST_VIDEO_FORMAT_BGRx:
|
||||
return GDK_MEMORY_B8G8R8A8_PREMULTIPLIED;
|
||||
case GST_VIDEO_FORMAT_RGB:
|
||||
return GDK_MEMORY_R8G8B8;
|
||||
case GST_VIDEO_FORMAT_BGR:
|
||||
return GDK_MEMORY_B8G8R8;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* This should never happen as long as above switch statement
|
||||
* is updated when new formats are added to caps */
|
||||
g_assert_not_reached ();
|
||||
|
||||
/* Fallback format */
|
||||
return GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
|
||||
}
|
||||
|
||||
GdkTexture *
|
||||
gst_video_frame_into_gdk_texture (GstVideoFrame *frame)
|
||||
{
|
||||
GdkTexture *texture;
|
||||
GBytes *bytes;
|
||||
|
||||
bytes = g_bytes_new_with_free_func (
|
||||
GST_VIDEO_FRAME_PLANE_DATA (frame, 0),
|
||||
GST_VIDEO_FRAME_HEIGHT (frame) * GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0),
|
||||
(GDestroyNotify) gst_buffer_unref,
|
||||
gst_buffer_ref (frame->buffer));
|
||||
|
||||
texture = gdk_memory_texture_new (
|
||||
GST_VIDEO_FRAME_WIDTH (frame),
|
||||
GST_VIDEO_FRAME_HEIGHT (frame),
|
||||
gst_gdk_memory_format_from_frame (frame),
|
||||
bytes,
|
||||
GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0));
|
||||
|
||||
g_bytes_unref (bytes);
|
||||
|
||||
return texture;
|
||||
}
|
@@ -1,5 +1,8 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Rafał Dzięgiel <rafostar.github@gmail.com>
|
||||
* GStreamer
|
||||
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
|
||||
* Copyright (C) 2015 Thibault Saunier <tsaunier@gnome.org>
|
||||
* Copyright (C) 2022 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 Library General Public
|
||||
@@ -17,33 +20,16 @@
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_CLAPPER_PLAYLIST_ITEM_PRIVATE_H__
|
||||
#define __GST_CLAPPER_PLAYLIST_ITEM_PRIVATE_H__
|
||||
#pragma once
|
||||
|
||||
#include "gstclapper-playlist.h"
|
||||
#include <glib.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <gst/video/video.h>
|
||||
|
||||
struct _GstClapperPlaylistItem
|
||||
{
|
||||
GstObject parent;
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/* ID of the playlist this item belongs to */
|
||||
gchar *owner_uuid;
|
||||
gint id;
|
||||
gpointer gst_gtk_invoke_on_main (GThreadFunc func, gpointer data);
|
||||
|
||||
gchar *uri;
|
||||
gchar *suburi;
|
||||
gchar *custom_title;
|
||||
GdkTexture * gst_video_frame_into_gdk_texture (GstVideoFrame *frame);
|
||||
|
||||
/* Signals */
|
||||
gulong activated_signal_id;
|
||||
};
|
||||
|
||||
struct _GstClapperPlaylistItemClass
|
||||
{
|
||||
GstObjectClass parent_class;
|
||||
};
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
void gst_clapper_playlist_item_mark_added (GstClapperPlaylistItem *item, GstClapperPlaylist *playlist);
|
||||
|
||||
#endif /* __GST_CLAPPER_PLAYLIST_ITEM_PRIVATE_H__ */
|
||||
G_END_DECLS
|
43
lib/gst/plugin/gstplugin.c
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gmodule.h>
|
||||
|
||||
#include "gstclappersink.h"
|
||||
|
||||
static gboolean
|
||||
plugin_init (GstPlugin *plugin)
|
||||
{
|
||||
if (!g_module_supported ())
|
||||
return FALSE;
|
||||
|
||||
gst_plugin_add_dependency_simple (plugin,
|
||||
NULL, CLAPPER_SINK_IMPORTER_PATH, NULL,
|
||||
GST_PLUGIN_DEPENDENCY_FLAG_NONE);
|
||||
|
||||
return GST_ELEMENT_REGISTER (clappersink, plugin);
|
||||
}
|
||||
|
||||
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
|
||||
clapper, "Clapper elements", plugin_init, VERSION, "LGPL",
|
||||
GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
|
583
lib/gst/plugin/handlers/gl/gstclapperglcontexthandler.c
Normal file
@@ -0,0 +1,583 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstclapperglcontexthandler.h"
|
||||
#include "gst/plugin/gstgdkformats.h"
|
||||
#include "gst/plugin/gstgtkutils.h"
|
||||
|
||||
#if GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_WAYLAND
|
||||
#include <gdk/wayland/gdkwayland.h>
|
||||
#include <gst/gl/wayland/gstgldisplay_wayland.h>
|
||||
#endif
|
||||
|
||||
#if GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_X11
|
||||
#include <gdk/x11/gdkx.h>
|
||||
#endif
|
||||
#if GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_X11_GLX
|
||||
#include <gst/gl/x11/gstgldisplay_x11.h>
|
||||
#endif
|
||||
#if GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_X11_EGL || GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_WIN32_EGL
|
||||
#include <gst/gl/egl/gstgldisplay_egl.h>
|
||||
#endif
|
||||
|
||||
#if GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_WIN32
|
||||
#include <gdk/win32/gdkwin32.h>
|
||||
#endif
|
||||
|
||||
#if GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_MACOS
|
||||
#include <gdk/macos/gdkmacos.h>
|
||||
#endif
|
||||
|
||||
#define GST_CAT_DEFAULT gst_clapper_gl_context_handler_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
#define parent_class gst_clapper_gl_context_handler_parent_class
|
||||
G_DEFINE_TYPE (GstClapperGLContextHandler, gst_clapper_gl_context_handler, GST_TYPE_OBJECT);
|
||||
|
||||
static GstGLContext *
|
||||
_wrap_current_gl (GstGLDisplay *display, GdkGLAPI gdk_gl_api, GstGLPlatform platform)
|
||||
{
|
||||
GstGLAPI gst_gl_api = GST_GL_API_NONE;
|
||||
|
||||
switch (gdk_gl_api) {
|
||||
case GDK_GL_API_GL:
|
||||
gst_gl_api = GST_GL_API_OPENGL | GST_GL_API_OPENGL3;
|
||||
break;
|
||||
case GDK_GL_API_GLES:
|
||||
gst_gl_api = GST_GL_API_GLES2;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
}
|
||||
|
||||
if (gst_gl_api != GST_GL_API_NONE) {
|
||||
guintptr gl_handle;
|
||||
|
||||
gst_gl_display_filter_gl_api (display, gst_gl_api);
|
||||
|
||||
if ((gl_handle = gst_gl_context_get_current_gl_context (platform)))
|
||||
return gst_gl_context_new_wrapped (display, gl_handle, platform, gst_gl_api);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_realize_gdk_context_with_api (GdkGLContext *gdk_context, GdkGLAPI api, gint maj, gint min)
|
||||
{
|
||||
GError *error = NULL;
|
||||
gboolean success;
|
||||
|
||||
gdk_gl_context_set_allowed_apis (gdk_context, api);
|
||||
gdk_gl_context_set_required_version (gdk_context, maj, min);
|
||||
|
||||
GST_DEBUG ("Trying to realize %s context, min ver: %i.%i",
|
||||
(api & GDK_GL_API_GL) ? "GL" : "GLES", maj, min);
|
||||
|
||||
if (!(success = gdk_gl_context_realize (gdk_context, &error))) {
|
||||
GST_DEBUG ("Could not realize Gdk context with %s: %s",
|
||||
(api & GDK_GL_API_GL) ? "GL" : "GLES", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_gl_context_handler_context_realize (GstClapperGLContextHandler *self, GdkGLContext *gdk_context)
|
||||
{
|
||||
GdkGLAPI preferred_api = GDK_GL_API_GL;
|
||||
GdkDisplay *gdk_display;
|
||||
const gchar *gl_env;
|
||||
gboolean success;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Realizing GdkGLContext with default implementation");
|
||||
|
||||
/* Use single "GST_GL_API" env to also influence Gdk GL selection */
|
||||
if ((gl_env = g_getenv ("GST_GL_API"))) {
|
||||
preferred_api = (g_str_has_prefix (gl_env, "gles"))
|
||||
? GDK_GL_API_GLES
|
||||
: g_str_has_prefix (gl_env, "opengl")
|
||||
? GDK_GL_API_GL
|
||||
: GDK_GL_API_GL | GDK_GL_API_GLES;
|
||||
|
||||
/* With requested by user API, we either use it or give up */
|
||||
return _realize_gdk_context_with_api (gdk_context, preferred_api, 0, 0);
|
||||
}
|
||||
|
||||
gdk_display = gdk_gl_context_get_display (gdk_context);
|
||||
GST_DEBUG_OBJECT (self, "Auto selecting GL API for display: %s",
|
||||
gdk_display_get_name (gdk_display));
|
||||
|
||||
/* Apple decoder uses rectangle texture-target, which GLES does not support.
|
||||
* For Linux we prefer EGL + GLES in order to get direct HW colorspace conversion.
|
||||
* Windows will try EGL + GLES setup first and auto fallback to WGL. */
|
||||
#if GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_WAYLAND
|
||||
if (GDK_IS_WAYLAND_DISPLAY (gdk_display))
|
||||
preferred_api = GDK_GL_API_GLES;
|
||||
#endif
|
||||
#if GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_X11_EGL
|
||||
if (GDK_IS_X11_DISPLAY (gdk_display) && gdk_x11_display_get_egl_display (gdk_display))
|
||||
preferred_api = GDK_GL_API_GLES;
|
||||
#endif
|
||||
#if GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_WIN32_EGL
|
||||
if (GDK_IS_WIN32_DISPLAY (gdk_display) && gdk_win32_display_get_egl_display (gdk_display))
|
||||
preferred_api = GDK_GL_API_GLES;
|
||||
#endif
|
||||
|
||||
/* FIXME: Remove once GStreamer can handle DRM modifiers. This tries to avoid
|
||||
* "scrambled" image on Linux with Intel GPUs that are mostly used together with
|
||||
* x86 CPUs at the expense of using slightly slower non-direct DMABuf import.
|
||||
* See: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/1236 */
|
||||
#if GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_WAYLAND || GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_X11_EGL
|
||||
#if !defined(HAVE_GST_PATCHES) && (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64))
|
||||
preferred_api = GDK_GL_API_GL;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Continue with GLES only if it should have "GL_EXT_texture_norm16"
|
||||
* extension, as we need it to handle P010_10LE, etc. */
|
||||
if ((preferred_api == GDK_GL_API_GLES)
|
||||
&& _realize_gdk_context_with_api (gdk_context, GDK_GL_API_GLES, 3, 1))
|
||||
return TRUE;
|
||||
|
||||
/* If not using GLES 3.1, try with core GL 3.2 that GTK4 defaults to */
|
||||
if (_realize_gdk_context_with_api (gdk_context, GDK_GL_API_GL, 3, 2))
|
||||
return TRUE;
|
||||
|
||||
/* Try with what we normally prefer first, otherwise use fallback */
|
||||
if (!(success = _realize_gdk_context_with_api (gdk_context, preferred_api, 0, 0))) {
|
||||
GdkGLAPI fallback_api;
|
||||
|
||||
fallback_api = (GDK_GL_API_GL | GDK_GL_API_GLES);
|
||||
fallback_api &= ~preferred_api;
|
||||
|
||||
success = _realize_gdk_context_with_api (gdk_context, fallback_api, 0, 0);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_retrieve_gl_context_on_main (GstClapperGLContextHandler *self)
|
||||
{
|
||||
GdkDisplay *gdk_display;
|
||||
GdkGLContext *gdk_context;
|
||||
GError *error = NULL;
|
||||
GdkGLAPI gdk_gl_api;
|
||||
GstGLPlatform platform = GST_GL_PLATFORM_NONE;
|
||||
gint gl_major = 0, gl_minor = 0;
|
||||
|
||||
if (!gtk_init_check ()) {
|
||||
GST_ERROR_OBJECT (self, "Could not ensure GTK initialization");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gdk_display = gdk_display_get_default ();
|
||||
|
||||
if (G_UNLIKELY (!gdk_display)) {
|
||||
GST_ERROR_OBJECT (self, "Could not retrieve Gdk display");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!(gdk_context = gdk_display_create_gl_context (gdk_display, &error))) {
|
||||
GST_ERROR_OBJECT (self, "Error creating Gdk GL context: %s",
|
||||
error ? error->message : "No error set by Gdk");
|
||||
g_clear_error (&error);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!_gl_context_handler_context_realize (self, gdk_context)) {
|
||||
GST_ERROR_OBJECT (self, "Could not realize Gdk context: %" GST_PTR_FORMAT,
|
||||
gdk_context);
|
||||
g_object_unref (gdk_context);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
gdk_gl_api = gdk_gl_context_get_api (gdk_context);
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
|
||||
self->gdk_context = gdk_context;
|
||||
|
||||
#if GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_WAYLAND
|
||||
if (GDK_IS_WAYLAND_DISPLAY (gdk_display)) {
|
||||
struct wl_display *wayland_display =
|
||||
gdk_wayland_display_get_wl_display (gdk_display);
|
||||
self->gst_display = (GstGLDisplay *)
|
||||
gst_gl_display_wayland_new_with_display (wayland_display);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_X11
|
||||
if (GDK_IS_X11_DISPLAY (gdk_display)) {
|
||||
gpointer display_ptr;
|
||||
#if GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_X11_EGL
|
||||
display_ptr = gdk_x11_display_get_egl_display (gdk_display);
|
||||
if (display_ptr) {
|
||||
self->gst_display = (GstGLDisplay *)
|
||||
gst_gl_display_egl_new_with_egl_display (display_ptr);
|
||||
}
|
||||
#endif
|
||||
#if GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_X11_GLX
|
||||
if (!self->gst_display) {
|
||||
display_ptr = gdk_x11_display_get_xdisplay (gdk_display);
|
||||
self->gst_display = (GstGLDisplay *)
|
||||
gst_gl_display_x11_new_with_display (display_ptr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_WIN32
|
||||
if (GDK_IS_WIN32_DISPLAY (gdk_display)) {
|
||||
#if GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_WIN32_EGL
|
||||
gpointer display_ptr = gdk_win32_display_get_egl_display (gdk_display);
|
||||
if (display_ptr) {
|
||||
self->gst_display = (GstGLDisplay *)
|
||||
gst_gl_display_egl_new_with_egl_display (display_ptr);
|
||||
}
|
||||
#endif
|
||||
#if GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_WIN32_WGL
|
||||
if (!self->gst_display) {
|
||||
self->gst_display =
|
||||
gst_gl_display_new_with_type (GST_GL_DISPLAY_TYPE_WIN32);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_MACOS
|
||||
if (GDK_IS_MACOS_DISPLAY (gdk_display)) {
|
||||
self->gst_display =
|
||||
gst_gl_display_new_with_type (GST_GL_DISPLAY_TYPE_COCOA);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Fallback to generic display */
|
||||
if (G_UNLIKELY (!self->gst_display)) {
|
||||
GST_WARNING_OBJECT (self, "Unknown Gdk display!");
|
||||
self->gst_display = gst_gl_display_new ();
|
||||
}
|
||||
|
||||
#if GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_WAYLAND
|
||||
if (GST_IS_GL_DISPLAY_WAYLAND (self->gst_display)) {
|
||||
platform = GST_GL_PLATFORM_EGL;
|
||||
GST_INFO_OBJECT (self, "Using EGL on Wayland");
|
||||
goto have_display;
|
||||
}
|
||||
#endif
|
||||
#if GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_X11_EGL
|
||||
if (GST_IS_GL_DISPLAY_EGL (self->gst_display)
|
||||
&& GDK_IS_X11_DISPLAY (gdk_display)) {
|
||||
platform = GST_GL_PLATFORM_EGL;
|
||||
GST_INFO_OBJECT (self, "Using EGL on x11");
|
||||
goto have_display;
|
||||
}
|
||||
#endif
|
||||
#if GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_X11_GLX
|
||||
if (GST_IS_GL_DISPLAY_X11 (self->gst_display)) {
|
||||
platform = GST_GL_PLATFORM_GLX;
|
||||
GST_INFO_OBJECT (self, "Using GLX on x11");
|
||||
goto have_display;
|
||||
}
|
||||
#endif
|
||||
#if GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_WIN32_EGL
|
||||
if (GST_IS_GL_DISPLAY_EGL (self->gst_display)
|
||||
&& GDK_IS_WIN32_DISPLAY (gdk_display)) {
|
||||
platform = GST_GL_PLATFORM_EGL;
|
||||
GST_INFO_OBJECT (self, "Using EGL on Win32");
|
||||
goto have_display;
|
||||
}
|
||||
#endif
|
||||
#if GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_WIN32_WGL
|
||||
if (gst_gl_display_get_handle_type (self->gst_display) == GST_GL_DISPLAY_TYPE_WIN32) {
|
||||
platform = GST_GL_PLATFORM_WGL;
|
||||
GST_INFO_OBJECT (self, "Using WGL on Win32");
|
||||
goto have_display;
|
||||
}
|
||||
#endif
|
||||
#if GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_MACOS
|
||||
if (gst_gl_display_get_handle_type (self->gst_display) == GST_GL_DISPLAY_TYPE_COCOA) {
|
||||
platform = GST_GL_PLATFORM_CGL;
|
||||
GST_INFO_OBJECT (self, "Using CGL on macOS");
|
||||
goto have_display;
|
||||
}
|
||||
#endif
|
||||
|
||||
g_clear_object (&self->gdk_context);
|
||||
gst_clear_object (&self->gst_display);
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
GST_ERROR_OBJECT (self, "Unsupported GL platform");
|
||||
return FALSE;
|
||||
|
||||
have_display:
|
||||
gdk_gl_context_make_current (self->gdk_context);
|
||||
|
||||
self->wrapped_context = _wrap_current_gl (self->gst_display, gdk_gl_api, platform);
|
||||
if (!self->wrapped_context) {
|
||||
GST_ERROR ("Could not retrieve Gdk OpenGL context");
|
||||
gdk_gl_context_clear_current ();
|
||||
|
||||
g_clear_object (&self->gdk_context);
|
||||
gst_clear_object (&self->gst_display);
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GST_INFO ("Retrieved Gdk OpenGL context %" GST_PTR_FORMAT, self->wrapped_context);
|
||||
gst_gl_context_activate (self->wrapped_context, TRUE);
|
||||
|
||||
if (!gst_gl_context_fill_info (self->wrapped_context, &error)) {
|
||||
GST_ERROR ("Failed to fill Gdk context info: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
|
||||
gst_gl_context_activate (self->wrapped_context, FALSE);
|
||||
|
||||
gst_clear_object (&self->wrapped_context);
|
||||
g_clear_object (&self->gdk_context);
|
||||
gst_clear_object (&self->gst_display);
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gst_gl_context_get_gl_version (self->wrapped_context, &gl_major, &gl_minor);
|
||||
GST_INFO ("Using OpenGL%s %i.%i", (gdk_gl_api == GDK_GL_API_GLES) ? " ES" : "",
|
||||
gl_major, gl_minor);
|
||||
|
||||
/* Deactivate in both places */
|
||||
gst_gl_context_activate (self->wrapped_context, FALSE);
|
||||
gdk_gl_context_clear_current ();
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_retrieve_gst_context (GstClapperGLContextHandler *self)
|
||||
{
|
||||
GstGLDisplay *gst_display = NULL;
|
||||
GstGLContext *gst_context = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
|
||||
gst_display = gst_object_ref (self->gst_display);
|
||||
GST_TRACE_OBJECT (self, "Creating new GstGLContext");
|
||||
|
||||
/* GstGLDisplay operations require display object lock to be held */
|
||||
GST_OBJECT_LOCK (gst_display);
|
||||
|
||||
if (!gst_gl_display_create_context (gst_display, self->wrapped_context,
|
||||
&self->gst_context, &error)) {
|
||||
GST_WARNING ("Could not create OpenGL context: %s",
|
||||
error ? error->message : "Unknown");
|
||||
g_clear_error (&error);
|
||||
|
||||
GST_OBJECT_UNLOCK (gst_display);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gst_context = gst_object_ref (self->gst_context);
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
gst_gl_display_add_context (gst_display, gst_context);
|
||||
|
||||
GST_OBJECT_UNLOCK (gst_display);
|
||||
|
||||
gst_object_unref (gst_display);
|
||||
gst_object_unref (gst_context);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_clapper_gl_context_handler_handle_context_query (GstClapperContextHandler *handler,
|
||||
GstBaseSink *bsink, GstQuery *query)
|
||||
{
|
||||
GstClapperGLContextHandler *self = GST_CLAPPER_GL_CONTEXT_HANDLER_CAST (handler);
|
||||
gboolean res;
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
res = gst_gl_handle_context_query (GST_ELEMENT_CAST (bsink), query,
|
||||
self->gst_display, self->gst_context, self->wrapped_context);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_context_handler_init (GstClapperGLContextHandler *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_context_handler_constructed (GObject *object)
|
||||
{
|
||||
GstClapperGLContextHandler *self = GST_CLAPPER_GL_CONTEXT_HANDLER_CAST (object);
|
||||
|
||||
if (! !gst_gtk_invoke_on_main ((GThreadFunc) (GCallback)
|
||||
_retrieve_gl_context_on_main, self)) {
|
||||
_retrieve_gst_context (self);
|
||||
}
|
||||
|
||||
GST_CALL_PARENT (G_OBJECT_CLASS, constructed, (object));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_context_handler_finalize (GObject *object)
|
||||
{
|
||||
GstClapperGLContextHandler *self = GST_CLAPPER_GL_CONTEXT_HANDLER_CAST (object);
|
||||
|
||||
GST_TRACE ("Finalize");
|
||||
|
||||
g_clear_object (&self->gdk_context);
|
||||
|
||||
gst_clear_object (&self->gst_display);
|
||||
gst_clear_object (&self->wrapped_context);
|
||||
gst_clear_object (&self->gst_context);
|
||||
|
||||
GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_context_handler_class_init (GstClapperGLContextHandlerClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
GstClapperContextHandlerClass *handler_class = (GstClapperContextHandlerClass *) klass;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperglcontexthandler", 0,
|
||||
"Clapper GL Context Handler");
|
||||
|
||||
gobject_class->constructed = gst_clapper_gl_context_handler_constructed;
|
||||
gobject_class->finalize = gst_clapper_gl_context_handler_finalize;
|
||||
|
||||
handler_class->handle_context_query = gst_clapper_gl_context_handler_handle_context_query;
|
||||
}
|
||||
|
||||
void
|
||||
gst_clapper_gl_context_handler_add_handler (GPtrArray *context_handlers)
|
||||
{
|
||||
guint i;
|
||||
gboolean found = FALSE;
|
||||
|
||||
for (i = 0; i < context_handlers->len; i++) {
|
||||
GstClapperContextHandler *handler = g_ptr_array_index (context_handlers, i);
|
||||
|
||||
if ((found = GST_IS_CLAPPER_GL_CONTEXT_HANDLER (handler)))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
GstClapperGLContextHandler *handler = g_object_new (
|
||||
GST_TYPE_CLAPPER_GL_CONTEXT_HANDLER, NULL);
|
||||
|
||||
g_ptr_array_add (context_handlers, handler);
|
||||
GST_DEBUG ("Added GL context handler to handlers array");
|
||||
}
|
||||
}
|
||||
|
||||
GstCaps *
|
||||
gst_clapper_gl_context_handler_make_gdk_gl_caps (const gchar *features, gboolean only_2d)
|
||||
{
|
||||
GstCaps *caps, *tmp;
|
||||
|
||||
if (only_2d) {
|
||||
tmp = gst_caps_from_string (GST_VIDEO_CAPS_MAKE (
|
||||
"{ " GST_GDK_GL_TEXTURE_FORMATS " }") ", "
|
||||
"texture-target = (string) { " GST_GL_TEXTURE_TARGET_2D_STR " }");
|
||||
} else {
|
||||
tmp = gst_caps_from_string (GST_VIDEO_CAPS_MAKE (
|
||||
"{ " GST_GDK_GL_TEXTURE_FORMATS " }"));
|
||||
}
|
||||
|
||||
caps = gst_caps_copy (tmp);
|
||||
|
||||
gst_caps_set_features_simple (tmp, gst_caps_features_new (
|
||||
features, NULL));
|
||||
gst_caps_set_features_simple (caps, gst_caps_features_new (
|
||||
features, GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION, NULL));
|
||||
|
||||
gst_caps_append (caps, tmp);
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
GdkTexture *
|
||||
gst_clapper_gl_context_handler_make_gl_texture (GstClapperGLContextHandler *self,
|
||||
GstBuffer *buffer, GstVideoInfo *v_info)
|
||||
{
|
||||
GdkTexture *texture;
|
||||
GstGLSyncMeta *sync_meta;
|
||||
GstVideoFrame frame;
|
||||
|
||||
if (G_UNLIKELY (!gst_video_frame_map (&frame, v_info, buffer, GST_MAP_READ | GST_MAP_GL))) {
|
||||
GST_ERROR_OBJECT (self, "Could not map input buffer for reading");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
|
||||
/* Must have context active here for both sync meta
|
||||
* and Gdk texture format auto-detection to work */
|
||||
gdk_gl_context_make_current (self->gdk_context);
|
||||
gst_gl_context_activate (self->wrapped_context, TRUE);
|
||||
|
||||
sync_meta = gst_buffer_get_gl_sync_meta (buffer);
|
||||
|
||||
/* Wait for all previous OpenGL commands to complete,
|
||||
* before we start using the input texture */
|
||||
if (sync_meta) {
|
||||
gst_gl_sync_meta_set_sync_point (sync_meta, self->gst_context);
|
||||
gst_gl_sync_meta_wait (sync_meta, self->wrapped_context);
|
||||
}
|
||||
|
||||
texture = gdk_gl_texture_new (
|
||||
self->gdk_context,
|
||||
*(guint *) GST_VIDEO_FRAME_PLANE_DATA (&frame, 0),
|
||||
GST_VIDEO_FRAME_WIDTH (&frame),
|
||||
GST_VIDEO_FRAME_HEIGHT (&frame),
|
||||
(GDestroyNotify) gst_buffer_unref,
|
||||
gst_buffer_ref (buffer));
|
||||
|
||||
gst_gl_context_activate (self->wrapped_context, FALSE);
|
||||
gdk_gl_context_clear_current ();
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
gst_video_frame_unmap (&frame);
|
||||
|
||||
return texture;
|
||||
}
|
64
lib/gst/plugin/handlers/gl/gstclapperglcontexthandler.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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 <gst/gst.h>
|
||||
#include <gst/video/video.h>
|
||||
#include <gst/gl/gl.h>
|
||||
#include <gst/gl/gstglfuncs.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "gst/plugin/gstclappercontexthandler.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_CLAPPER_GL_CONTEXT_HANDLER (gst_clapper_gl_context_handler_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstClapperGLContextHandler, gst_clapper_gl_context_handler, GST, CLAPPER_GL_CONTEXT_HANDLER, GstClapperContextHandler)
|
||||
|
||||
#define GST_CLAPPER_GL_CONTEXT_HANDLER_CAST(obj) ((GstClapperGLContextHandler *)(obj))
|
||||
|
||||
#define GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_WAYLAND (GST_GL_HAVE_WINDOW_WAYLAND && defined (GDK_WINDOWING_WAYLAND))
|
||||
#define GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_X11 (GST_GL_HAVE_WINDOW_X11 && defined (GDK_WINDOWING_X11))
|
||||
#define GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_X11_GLX (GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_X11 && GST_GL_HAVE_PLATFORM_GLX)
|
||||
#define GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_X11_EGL (GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_X11 && GST_GL_HAVE_PLATFORM_EGL)
|
||||
#define GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_WIN32 (GST_GL_HAVE_WINDOW_WIN32 && defined (GDK_WINDOWING_WIN32))
|
||||
#define GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_WIN32_WGL (GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_WIN32 && GST_GL_HAVE_PLATFORM_WGL)
|
||||
#define GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_WIN32_EGL (GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_WIN32 && GST_GL_HAVE_PLATFORM_EGL)
|
||||
#define GST_CLAPPER_GL_CONTEXT_HANDLER_HAVE_MACOS (GST_GL_HAVE_WINDOW_COCOA && defined (GDK_WINDOWING_MACOS) && GST_GL_HAVE_PLATFORM_CGL)
|
||||
|
||||
struct _GstClapperGLContextHandler
|
||||
{
|
||||
GstClapperContextHandler parent;
|
||||
|
||||
GdkGLContext *gdk_context;
|
||||
|
||||
GstGLDisplay *gst_display;
|
||||
GstGLContext *wrapped_context;
|
||||
GstGLContext *gst_context;
|
||||
};
|
||||
|
||||
void gst_clapper_gl_context_handler_add_handler (GPtrArray *context_handlers);
|
||||
|
||||
GstCaps * gst_clapper_gl_context_handler_make_gdk_gl_caps (const gchar *features, gboolean only_2d);
|
||||
|
||||
GdkTexture * gst_clapper_gl_context_handler_make_gl_texture (GstClapperGLContextHandler *handler, GstBuffer *buffer, GstVideoInfo *v_info);
|
||||
|
||||
G_END_DECLS
|
83
lib/gst/plugin/handlers/gl/meson.build
Normal file
@@ -0,0 +1,83 @@
|
||||
gst_clapper_gl_ch_dep = dependency('', required: false)
|
||||
|
||||
build_gl_ch = (
|
||||
not get_option('glimporter').disabled()
|
||||
or not get_option('gluploader').disabled()
|
||||
)
|
||||
gl_support_required = (
|
||||
get_option('glimporter').enabled()
|
||||
or get_option('gluploader').enabled()
|
||||
)
|
||||
|
||||
gst_plugin_gl_ch_deps = [gst_clapper_sink_dep, gstgl_dep, gstglproto_dep]
|
||||
have_gtk_gl_windowing = false
|
||||
|
||||
if gst_gl_have_window_x11 and (gst_gl_have_platform_egl or gst_gl_have_platform_glx)
|
||||
gtk_x11_dep = dependency('gtk4-x11', required: false)
|
||||
if gtk_x11_dep.found()
|
||||
gst_plugin_gl_ch_deps += gtk_x11_dep
|
||||
if gst_gl_have_platform_glx
|
||||
gst_plugin_gl_ch_deps += gstglx11_dep
|
||||
endif
|
||||
have_gtk_gl_windowing = true
|
||||
endif
|
||||
endif
|
||||
|
||||
if gst_gl_have_window_wayland and gst_gl_have_platform_egl
|
||||
gtk_wayland_dep = dependency('gtk4-wayland', required: false)
|
||||
if gtk_wayland_dep.found()
|
||||
gst_plugin_gl_ch_deps += [gtk_wayland_dep, gstglwayland_dep]
|
||||
have_gtk_gl_windowing = true
|
||||
endif
|
||||
endif
|
||||
|
||||
if gst_gl_have_window_win32 and (gst_gl_have_platform_egl or gst_gl_have_platform_wgl)
|
||||
gtk_win32_dep = dependency('gtk4-win32', required: false)
|
||||
if gtk_win32_dep.found()
|
||||
gst_plugin_gl_ch_deps += gtk_win32_dep
|
||||
have_gtk_gl_windowing = true
|
||||
endif
|
||||
endif
|
||||
|
||||
if gst_gl_have_window_cocoa and gst_gl_have_platform_cgl
|
||||
gtk_macos_dep = dependency('gtk4-macos', required: false)
|
||||
if gtk_macos_dep.found()
|
||||
gst_plugin_gl_ch_deps += gtk_macos_dep
|
||||
have_gtk_gl_windowing = true
|
||||
endif
|
||||
endif
|
||||
|
||||
if not have_gtk_gl_windowing
|
||||
if gl_support_required
|
||||
error('GL-based importer was enabled, but support for current GL windowing is missing')
|
||||
endif
|
||||
build_gl_ch = false
|
||||
endif
|
||||
|
||||
if gst_gl_have_platform_egl
|
||||
gst_plugin_gl_ch_deps += gstglegl_dep
|
||||
endif
|
||||
|
||||
foreach dep : gst_plugin_gl_ch_deps
|
||||
if not dep.found()
|
||||
if gl_support_required
|
||||
error('GL-based importer was enabled, but required dependencies were not found')
|
||||
endif
|
||||
build_gl_ch = false
|
||||
endif
|
||||
endforeach
|
||||
|
||||
if build_gl_ch
|
||||
gst_clapper_gl_ch_dep = declare_dependency(
|
||||
link_with: library('gstclapperglcontexthandler',
|
||||
'gstclapperglcontexthandler.c',
|
||||
c_args: gst_clapper_plugin_args,
|
||||
include_directories: configinc,
|
||||
dependencies: gst_plugin_gl_ch_deps,
|
||||
version: libversion,
|
||||
install: true,
|
||||
),
|
||||
include_directories: configinc,
|
||||
dependencies: gst_plugin_gl_ch_deps,
|
||||
)
|
||||
endif
|
1
lib/gst/plugin/handlers/meson.build
Normal file
@@ -0,0 +1 @@
|
||||
subdir('gl')
|
130
lib/gst/plugin/importers/gstclapperglimporter.c
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstclapperglimporter.h"
|
||||
|
||||
#define GST_CAT_DEFAULT gst_clapper_gl_importer_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
#define parent_class gst_clapper_gl_importer_parent_class
|
||||
GST_CLAPPER_IMPORTER_DEFINE (GstClapperGLImporter, gst_clapper_gl_importer, GST_TYPE_CLAPPER_IMPORTER);
|
||||
|
||||
static GstBufferPool *
|
||||
gst_clapper_gl_importer_create_pool (GstClapperImporter *importer, GstStructure **config)
|
||||
{
|
||||
GstClapperGLImporter *self = GST_CLAPPER_GL_IMPORTER_CAST (importer);
|
||||
GstBufferPool *pool;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Creating new GL buffer pool");
|
||||
|
||||
pool = gst_gl_buffer_pool_new (self->gl_handler->gst_context);
|
||||
*config = gst_buffer_pool_get_config (pool);
|
||||
|
||||
gst_buffer_pool_config_add_option (*config, GST_BUFFER_POOL_OPTION_VIDEO_META);
|
||||
gst_buffer_pool_config_add_option (*config, GST_BUFFER_POOL_OPTION_GL_SYNC_META);
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_importer_add_allocation_metas (GstClapperImporter *importer, GstQuery *query)
|
||||
{
|
||||
GstClapperGLImporter *self = GST_CLAPPER_GL_IMPORTER_CAST (importer);
|
||||
|
||||
/* We can support GL sync meta */
|
||||
if (self->gl_handler->gst_context->gl_vtable->FenceSync)
|
||||
gst_query_add_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, NULL);
|
||||
|
||||
/* Also add base importer class supported meta */
|
||||
GST_CLAPPER_IMPORTER_CLASS (parent_class)->add_allocation_metas (importer, query);
|
||||
}
|
||||
|
||||
static GdkTexture *
|
||||
gst_clapper_gl_importer_generate_texture (GstClapperImporter *importer,
|
||||
GstBuffer *buffer, GstVideoInfo *v_info)
|
||||
{
|
||||
GstClapperGLImporter *self = GST_CLAPPER_GL_IMPORTER_CAST (importer);
|
||||
|
||||
return gst_clapper_gl_context_handler_make_gl_texture (self->gl_handler, buffer, v_info);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_importer_init (GstClapperGLImporter *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_importer_finalize (GObject *object)
|
||||
{
|
||||
GstClapperGLImporter *self = GST_CLAPPER_GL_IMPORTER_CAST (object);
|
||||
|
||||
gst_clear_object (&self->gl_handler);
|
||||
|
||||
GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_importer_class_init (GstClapperGLImporterClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
GstClapperImporterClass *importer_class = (GstClapperImporterClass *) klass;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperglimporter", 0,
|
||||
"Clapper GL Importer");
|
||||
|
||||
gobject_class->finalize = gst_clapper_gl_importer_finalize;
|
||||
|
||||
importer_class->create_pool = gst_clapper_gl_importer_create_pool;
|
||||
importer_class->add_allocation_metas = gst_clapper_gl_importer_add_allocation_metas;
|
||||
importer_class->generate_texture = gst_clapper_gl_importer_generate_texture;
|
||||
}
|
||||
|
||||
GstClapperImporter *
|
||||
make_importer (GPtrArray *context_handlers)
|
||||
{
|
||||
GstClapperGLImporter *self;
|
||||
GstClapperContextHandler *handler;
|
||||
|
||||
handler = gst_clapper_context_handler_obtain_with_type (context_handlers,
|
||||
GST_TYPE_CLAPPER_GL_CONTEXT_HANDLER);
|
||||
|
||||
if (G_UNLIKELY (!handler))
|
||||
return NULL;
|
||||
|
||||
self = g_object_new (GST_TYPE_CLAPPER_GL_IMPORTER, NULL);
|
||||
self->gl_handler = GST_CLAPPER_GL_CONTEXT_HANDLER_CAST (handler);
|
||||
|
||||
return GST_CLAPPER_IMPORTER_CAST (self);
|
||||
}
|
||||
|
||||
GstCaps *
|
||||
make_caps (gboolean is_template, GstRank *rank, GPtrArray *context_handlers)
|
||||
{
|
||||
*rank = GST_RANK_SECONDARY;
|
||||
|
||||
if (!is_template && context_handlers)
|
||||
gst_clapper_gl_context_handler_add_handler (context_handlers);
|
||||
|
||||
return gst_clapper_gl_context_handler_make_gdk_gl_caps (
|
||||
GST_CAPS_FEATURE_MEMORY_GL_MEMORY, TRUE);
|
||||
}
|
39
lib/gst/plugin/importers/gstclapperglimporter.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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 "gst/plugin/gstclapperimporter.h"
|
||||
#include "gst/plugin/handlers/gl/gstclapperglcontexthandler.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_CLAPPER_GL_IMPORTER (gst_clapper_gl_importer_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstClapperGLImporter, gst_clapper_gl_importer, GST, CLAPPER_GL_IMPORTER, GstClapperImporter)
|
||||
|
||||
#define GST_CLAPPER_GL_IMPORTER_CAST(obj) ((GstClapperGLImporter *)(obj))
|
||||
|
||||
struct _GstClapperGLImporter
|
||||
{
|
||||
GstClapperImporter parent;
|
||||
|
||||
GstClapperGLContextHandler *gl_handler;
|
||||
};
|
||||
|
||||
G_END_DECLS
|
328
lib/gst/plugin/importers/gstclappergluploader.c
Normal file
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstclappergluploader.h"
|
||||
|
||||
#define GST_CAT_DEFAULT gst_clapper_gl_uploader_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
#define parent_class gst_clapper_gl_uploader_parent_class
|
||||
GST_CLAPPER_IMPORTER_DEFINE (GstClapperGLUploader, gst_clapper_gl_uploader, GST_TYPE_CLAPPER_IMPORTER);
|
||||
|
||||
static void
|
||||
_update_elements_caps_locked (GstClapperGLUploader *self, GstCaps *upload_sink_caps)
|
||||
{
|
||||
GstGLContext *gst_context;
|
||||
GstCaps *upload_src_caps, *color_sink_caps, *color_src_caps, *gdk_sink_caps;
|
||||
|
||||
gst_context = self->gl_handler->gst_context;
|
||||
|
||||
GST_INFO_OBJECT (self, "Input caps: %" GST_PTR_FORMAT, upload_sink_caps);
|
||||
|
||||
upload_src_caps = gst_gl_upload_transform_caps (self->upload, gst_context,
|
||||
GST_PAD_SINK, upload_sink_caps, NULL);
|
||||
upload_src_caps = gst_caps_fixate (upload_src_caps);
|
||||
|
||||
GST_INFO_OBJECT (self, "GLUpload caps: %" GST_PTR_FORMAT, upload_src_caps);
|
||||
gst_gl_upload_set_caps (self->upload, upload_sink_caps, upload_src_caps);
|
||||
|
||||
gdk_sink_caps = gst_clapper_gl_context_handler_make_gdk_gl_caps (
|
||||
GST_CAPS_FEATURE_MEMORY_GL_MEMORY, TRUE);
|
||||
color_sink_caps = gst_gl_color_convert_transform_caps (gst_context,
|
||||
GST_PAD_SRC, upload_src_caps, gdk_sink_caps);
|
||||
gst_caps_unref (gdk_sink_caps);
|
||||
|
||||
/* Second caps arg is transfer-full */
|
||||
color_src_caps = gst_gl_color_convert_fixate_caps (gst_context,
|
||||
GST_PAD_SINK, upload_src_caps, color_sink_caps);
|
||||
|
||||
GST_INFO_OBJECT (self, "GLColorConvert caps: %" GST_PTR_FORMAT, color_src_caps);
|
||||
gst_gl_color_convert_set_caps (self->color_convert, upload_src_caps, color_src_caps);
|
||||
|
||||
self->has_pending_v_info = gst_video_info_from_caps (&self->pending_v_info, color_src_caps);
|
||||
|
||||
gst_caps_unref (upload_src_caps);
|
||||
gst_caps_unref (color_src_caps);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_uploader_set_caps (GstClapperImporter *importer, GstCaps *caps)
|
||||
{
|
||||
GstClapperGLUploader *self = GST_CLAPPER_GL_UPLOADER_CAST (importer);
|
||||
|
||||
GST_CLAPPER_GL_UPLOADER_LOCK (self);
|
||||
_update_elements_caps_locked (self, caps);
|
||||
GST_CLAPPER_GL_UPLOADER_UNLOCK (self);
|
||||
}
|
||||
|
||||
static void
|
||||
_uploader_reconfigure_locked (GstClapperGLUploader *self)
|
||||
{
|
||||
GstCaps *in_caps = NULL;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Reconfiguring upload");
|
||||
|
||||
gst_gl_upload_get_caps (self->upload, &in_caps, NULL);
|
||||
|
||||
if (G_LIKELY (in_caps)) {
|
||||
_update_elements_caps_locked (self, in_caps);
|
||||
gst_caps_unref (in_caps);
|
||||
}
|
||||
}
|
||||
|
||||
static GstBuffer *
|
||||
_upload_perform_locked (GstClapperGLUploader *self, GstBuffer *buffer)
|
||||
{
|
||||
GstBuffer *upload_buf = NULL;
|
||||
GstGLUploadReturn ret;
|
||||
|
||||
ret = gst_gl_upload_perform_with_buffer (self->upload, buffer, &upload_buf);
|
||||
|
||||
if (G_UNLIKELY (ret != GST_GL_UPLOAD_DONE)) {
|
||||
switch (ret) {
|
||||
case GST_GL_UPLOAD_RECONFIGURE:
|
||||
_uploader_reconfigure_locked (self);
|
||||
/* Retry with the same buffer after reconfiguring */
|
||||
return _upload_perform_locked (self, buffer);
|
||||
default:
|
||||
GST_ERROR_OBJECT (self, "Could not upload input buffer, returned: %i", ret);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return upload_buf;
|
||||
}
|
||||
|
||||
static GstBufferPool *
|
||||
gst_clapper_gl_uploader_create_pool (GstClapperImporter *importer, GstStructure **config)
|
||||
{
|
||||
/* Since GLUpload API provides a ready to use propose_allocation method,
|
||||
* we will use it with our query in add_allocation_metas instead of
|
||||
* making pool here ourselves */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_uploader_add_allocation_metas (GstClapperImporter *importer, GstQuery *query)
|
||||
{
|
||||
GstClapperGLUploader *self = GST_CLAPPER_GL_UPLOADER_CAST (importer);
|
||||
GstGLUpload *upload;
|
||||
|
||||
GST_CLAPPER_GL_UPLOADER_LOCK (self);
|
||||
upload = gst_object_ref (self->upload);
|
||||
GST_CLAPPER_GL_UPLOADER_UNLOCK (self);
|
||||
|
||||
/* Add glupload supported meta */
|
||||
gst_gl_upload_propose_allocation (upload, NULL, query);
|
||||
gst_object_unref (upload);
|
||||
|
||||
/* We can support GL sync meta */
|
||||
if (self->gl_handler->gst_context->gl_vtable->FenceSync)
|
||||
gst_query_add_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, NULL);
|
||||
|
||||
/* Also add base importer class supported meta */
|
||||
GST_CLAPPER_IMPORTER_CLASS (parent_class)->add_allocation_metas (importer, query);
|
||||
}
|
||||
|
||||
static GdkTexture *
|
||||
gst_clapper_gl_uploader_generate_texture (GstClapperImporter *importer,
|
||||
GstBuffer *buffer, GstVideoInfo *v_info)
|
||||
{
|
||||
GstClapperGLUploader *self = GST_CLAPPER_GL_UPLOADER_CAST (importer);
|
||||
GstBuffer *upload_buf, *color_buf;
|
||||
GdkTexture *texture;
|
||||
|
||||
/* XXX: We both upload and perform color conversion here, thus we skip
|
||||
* upload for buffers that are not going to be shown and gain more free
|
||||
* CPU time to prepare the next one. Improves performance on weak HW. */
|
||||
|
||||
GST_LOG_OBJECT (self, "Uploading %" GST_PTR_FORMAT, buffer);
|
||||
|
||||
GST_CLAPPER_GL_UPLOADER_LOCK (self);
|
||||
|
||||
upload_buf = _upload_perform_locked (self, buffer);
|
||||
|
||||
if (G_UNLIKELY (!upload_buf)) {
|
||||
GST_ERROR_OBJECT (self, "Could not perform upload on input buffer");
|
||||
GST_CLAPPER_GL_UPLOADER_UNLOCK (self);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
GST_LOG_OBJECT (self, "Uploaded into %" GST_PTR_FORMAT, upload_buf);
|
||||
|
||||
color_buf = gst_gl_color_convert_perform (self->color_convert, upload_buf);
|
||||
gst_buffer_unref (upload_buf);
|
||||
|
||||
/* Use video info associated with converted buffer */
|
||||
if (self->has_pending_v_info) {
|
||||
self->v_info = self->pending_v_info;
|
||||
self->has_pending_v_info = FALSE;
|
||||
}
|
||||
|
||||
GST_CLAPPER_GL_UPLOADER_UNLOCK (self);
|
||||
|
||||
if (G_UNLIKELY (!color_buf)) {
|
||||
GST_ERROR_OBJECT (self, "Could not perform color conversion on input buffer");
|
||||
return NULL;
|
||||
}
|
||||
GST_LOG_OBJECT (self, "Color converted into %" GST_PTR_FORMAT, color_buf);
|
||||
|
||||
texture = gst_clapper_gl_context_handler_make_gl_texture (self->gl_handler, color_buf, &self->v_info);
|
||||
gst_buffer_unref (color_buf);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_uploader_init (GstClapperGLUploader *self)
|
||||
{
|
||||
g_mutex_init (&self->lock);
|
||||
|
||||
gst_video_info_init (&self->pending_v_info);
|
||||
gst_video_info_init (&self->v_info);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_uploader_finalize (GObject *object)
|
||||
{
|
||||
GstClapperGLUploader *self = GST_CLAPPER_GL_UPLOADER_CAST (object);
|
||||
|
||||
gst_clear_object (&self->upload);
|
||||
gst_clear_object (&self->color_convert);
|
||||
|
||||
gst_clear_object (&self->gl_handler);
|
||||
|
||||
g_mutex_clear (&self->lock);
|
||||
|
||||
GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_gl_uploader_class_init (GstClapperGLUploaderClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
GstClapperImporterClass *importer_class = (GstClapperImporterClass *) klass;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clappergluploader", 0,
|
||||
"Clapper GL Uploader");
|
||||
|
||||
gobject_class->finalize = gst_clapper_gl_uploader_finalize;
|
||||
|
||||
importer_class->set_caps = gst_clapper_gl_uploader_set_caps;
|
||||
importer_class->create_pool = gst_clapper_gl_uploader_create_pool;
|
||||
importer_class->add_allocation_metas = gst_clapper_gl_uploader_add_allocation_metas;
|
||||
importer_class->generate_texture = gst_clapper_gl_uploader_generate_texture;
|
||||
}
|
||||
|
||||
GstClapperImporter *
|
||||
make_importer (GPtrArray *context_handlers)
|
||||
{
|
||||
GstClapperGLUploader *self;
|
||||
GstClapperContextHandler *handler;
|
||||
|
||||
handler = gst_clapper_context_handler_obtain_with_type (context_handlers,
|
||||
GST_TYPE_CLAPPER_GL_CONTEXT_HANDLER);
|
||||
|
||||
if (G_UNLIKELY (!handler))
|
||||
return NULL;
|
||||
|
||||
self = g_object_new (GST_TYPE_CLAPPER_GL_UPLOADER, NULL);
|
||||
self->gl_handler = GST_CLAPPER_GL_CONTEXT_HANDLER_CAST (handler);
|
||||
self->upload = gst_gl_upload_new (self->gl_handler->gst_context);
|
||||
self->color_convert = gst_gl_color_convert_new (self->gl_handler->gst_context);
|
||||
|
||||
return GST_CLAPPER_IMPORTER_CAST (self);
|
||||
}
|
||||
|
||||
static GstCaps *
|
||||
_make_actual_caps (GstClapperGLContextHandler *gl_handler)
|
||||
{
|
||||
GstGLUpload *upload;
|
||||
GstCaps *gdk_sink_caps, *color_sink_caps, *upload_sink_caps, *actual;
|
||||
guint i;
|
||||
|
||||
/* Having "gst_context" means we also have all other contexts and
|
||||
* display as they are used to create it, so no need to check */
|
||||
if (!gl_handler->gst_context)
|
||||
return NULL;
|
||||
|
||||
gdk_sink_caps = gst_clapper_gl_context_handler_make_gdk_gl_caps (
|
||||
GST_CAPS_FEATURE_MEMORY_GL_MEMORY, TRUE);
|
||||
|
||||
color_sink_caps = gst_gl_color_convert_transform_caps (gl_handler->gst_context,
|
||||
GST_PAD_SRC, gdk_sink_caps, NULL);
|
||||
gst_caps_unref (gdk_sink_caps);
|
||||
|
||||
upload = gst_gl_upload_new (NULL);
|
||||
|
||||
upload_sink_caps = gst_gl_upload_transform_caps (upload, gl_handler->gst_context,
|
||||
GST_PAD_SRC, color_sink_caps, NULL);
|
||||
gst_caps_unref (color_sink_caps);
|
||||
|
||||
gst_object_unref (upload);
|
||||
|
||||
/* Check for existence and remove duplicated structures,
|
||||
* they may contain unsupported by our GL context formats */
|
||||
actual = gst_caps_new_empty ();
|
||||
for (i = 0; i < gst_caps_get_size (upload_sink_caps); i++) {
|
||||
GstCaps *tmp = gst_caps_copy_nth (upload_sink_caps, i);
|
||||
|
||||
if (!gst_caps_can_intersect (actual, tmp))
|
||||
gst_caps_append (actual, tmp);
|
||||
else
|
||||
gst_caps_unref (tmp);
|
||||
}
|
||||
gst_caps_unref (upload_sink_caps);
|
||||
|
||||
if (G_UNLIKELY (gst_caps_is_empty (actual)))
|
||||
gst_clear_caps (&actual);
|
||||
|
||||
return actual;
|
||||
}
|
||||
|
||||
GstCaps *
|
||||
make_caps (gboolean is_template, GstRank *rank, GPtrArray *context_handlers)
|
||||
{
|
||||
GstCaps *caps = NULL;
|
||||
|
||||
if (is_template) {
|
||||
caps = gst_gl_upload_get_input_template_caps ();
|
||||
} else if (context_handlers) {
|
||||
GstClapperGLContextHandler *gl_handler;
|
||||
|
||||
/* Add GL context handler if not already present */
|
||||
gst_clapper_gl_context_handler_add_handler (context_handlers);
|
||||
|
||||
if ((gl_handler = GST_CLAPPER_GL_CONTEXT_HANDLER_CAST (
|
||||
gst_clapper_context_handler_obtain_with_type (context_handlers,
|
||||
GST_TYPE_CLAPPER_GL_CONTEXT_HANDLER)))) {
|
||||
caps = _make_actual_caps (gl_handler);
|
||||
gst_object_unref (gl_handler);
|
||||
}
|
||||
}
|
||||
|
||||
if (caps)
|
||||
*rank = GST_RANK_MARGINAL + 1;
|
||||
|
||||
return caps;
|
||||
}
|
51
lib/gst/plugin/importers/gstclappergluploader.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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 "gst/plugin/gstclapperimporter.h"
|
||||
#include "gst/plugin/handlers/gl/gstclapperglcontexthandler.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_CLAPPER_GL_UPLOADER (gst_clapper_gl_uploader_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstClapperGLUploader, gst_clapper_gl_uploader, GST, CLAPPER_GL_UPLOADER, GstClapperImporter)
|
||||
|
||||
#define GST_CLAPPER_GL_UPLOADER_CAST(obj) ((GstClapperGLUploader *)(obj))
|
||||
|
||||
#define GST_CLAPPER_GL_UPLOADER_GET_LOCK(obj) (&GST_CLAPPER_GL_UPLOADER_CAST(obj)->lock)
|
||||
#define GST_CLAPPER_GL_UPLOADER_LOCK(obj) g_mutex_lock (GST_CLAPPER_GL_UPLOADER_GET_LOCK(obj))
|
||||
#define GST_CLAPPER_GL_UPLOADER_UNLOCK(obj) g_mutex_unlock (GST_CLAPPER_GL_UPLOADER_GET_LOCK(obj))
|
||||
|
||||
struct _GstClapperGLUploader
|
||||
{
|
||||
GstClapperImporter parent;
|
||||
|
||||
GMutex lock;
|
||||
|
||||
GstClapperGLContextHandler *gl_handler;
|
||||
|
||||
GstGLUpload *upload;
|
||||
GstGLColorConvert *color_convert;
|
||||
|
||||
GstVideoInfo pending_v_info, v_info;
|
||||
gboolean has_pending_v_info;
|
||||
};
|
||||
|
||||
G_END_DECLS
|
103
lib/gst/plugin/importers/gstclapperrawimporter.c
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstclapperrawimporter.h"
|
||||
#include "gst/plugin/gstgtkutils.h"
|
||||
#include "gst/plugin/gstgdkformats.h"
|
||||
|
||||
#define GST_CAT_DEFAULT gst_clapper_raw_importer_debug
|
||||
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
||||
|
||||
#define parent_class gst_clapper_raw_importer_parent_class
|
||||
GST_CLAPPER_IMPORTER_DEFINE (GstClapperRawImporter, gst_clapper_raw_importer, GST_TYPE_CLAPPER_IMPORTER);
|
||||
|
||||
static GstBufferPool *
|
||||
gst_clapper_raw_importer_create_pool (GstClapperImporter *importer, GstStructure **config)
|
||||
{
|
||||
GstClapperRawImporter *self = GST_CLAPPER_RAW_IMPORTER_CAST (importer);
|
||||
GstBufferPool *pool;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Creating new buffer pool");
|
||||
|
||||
pool = gst_video_buffer_pool_new ();
|
||||
*config = gst_buffer_pool_get_config (pool);
|
||||
|
||||
gst_buffer_pool_config_add_option (*config, GST_BUFFER_POOL_OPTION_VIDEO_META);
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
static GdkTexture *
|
||||
gst_clapper_raw_importer_generate_texture (GstClapperImporter *importer,
|
||||
GstBuffer *buffer, GstVideoInfo *v_info)
|
||||
{
|
||||
GdkTexture *texture;
|
||||
GstVideoFrame frame;
|
||||
|
||||
if (G_UNLIKELY (!gst_video_frame_map (&frame, v_info, buffer, GST_MAP_READ))) {
|
||||
GST_ERROR_OBJECT (importer, "Could not map input buffer for reading");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
texture = gst_video_frame_into_gdk_texture (&frame);
|
||||
gst_video_frame_unmap (&frame);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_raw_importer_init (GstClapperRawImporter *self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gst_clapper_raw_importer_class_init (GstClapperRawImporterClass *klass)
|
||||
{
|
||||
GstClapperImporterClass *importer_class = (GstClapperImporterClass *) klass;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperrawimporter", 0,
|
||||
"Clapper RAW Importer");
|
||||
|
||||
importer_class->create_pool = gst_clapper_raw_importer_create_pool;
|
||||
importer_class->generate_texture = gst_clapper_raw_importer_generate_texture;
|
||||
}
|
||||
|
||||
GstClapperImporter *
|
||||
make_importer (GPtrArray *context_handlers)
|
||||
{
|
||||
return g_object_new (GST_TYPE_CLAPPER_RAW_IMPORTER, NULL);
|
||||
}
|
||||
|
||||
GstCaps *
|
||||
make_caps (gboolean is_template, GstRank *rank, GPtrArray *context_handlers)
|
||||
{
|
||||
*rank = GST_RANK_MARGINAL;
|
||||
|
||||
return gst_caps_from_string (
|
||||
GST_VIDEO_CAPS_MAKE_WITH_FEATURES (GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY ", "
|
||||
GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION,
|
||||
"{ " GST_GDK_MEMORY_FORMATS " }")
|
||||
"; "
|
||||
GST_VIDEO_CAPS_MAKE (
|
||||
"{ " GST_GDK_MEMORY_FORMATS " }"));
|
||||
}
|
36
lib/gst/plugin/importers/gstclapperrawimporter.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library 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 "gst/plugin/gstclapperimporter.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_CLAPPER_RAW_IMPORTER (gst_clapper_raw_importer_get_type())
|
||||
G_DECLARE_FINAL_TYPE (GstClapperRawImporter, gst_clapper_raw_importer, GST, CLAPPER_RAW_IMPORTER, GstClapperImporter)
|
||||
|
||||
#define GST_CLAPPER_RAW_IMPORTER_CAST(obj) ((GstClapperRawImporter *)(obj))
|
||||
|
||||
struct _GstClapperRawImporter
|
||||
{
|
||||
GstClapperImporter parent;
|
||||
};
|
||||
|
||||
G_END_DECLS
|
67
lib/gst/plugin/importers/meson.build
Normal file
@@ -0,0 +1,67 @@
|
||||
all_importers = [
|
||||
'glimporter',
|
||||
'gluploader',
|
||||
'rawimporter',
|
||||
]
|
||||
|
||||
# We cannot build any importers without sink that they depend on
|
||||
if not gst_clapper_sink_dep.found()
|
||||
foreach imp : all_importers
|
||||
if get_option(imp).enabled()
|
||||
error('"@0@" option was enabled, but it requires building gstreamer plugin'.format(imp))
|
||||
endif
|
||||
endforeach
|
||||
endif
|
||||
|
||||
build_glimporter = (
|
||||
not get_option('glimporter').disabled()
|
||||
and gst_clapper_gl_ch_dep.found()
|
||||
)
|
||||
|
||||
if build_glimporter
|
||||
library(
|
||||
'gstclapperglimporter',
|
||||
'gstclapperglimporter.c',
|
||||
dependencies: gst_clapper_gl_ch_dep,
|
||||
include_directories: configinc,
|
||||
c_args: gst_clapper_plugin_args,
|
||||
install: true,
|
||||
install_dir: gst_clapper_importers_libdir,
|
||||
)
|
||||
endif
|
||||
|
||||
build_gluploader = (
|
||||
not get_option('gluploader').disabled()
|
||||
and gst_clapper_gl_ch_dep.found()
|
||||
)
|
||||
|
||||
if build_gluploader
|
||||
library(
|
||||
'gstclappergluploader',
|
||||
'gstclappergluploader.c',
|
||||
dependencies: gst_clapper_gl_ch_dep,
|
||||
include_directories: configinc,
|
||||
c_args: gst_clapper_plugin_args,
|
||||
install: true,
|
||||
install_dir: gst_clapper_importers_libdir,
|
||||
)
|
||||
endif
|
||||
|
||||
# No need to auto build rawimporter if we are building gluploader
|
||||
build_rawimporter = (
|
||||
not get_option('rawimporter').disabled()
|
||||
and (not build_gluploader or get_option('rawimporter').enabled())
|
||||
and gst_clapper_sink_dep.found()
|
||||
)
|
||||
|
||||
if build_rawimporter
|
||||
library(
|
||||
'gstclapperrawimporter',
|
||||
'gstclapperrawimporter.c',
|
||||
dependencies: gst_clapper_sink_dep,
|
||||
include_directories: configinc,
|
||||
c_args: gst_clapper_plugin_args,
|
||||
install: true,
|
||||
install_dir: gst_clapper_importers_libdir,
|
||||
)
|
||||
endif
|
65
lib/gst/plugin/meson.build
Normal file
@@ -0,0 +1,65 @@
|
||||
gst_plugins_libdir = join_paths(prefix, libdir, 'gstreamer-1.0')
|
||||
|
||||
gst_clapper_plugin_args = [
|
||||
'-DHAVE_CONFIG_H',
|
||||
'-DGST_USE_UNSTABLE_API',
|
||||
]
|
||||
|
||||
gst_clapper_sink_dep = dependency('', required: false)
|
||||
gtk4_dep = dependency('gtk4', version: '>=4.6.0', required: false)
|
||||
|
||||
gmodule_dep = dependency('gmodule-2.0',
|
||||
version: glib_req,
|
||||
required: false,
|
||||
fallback: ['glib', 'libgmodule_dep'],
|
||||
)
|
||||
|
||||
gst_clapper_plugin_deps = [
|
||||
gtk4_dep,
|
||||
gst_dep,
|
||||
gstbase_dep,
|
||||
gstvideo_dep,
|
||||
gmodule_dep,
|
||||
]
|
||||
|
||||
build_gst_plugin = not get_option('gst-plugin').disabled()
|
||||
foreach dep : gst_clapper_plugin_deps
|
||||
if not dep.found()
|
||||
if get_option('gst-plugin').enabled()
|
||||
error('GStreamer plugin was enabled, but required dependencies were not found')
|
||||
endif
|
||||
build_gst_plugin = false
|
||||
endif
|
||||
endforeach
|
||||
|
||||
if get_option('default_library') == 'static'
|
||||
gst_clapper_plugin_args += ['-DGST_STATIC_COMPILATION']
|
||||
endif
|
||||
|
||||
gst_clapper_plugin_sources = [
|
||||
'gstclappersink.c',
|
||||
'gstclapperpaintable.c',
|
||||
'gstgtkutils.c',
|
||||
'gstplugin.c',
|
||||
'gstclappercontexthandler.c',
|
||||
'gstclapperimporter.c',
|
||||
'gstclapperimporterloader.c',
|
||||
]
|
||||
|
||||
if build_gst_plugin
|
||||
gst_clapper_sink_dep = declare_dependency(
|
||||
link_with: library('gstclapper',
|
||||
gst_clapper_plugin_sources,
|
||||
c_args: gst_clapper_plugin_args,
|
||||
include_directories: configinc,
|
||||
dependencies: gst_clapper_plugin_deps,
|
||||
install: true,
|
||||
install_dir: gst_plugins_libdir,
|
||||
),
|
||||
include_directories: configinc,
|
||||
dependencies: gst_clapper_plugin_deps,
|
||||
)
|
||||
endif
|
||||
|
||||
subdir('handlers')
|
||||
subdir('importers')
|
106
lib/meson.build
@@ -1,5 +1,5 @@
|
||||
glib_req = '>= 2.56.0'
|
||||
gst_req = '>= 1.18.0'
|
||||
glib_req = '>= 2.68.0'
|
||||
gst_req = '>= 1.20.0'
|
||||
|
||||
api_version = '1.0'
|
||||
libversion = meson.project_version()
|
||||
@@ -7,12 +7,10 @@ libversion = meson.project_version()
|
||||
cc = meson.get_compiler('c')
|
||||
cxx = meson.get_compiler('cpp')
|
||||
|
||||
clapper_libdir = join_paths(
|
||||
get_option('prefix'), get_option('libdir'), meson.project_name()
|
||||
)
|
||||
|
||||
cdata = configuration_data()
|
||||
|
||||
os_unix = host_machine.system() != 'windows'
|
||||
|
||||
if cc.get_id() == 'msvc'
|
||||
msvc_args = [
|
||||
# Ignore several spurious warnings for things gstreamer does very commonly
|
||||
@@ -40,16 +38,12 @@ else
|
||||
endif
|
||||
|
||||
if cc.has_link_argument('-Wl,-Bsymbolic-functions')
|
||||
add_project_link_arguments('-Wl,-Bsymbolic-functions', language : 'c')
|
||||
add_project_link_arguments('-Wl,-Bsymbolic-functions', language: 'c')
|
||||
endif
|
||||
|
||||
# Symbol visibility
|
||||
if cc.get_id() == 'msvc'
|
||||
export_define = '__declspec(dllexport) extern'
|
||||
elif cc.has_argument('-fvisibility=hidden')
|
||||
add_project_arguments('-fvisibility=hidden', language: 'c')
|
||||
add_project_arguments('-fvisibility=hidden', language: 'cpp')
|
||||
export_define = 'extern __attribute__ ((visibility ("default")))'
|
||||
else
|
||||
export_define = 'extern'
|
||||
endif
|
||||
@@ -136,13 +130,13 @@ cdata.set('SIZEOF_SHORT', cc.sizeof('short'))
|
||||
cdata.set('SIZEOF_VOIDP', cc.sizeof('void*'))
|
||||
|
||||
cdata.set_quoted('VERSION', libversion)
|
||||
cdata.set_quoted('PACKAGE', 'gst-plugins-clapper')
|
||||
cdata.set_quoted('PACKAGE', 'clapper')
|
||||
cdata.set_quoted('PACKAGE_VERSION', libversion)
|
||||
cdata.set_quoted('PACKAGE_BUGREPORT', 'https://github.com/Rafostar/clapper/issues/new')
|
||||
cdata.set_quoted('PACKAGE_NAME', 'GStreamer Clapper Libs')
|
||||
cdata.set_quoted('GST_API_VERSION', api_version)
|
||||
cdata.set_quoted('GST_LICENSE', 'LGPL')
|
||||
cdata.set_quoted('LIBDIR', clapper_libdir)
|
||||
cdata.set_quoted('LIBDIR', pkglibdir)
|
||||
cdata.set_quoted('LOCALEDIR', join_paths(get_option('prefix'), get_option('localedir')))
|
||||
|
||||
warning_flags = [
|
||||
@@ -188,29 +182,29 @@ foreach extra_arg : warning_flags
|
||||
endif
|
||||
endforeach
|
||||
|
||||
cdata.set_quoted('GST_PACKAGE_NAME', 'GStreamer Plugins Clapper')
|
||||
cdata.set_quoted('GST_PACKAGE_NAME', 'gst-plugin-clapper')
|
||||
cdata.set_quoted('GST_PACKAGE_ORIGIN', 'https://github.com/Rafostar/clapper')
|
||||
|
||||
# Mandatory GST deps
|
||||
gst_dep = dependency('gstreamer-1.0', version : gst_req,
|
||||
fallback : ['gstreamer', 'gst_dep'])
|
||||
gstbase_dep = dependency('gstreamer-base-1.0', version : gst_req,
|
||||
fallback : ['gstreamer', 'gst_base_dep'])
|
||||
gstpbutils_dep = dependency('gstreamer-pbutils-1.0', version : gst_req,
|
||||
fallback : ['gst-plugins-base', 'pbutils_dep'])
|
||||
gstaudio_dep = dependency('gstreamer-audio-1.0', version : gst_req,
|
||||
fallback : ['gst-plugins-base', 'audio_dep'])
|
||||
gsttag_dep = dependency('gstreamer-tag-1.0', version : gst_req,
|
||||
fallback : ['gst-plugins-base', 'tag_dep'])
|
||||
gstvideo_dep = dependency('gstreamer-video-1.0', version : gst_req,
|
||||
fallback : ['gst-plugins-base', 'video_dep'])
|
||||
gst_dep = dependency('gstreamer-1.0', version: gst_req,
|
||||
fallback: ['gstreamer', 'gst_dep'])
|
||||
gstbase_dep = dependency('gstreamer-base-1.0', version: gst_req,
|
||||
fallback: ['gstreamer', 'gst_base_dep'])
|
||||
gstpbutils_dep = dependency('gstreamer-pbutils-1.0', version: gst_req,
|
||||
fallback: ['gst-plugins-base', 'pbutils_dep'])
|
||||
gstaudio_dep = dependency('gstreamer-audio-1.0', version: gst_req,
|
||||
fallback: ['gst-plugins-base', 'audio_dep'])
|
||||
gsttag_dep = dependency('gstreamer-tag-1.0', version: gst_req,
|
||||
fallback: ['gst-plugins-base', 'tag_dep'])
|
||||
gstvideo_dep = dependency('gstreamer-video-1.0', version: gst_req,
|
||||
fallback: ['gst-plugins-base', 'video_dep'])
|
||||
|
||||
# GStreamer OpenGL
|
||||
gstgl_dep = dependency('gstreamer-gl-1.0', version : gst_req,
|
||||
fallback : ['gst-plugins-base', 'gstgl_dep'], required: true)
|
||||
gstglx11_dep = dependency('', required : false)
|
||||
gstglwayland_dep = dependency('', required : false)
|
||||
gstglegl_dep = dependency('', required : false)
|
||||
gstgl_dep = dependency('gstreamer-gl-1.0', version: gst_req,
|
||||
fallback: ['gst-plugins-base', 'gstgl_dep'], required: true)
|
||||
gstglx11_dep = dependency('', required: false)
|
||||
gstglwayland_dep = dependency('', required: false)
|
||||
gstglegl_dep = dependency('', required: false)
|
||||
|
||||
gst_gl_apis = gstgl_dep.get_pkgconfig_variable('gl_apis')
|
||||
gst_gl_winsys = gstgl_dep.get_pkgconfig_variable('gl_winsys')
|
||||
@@ -232,53 +226,49 @@ foreach api : ['gl', 'gles2']
|
||||
set_variable('gst_gl_have_api_@0@'.format(api), gst_gl_apis.contains(api))
|
||||
endforeach
|
||||
|
||||
gstglproto_dep = dependency('gstreamer-gl-prototypes-1.0', version : gst_req,
|
||||
fallback : ['gst-plugins-base', 'gstglproto_dep'], required: true)
|
||||
gstglproto_dep = dependency('gstreamer-gl-prototypes-1.0', version: gst_req,
|
||||
fallback: ['gst-plugins-base', 'gstglproto_dep'], required: true)
|
||||
if gst_gl_have_window_x11
|
||||
gstglx11_dep = dependency('gstreamer-gl-x11-1.0', version : gst_req,
|
||||
fallback : ['gst-plugins-base', 'gstglx11_dep'], required: true)
|
||||
gstglx11_dep = dependency('gstreamer-gl-x11-1.0', version: gst_req,
|
||||
fallback: ['gst-plugins-base', 'gstglx11_dep'], required: true)
|
||||
endif
|
||||
if gst_gl_have_window_wayland
|
||||
gstglwayland_dep = dependency('gstreamer-gl-wayland-1.0', version : gst_req,
|
||||
fallback : ['gst-plugins-base', 'gstglwayland_dep'], required: true)
|
||||
gstglwayland_dep = dependency('gstreamer-gl-wayland-1.0', version: gst_req,
|
||||
fallback: ['gst-plugins-base', 'gstglwayland_dep'], required: true)
|
||||
endif
|
||||
if gst_gl_have_platform_egl
|
||||
gstglegl_dep = dependency('gstreamer-gl-egl-1.0', version : gst_req,
|
||||
fallback : ['gst-plugins-base', 'gstglegl_dep'], required: true)
|
||||
gstglegl_dep = dependency('gstreamer-gl-egl-1.0', version: gst_req,
|
||||
fallback: ['gst-plugins-base', 'gstglegl_dep'], required: true)
|
||||
endif
|
||||
|
||||
libm = cc.find_library('m', required : false)
|
||||
glib_dep = dependency('glib-2.0', version : glib_req, fallback: ['glib', 'libglib_dep'])
|
||||
libm = cc.find_library('m', required: false)
|
||||
glib_dep = dependency('glib-2.0', version: glib_req, fallback: ['glib', 'libglib_dep'])
|
||||
gmodule_dep = dependency('gmodule-2.0', fallback: ['glib', 'libgmodule_dep'])
|
||||
gio_dep = dependency('gio-2.0', fallback: ['glib', 'libgio_dep'])
|
||||
|
||||
if os_unix
|
||||
giounix_dep = dependency('gio-unix-2.0', version: glib_req, fallback: ['glib', 'libgio_dep'])
|
||||
else
|
||||
giowin_dep = dependency('gio-windows-2.0', version: glib_req, fallback : ['glib', 'libgio_dep'])
|
||||
endif
|
||||
|
||||
cdata.set('DISABLE_ORC', 1)
|
||||
cdata.set('GST_ENABLE_EXTRA_CHECKS', get_option('devel-checks'))
|
||||
cdata.set_quoted('GST_PACKAGE_RELEASE_DATETIME', 'Unknown')
|
||||
|
||||
message('GStreamer debug system is disabled')
|
||||
if cc.has_argument('-Wno-unused')
|
||||
add_project_arguments('-Wno-unused', language: 'c')
|
||||
endif
|
||||
if cxx.has_argument ('-Wno-unused')
|
||||
add_project_arguments('-Wno-unused', language: 'cpp')
|
||||
endif
|
||||
|
||||
configinc = include_directories('.')
|
||||
libsinc = include_directories('gst')
|
||||
|
||||
gnome = import('gnome')
|
||||
gir = find_program('g-ir-scanner', required : true)
|
||||
|
||||
if not gir.found()
|
||||
error('Clapper requires GI bindings to be compiled')
|
||||
endif
|
||||
|
||||
gir = find_program('g-ir-scanner', required: false)
|
||||
gir_init_section = ['--add-init-section=extern void gst_init(gint*,gchar**);' + \
|
||||
'g_setenv("GST_REGISTRY_1.0", "@0@", TRUE);'.format(meson.current_build_dir() + '/gir_empty_registry.reg') + \
|
||||
'g_setenv("GST_PLUGIN_PATH_1_0", "", TRUE);' + \
|
||||
'g_setenv("GST_PLUGIN_SYSTEM_PATH_1_0", "", TRUE);' + \
|
||||
'gst_init(NULL,NULL);', '--quiet'
|
||||
]
|
||||
|
||||
gst_clapper_plugin_libdir = join_paths(get_option('prefix'), libdir, 'clapper-@0@'.format(api_version), 'gst', 'plugin')
|
||||
gst_clapper_importers_libdir = join_paths(gst_clapper_plugin_libdir, 'importers')
|
||||
cdata.set_quoted('CLAPPER_SINK_IMPORTER_PATH', gst_clapper_importers_libdir)
|
||||
|
||||
subdir('gst')
|
||||
configure_file(output : 'config.h', configuration : cdata)
|
||||
configure_file(output: 'config.h', configuration: cdata)
|
||||
|
36
meson.build
@@ -1,33 +1,39 @@
|
||||
project('com.github.rafostar.Clapper', 'c', 'cpp',
|
||||
version: '0.2.1',
|
||||
version: '0.5.2',
|
||||
meson_version: '>= 0.50.0',
|
||||
license: 'GPL3',
|
||||
license: 'GPL-3.0-or-later',
|
||||
default_options: [
|
||||
'warning_level=1',
|
||||
'buildtype=debugoptimized'
|
||||
]
|
||||
)
|
||||
|
||||
gnome = import('gnome')
|
||||
i18n = import('i18n')
|
||||
python = import('python')
|
||||
python_bin = python.find_installation('python3')
|
||||
|
||||
if not python_bin.found()
|
||||
error('No valid python3 binary found')
|
||||
endif
|
||||
bindir = join_paths(get_option('prefix'), get_option('bindir'))
|
||||
libdir = join_paths(get_option('prefix'), get_option('libdir'))
|
||||
datadir = join_paths(get_option('prefix'), get_option('datadir'))
|
||||
|
||||
if get_option('clapper-lib')
|
||||
subdir('lib')
|
||||
endif
|
||||
pkglibdir = join_paths(libdir, meson.project_name())
|
||||
pkgdatadir = join_paths(datadir, meson.project_name())
|
||||
|
||||
if get_option('clapper-player')
|
||||
subdir('lib')
|
||||
|
||||
if get_option('player')
|
||||
subdir('bin')
|
||||
subdir('data')
|
||||
subdir('po')
|
||||
|
||||
installdir = join_paths(get_option('prefix'), 'share', meson.project_name())
|
||||
install_subdir('src', install_dir : installdir)
|
||||
install_subdir('extras', install_dir : installdir)
|
||||
install_subdir('css', install_dir : installdir)
|
||||
install_subdir('ui', install_dir : installdir)
|
||||
install_subdir('src', install_dir: pkgdatadir)
|
||||
install_subdir('extras', install_dir: pkgdatadir)
|
||||
install_subdir('css', install_dir: pkgdatadir)
|
||||
install_subdir('ui', install_dir: pkgdatadir)
|
||||
|
||||
python_bin = python.find_installation('python3')
|
||||
if not python_bin.found()
|
||||
error('No valid python3 binary found')
|
||||
endif
|
||||
meson.add_install_script('build-aux/meson/postinstall.py')
|
||||
endif
|
||||
|
@@ -1,20 +1,42 @@
|
||||
option('clapper-player',
|
||||
type : 'boolean',
|
||||
value : true,
|
||||
option('player',
|
||||
type: 'boolean',
|
||||
value: true,
|
||||
description: 'Build Clapper player'
|
||||
)
|
||||
option('clapper-lib',
|
||||
type : 'boolean',
|
||||
value : true,
|
||||
description: 'Build Clapper libs (including API)'
|
||||
option('lib',
|
||||
type: 'boolean',
|
||||
value: true,
|
||||
description: 'Build GstClapper lib'
|
||||
)
|
||||
option('gst-plugin',
|
||||
type: 'feature',
|
||||
value: 'auto',
|
||||
description: 'Build GStreamer plugin (includes GTK video sink element)'
|
||||
)
|
||||
|
||||
option('glimporter',
|
||||
type: 'feature',
|
||||
value: 'auto',
|
||||
description: 'Build GL memory importer for clappersink'
|
||||
)
|
||||
option('gluploader',
|
||||
type: 'feature',
|
||||
value: 'auto',
|
||||
description: 'Build GL uploader for clappersink'
|
||||
)
|
||||
option('rawimporter',
|
||||
type: 'feature',
|
||||
value: 'auto',
|
||||
description: 'Build RAW system memory importer for clappersink'
|
||||
)
|
||||
|
||||
option('devel-checks',
|
||||
type : 'boolean',
|
||||
value : false,
|
||||
type: 'boolean',
|
||||
value: false,
|
||||
description: 'GStreamer GLib checks and asserts such as API guards (disable for stable releases)'
|
||||
)
|
||||
option('deprecated-glib-api',
|
||||
type : 'boolean',
|
||||
value : true,
|
||||
type: 'boolean',
|
||||
value: true,
|
||||
description: 'Allow using of deprecated GLib API'
|
||||
)
|
||||
|
@@ -1,29 +0,0 @@
|
||||
pkgbase = clapper-git
|
||||
pkgdesc = A GNOME media player built using GJS with GTK4 toolkit and powered by GStreamer with OpenGL rendering.
|
||||
pkgver = r393.bf04af2
|
||||
pkgrel = 1
|
||||
url = https://github.com/Rafostar/clapper
|
||||
arch = any
|
||||
license = GPL-3.0
|
||||
makedepends = meson>=0.50
|
||||
makedepends = git
|
||||
depends = gtk4
|
||||
depends = gjs
|
||||
depends = glib2>=2.56.0
|
||||
depends = gobject-introspection
|
||||
depends = wayland-protocols
|
||||
depends = hicolor-icon-theme
|
||||
depends = gstreamer>=1.18.0
|
||||
depends = gst-plugins-base-libs>=1.18.0
|
||||
depends = gst-plugins-good>=1.18.0
|
||||
depends = gst-plugins-bad-libs>=1.18.0
|
||||
optdepends = gst-libav>=1.18.0: Popular video decoders
|
||||
optdepends = gstreamer-vaapi>=1.18.0: Intel/AMD video acceleration
|
||||
provides = clapper
|
||||
conflicts = clapper
|
||||
replaces = clapper
|
||||
source = clapper::git+https://github.com/Rafostar/clapper.git
|
||||
md5sums = SKIP
|
||||
|
||||
pkgname = clapper-git
|
||||
|
4
pkgs/arch/.gitignore
vendored
@@ -1,4 +0,0 @@
|
||||
clapper-*/
|
||||
clapper-*.pkg.tar.*
|
||||
pkg/
|
||||
src/
|
@@ -1,74 +0,0 @@
|
||||
#
|
||||
# PKGBUILD file for package clapper
|
||||
#
|
||||
# Copyright (C) 2020/21 sp1rit
|
||||
# Copyright (C) 2020 Rafostar
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
# Maintainer: sp1rit <sp1ritCS@protonmail.com>
|
||||
|
||||
_basename=clapper
|
||||
pkgname="${_basename}-git"
|
||||
pkgver=r393.bf04af2
|
||||
pkgrel=1
|
||||
pkgdesc="A GNOME media player built using GJS with GTK4 toolkit and powered by GStreamer with OpenGL rendering."
|
||||
arch=(any)
|
||||
url="https://github.com/Rafostar/clapper"
|
||||
license=("GPL-3.0")
|
||||
depends=(
|
||||
"gtk4"
|
||||
"gjs"
|
||||
"glib2>=2.56.0" # glib-2.0, gmodule-2.0, gio-2.0
|
||||
"gobject-introspection" # /usr/sbin/g-ir-scanner
|
||||
"wayland-protocols" # gtk4 non-default runtime dep
|
||||
"hicolor-icon-theme"
|
||||
"gstreamer>=1.18.0" # gstreamer-1.0, gstreamer-base-1.0
|
||||
"gst-plugins-base-libs>=1.18.0" # gstreamer-pbutils-1.0, gstreamer-audio-1.0, gstreamer-tag-1.0, gstreamer-video-1.0, gstreamer-gl-1.0, gstreamer-gl-prototypes-1.0, gstreamer-gl-x11-1.0, gstreamer-gl-wayland-1.0, gstreamer-gl-egl-1.0,
|
||||
"gst-plugins-good>=1.18.0"
|
||||
"gst-plugins-bad-libs>=1.18.0"
|
||||
)
|
||||
makedepends=(
|
||||
"meson>=0.50"
|
||||
"git"
|
||||
)
|
||||
optdepends=(
|
||||
"gst-libav>=1.18.0: Popular video decoders"
|
||||
"gstreamer-vaapi>=1.18.0: Intel/AMD video acceleration"
|
||||
)
|
||||
source=("${_basename}::git+https://github.com/Rafostar/${_basename}.git")
|
||||
provides=("${_basename}")
|
||||
replaces=("${_basename}")
|
||||
conflicts=("${_basename}")
|
||||
md5sums=("SKIP")
|
||||
|
||||
pkgver() {
|
||||
cd "${srcdir}/${_basename}"
|
||||
printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
|
||||
}
|
||||
|
||||
prepare() {
|
||||
cd "${srcdir}/${_basename}"
|
||||
arch-meson . _build
|
||||
}
|
||||
|
||||
build() {
|
||||
cd "${srcdir}/${_basename}"
|
||||
ninja -C _build
|
||||
}
|
||||
|
||||
package() {
|
||||
cd "${srcdir}/${_basename}"
|
||||
DESTDIR="$pkgdir" meson install -C _build/
|
||||
}
|
@@ -1,28 +0,0 @@
|
||||
Format: 3.0 (quilt)
|
||||
Source: clapper
|
||||
Binary: clapper
|
||||
Architecture: any
|
||||
Version: 0.2.1
|
||||
Maintainer: Rafostar <rafostar.github@gmail.com>
|
||||
Build-Depends: debhelper (>= 10),
|
||||
meson (>= 0.50),
|
||||
gjs,
|
||||
gobject-introspection,
|
||||
libgtk-4-dev (>= 4.0.0),
|
||||
libgstreamer1.0-dev (>= 1.18),
|
||||
libgstreamer-plugins-base1.0-dev (>= 1.18),
|
||||
libgstreamer-gl1.0-0 (>= 1.18),
|
||||
libgles-dev,
|
||||
libglib2.0-dev,
|
||||
libglib2.0-bin,
|
||||
desktop-file-utils,
|
||||
hicolor-icon-theme,
|
||||
brz,
|
||||
libfontconfig1-dev,
|
||||
libpam-systemd
|
||||
Package-List:
|
||||
clapper deb gnome optional arch=any
|
||||
Files:
|
||||
0 0 debian.tar.xz
|
||||
Description: Simple and modern GNOME media player
|
||||
A GNOME media player built using GJS with GTK4 toolkit and powered by GStreamer with OpenGL rendering.
|
@@ -1,5 +0,0 @@
|
||||
clapper (0.2.1) unstable; urgency=low
|
||||
|
||||
* New version
|
||||
|
||||
-- Rafostar <rafostar.github@gmail.com> Mon, 19 Apr 2021 09:39:00 +0100
|
@@ -1 +0,0 @@
|
||||
10
|
@@ -1,37 +0,0 @@
|
||||
Source: clapper
|
||||
Section: gnome
|
||||
Priority: optional
|
||||
Maintainer: Rafostar <rafostar.github@gmail.com>
|
||||
Standards-Version: 4.4.0
|
||||
Build-Depends: debhelper (>= 10),
|
||||
meson (>= 0.50),
|
||||
gjs,
|
||||
gobject-introspection,
|
||||
libgtk-4-dev (>= 4.0.0),
|
||||
libgstreamer1.0-dev (>= 1.18),
|
||||
libgstreamer-plugins-base1.0-dev (>= 1.18),
|
||||
libgstreamer-gl1.0-0 (>= 1.18),
|
||||
libgles-dev,
|
||||
libglib2.0-dev,
|
||||
libglib2.0-bin,
|
||||
desktop-file-utils,
|
||||
hicolor-icon-theme
|
||||
|
||||
Package: clapper
|
||||
Architecture: any
|
||||
Depends: gjs,
|
||||
gir1.2-gtk-4.0 (>= 4.0.0),
|
||||
hicolor-icon-theme,
|
||||
libgstreamer1.0-0 (>= 1.18),
|
||||
gstreamer1.0-plugins-base (>= 1.18),
|
||||
gstreamer1.0-plugins-good (>= 1.18),
|
||||
gstreamer1.0-plugins-bad (>= 1.18),
|
||||
gstreamer1.0-gl (>= 1.18)
|
||||
Recommends: gstreamer1.0-libav,
|
||||
gstreamer1.0-pulseaudio
|
||||
Suggests: gstreamer1.0-plugins-ugly,
|
||||
gstreamer1.0-vaapi
|
||||
Description: Simple and modern GNOME media player
|
||||
A GNOME media player built using GJS with GTK4 toolkit and powered by GStreamer with OpenGL rendering.
|
||||
.
|
||||
More codecs/features and video acceleration can be enabled by installing the suggested packages.
|
@@ -1,27 +0,0 @@
|
||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: clapper
|
||||
Source: https://github.com/Rafostar/clapper
|
||||
|
||||
Files: *
|
||||
Copyright: 2020 Rafostar <rafostar.github@gmail.com>
|
||||
License: GPL-3.0+
|
||||
|
||||
Files: debian/*
|
||||
Copyright: 2020 Rafostar <rafostar.github@gmail.com>
|
||||
License: GPL-3.0+
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
.
|
||||
On Debian systems, the complete text of the GNU General Public License
|
||||
Version 3 can be found in `/usr/share/common-licenses/GPL-3'.
|
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
%:
|
||||
dh $@
|
@@ -1 +0,0 @@
|
||||
3.0 (quilt)
|
58
pkgs/flatpak/com.github.rafostar.Clapper-nightly.json
Normal file
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"app-id": "com.github.rafostar.Clapper",
|
||||
"runtime": "org.gnome.Platform",
|
||||
"runtime-version": "master",
|
||||
"sdk": "org.gnome.Sdk",
|
||||
"sdk-extensions": [
|
||||
"org.freedesktop.Sdk.Extension.rust-nightly",
|
||||
"org.freedesktop.Sdk.Extension.llvm14"
|
||||
],
|
||||
"command": "com.github.rafostar.Clapper",
|
||||
"finish-args": [
|
||||
"--share=ipc",
|
||||
"--socket=fallback-x11",
|
||||
"--socket=wayland",
|
||||
"--device=dri",
|
||||
"--socket=pulseaudio",
|
||||
"--share=network",
|
||||
"--device=all",
|
||||
"--filesystem=xdg-run/pipewire-0:ro",
|
||||
"--filesystem=xdg-videos",
|
||||
"--filesystem=xdg-run/gvfsd",
|
||||
"--own-name=org.mpris.MediaPlayer2.Clapper",
|
||||
"--talk-name=org.gtk.vfs.*",
|
||||
"--talk-name=org.gnome.Shell",
|
||||
"--env=GST_PLUGIN_SYSTEM_PATH=/app/lib/gstreamer-1.0"
|
||||
],
|
||||
"build-options": {
|
||||
"append-path": "/usr/lib/sdk/rust-nightly/bin:/usr/lib/sdk/llvm14/bin",
|
||||
"prepend-ld-library-path": "/usr/lib/sdk/llvm14/lib"
|
||||
},
|
||||
"modules": [
|
||||
"flathub/shared-modules/gudev/gudev.json",
|
||||
"flathub/lib/liba52.json",
|
||||
"flathub/lib/libmpeg2.json",
|
||||
"flathub/lib/libdv.json",
|
||||
"flathub/lib/libdvdcss.json",
|
||||
"flathub/lib/libdvdread.json",
|
||||
"flathub/lib/libdvdnav.json",
|
||||
"flathub/lib/libass.json",
|
||||
"flathub/lib/ffmpeg.json",
|
||||
"testing/gstreamer.json",
|
||||
"testing/gst-plugins-rs.json",
|
||||
"testing/gtuber.json",
|
||||
{
|
||||
"name": "clapper",
|
||||
"buildsystem": "meson",
|
||||
"sources": [
|
||||
{
|
||||
"type": "dir",
|
||||
"path": "../../."
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"cleanup-commands": [
|
||||
"ln -s /lib/$FLATPAK_ARCH-linux-*/gstreamer-1.0/libgstpipewire.so /app/lib/gstreamer-1.0/"
|
||||
]
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"app-id": "com.github.rafostar.Clapper",
|
||||
"runtime": "org.gnome.Platform",
|
||||
"runtime-version": "40",
|
||||
"runtime-version": "43",
|
||||
"sdk": "org.gnome.Sdk",
|
||||
"command": "com.github.rafostar.Clapper",
|
||||
"finish-args": [
|
||||
@@ -12,21 +12,21 @@
|
||||
"--socket=pulseaudio",
|
||||
"--share=network",
|
||||
"--device=all",
|
||||
"--filesystem=xdg-run/pipewire-0:ro",
|
||||
"--filesystem=xdg-videos",
|
||||
"--filesystem=xdg-run/gvfsd",
|
||||
"--own-name=org.mpris.MediaPlayer2.Clapper",
|
||||
"--talk-name=org.gtk.vfs.*",
|
||||
"--talk-name=org.gnome.Shell",
|
||||
"--env=GST_PLUGIN_SYSTEM_PATH=/app/lib/gstreamer-1.0",
|
||||
"--env=GST_VAAPI_ALL_DRIVERS=1"
|
||||
"--env=GST_PLUGIN_SYSTEM_PATH=/app/lib/gstreamer-1.0"
|
||||
],
|
||||
"modules": [
|
||||
"flathub/lib/glib-networking.json",
|
||||
"flathub/shared-modules/gudev/gudev.json",
|
||||
"flathub/lib/pango.json",
|
||||
"flathub/lib/libsass.json",
|
||||
"flathub/lib/sassc.json",
|
||||
"flathub/lib/gtk4.json",
|
||||
"flathub/lib/liba52.json",
|
||||
"flathub/lib/libmpeg2.json",
|
||||
"flathub/lib/libdv.json",
|
||||
"flathub/lib/libdvdcss.json",
|
||||
"flathub/lib/libdvdread.json",
|
||||
"flathub/lib/libdvdnav.json",
|
||||
@@ -34,15 +34,15 @@
|
||||
"flathub/lib/ffmpeg.json",
|
||||
"flathub/lib/uchardet.json",
|
||||
"flathub/gstreamer-1.0/gstreamer.json",
|
||||
"flathub/gstreamer-1.0/gst-plugins-base.json",
|
||||
"flathub/gstreamer-1.0/gst-plugins-good.json",
|
||||
"flathub/gstreamer-1.0/gst-plugins-bad.json",
|
||||
"flathub/gstreamer-1.0/gst-plugins-ugly.json",
|
||||
"flathub/gstreamer-1.0/gst-libav.json",
|
||||
"flathub/gstreamer-1.0/gstreamer-vaapi.json",
|
||||
"testing/gtk4.json",
|
||||
"testing/libadwaita.json",
|
||||
"testing/gtuber.json",
|
||||
{
|
||||
"name": "clapper",
|
||||
"buildsystem": "meson",
|
||||
"config-opts": [
|
||||
"-Dc_args=\"-DHAVE_GST_PATCHES=1\""
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "dir",
|
||||
@@ -50,5 +50,8 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"cleanup-commands": [
|
||||
"ln -s /lib/$FLATPAK_ARCH-linux-*/gstreamer-1.0/libgstpipewire.so /app/lib/gstreamer-1.0/"
|
||||
]
|
||||
}
|
||||
|
26
pkgs/flatpak/testing/gst-plugins-rs.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "gst-plugins-rs",
|
||||
"buildsystem": "simple",
|
||||
"only-arches": [
|
||||
"x86_64"
|
||||
],
|
||||
"build-options": {
|
||||
"build-args": [
|
||||
"--share=network"
|
||||
],
|
||||
"env": {
|
||||
"CARGO_HOME": "/run/build/gst-plugins-rs/cargo"
|
||||
}
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git",
|
||||
"branch": "main"
|
||||
}
|
||||
],
|
||||
"build-commands": [
|
||||
"cargo install cargo-c",
|
||||
"cargo cinstall --prefix=/app -p gst-plugin-dav1d"
|
||||
]
|
||||
}
|
56
pkgs/flatpak/testing/gstreamer.json
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"name": "gstreamer",
|
||||
"buildsystem": "meson",
|
||||
"config-opts": [
|
||||
"--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",
|
||||
|
||||
"-Dgst-plugins-base:gl_api=opengl,gles2",
|
||||
"-Dgst-plugins-base:gl_platform=egl,glx",
|
||||
|
||||
"-Dgst-plugins-good:gtk3=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:mpeg2dec=enabled"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://gitlab.freedesktop.org/gstreamer/gstreamer.git",
|
||||
"branch": "main",
|
||||
"disable-submodules": true
|
||||
}
|
||||
]
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
diff --git a/meson.build b/meson.build
|
||||
index 38d23ed61c..68dea864a0 100644
|
||||
--- a/meson.build
|
||||
+++ b/meson.build
|
||||
@@ -799,7 +799,7 @@ subdir('docs/tools')
|
||||
subdir('docs/reference')
|
||||
|
||||
if not meson.is_cross_build()
|
||||
- if meson.version().version_compare('>=0.57.0')
|
||||
+ if false
|
||||
gnome.post_install(
|
||||
glib_compile_schemas: true,
|
||||
gio_querymodules: gio_module_dirs,
|
114
pkgs/flatpak/testing/gtk4-latest-gcc-fix.patch
Normal file
@@ -0,0 +1,114 @@
|
||||
From 4dcd02e85315f487310e2e01fe9412706a77dc35 Mon Sep 17 00:00:00 2001
|
||||
From: Emmanuele Bassi <ebassi@gnome.org>
|
||||
Date: Tue, 19 Apr 2022 15:33:21 +0100
|
||||
Subject: [PATCH] Quench the anger of GCC
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Direct access of the fields of the union trips compiler warnings with
|
||||
GCC 12, such as:
|
||||
|
||||
../gtk/gtkimagedefinition.c:135:13: error: array subscript
|
||||
‘GtkImageDefinition {aka union _GtkImageDefinition}[0]’ is partly
|
||||
outside array bounds of ‘GtkImageDefinitionEmpty[1]’ {aka
|
||||
‘struct _GtkImageDefinitionEmpty[1]’} [-Werror=array-bounds]
|
||||
---
|
||||
gtk/gtkimagedefinition.c | 38 ++++++++++++++++++++++++++++----------
|
||||
1 file changed, 28 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/gtk/gtkimagedefinition.c b/gtk/gtkimagedefinition.c
|
||||
index 1b7c9e51d9..3cf785b01c 100644
|
||||
--- a/gtk/gtkimagedefinition.c
|
||||
+++ b/gtk/gtkimagedefinition.c
|
||||
@@ -132,7 +132,9 @@ gtk_image_definition_new_paintable (GdkPaintable *paintable)
|
||||
GtkImageDefinition *
|
||||
gtk_image_definition_ref (GtkImageDefinition *def)
|
||||
{
|
||||
- def->empty.ref_count++;
|
||||
+ GtkImageDefinitionEmpty *empty = (GtkImageDefinitionEmpty *) def;
|
||||
+
|
||||
+ empty->ref_count++;
|
||||
|
||||
return def;
|
||||
}
|
||||
@@ -140,9 +142,11 @@ gtk_image_definition_ref (GtkImageDefinition *def)
|
||||
void
|
||||
gtk_image_definition_unref (GtkImageDefinition *def)
|
||||
{
|
||||
- def->empty.ref_count--;
|
||||
+ GtkImageDefinitionEmpty *empty = (GtkImageDefinitionEmpty *) def;
|
||||
+
|
||||
+ empty->ref_count--;
|
||||
|
||||
- if (def->empty.ref_count > 0)
|
||||
+ if (empty->ref_count > 0)
|
||||
return;
|
||||
|
||||
switch (def->type)
|
||||
@@ -152,13 +156,22 @@ gtk_image_definition_unref (GtkImageDefinition *def)
|
||||
g_assert_not_reached ();
|
||||
break;
|
||||
case GTK_IMAGE_PAINTABLE:
|
||||
- g_object_unref (def->paintable.paintable);
|
||||
+ {
|
||||
+ GtkImageDefinitionPaintable *paintable = (GtkImageDefinitionPaintable *) def;
|
||||
+ g_object_unref (paintable->paintable);
|
||||
+ }
|
||||
break;
|
||||
case GTK_IMAGE_ICON_NAME:
|
||||
- g_free (def->icon_name.icon_name);
|
||||
+ {
|
||||
+ GtkImageDefinitionIconName *icon_name = (GtkImageDefinitionIconName *) def;
|
||||
+ g_free (icon_name->icon_name);
|
||||
+ }
|
||||
break;
|
||||
case GTK_IMAGE_GICON:
|
||||
- g_object_unref (def->gicon.gicon);
|
||||
+ {
|
||||
+ GtkImageDefinitionGIcon *gicon = (GtkImageDefinitionGIcon *) def;
|
||||
+ g_object_unref (gicon->gicon);
|
||||
+ }
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -189,27 +202,32 @@ gtk_image_definition_get_scale (const GtkImageDefinition *def)
|
||||
const char *
|
||||
gtk_image_definition_get_icon_name (const GtkImageDefinition *def)
|
||||
{
|
||||
+ const GtkImageDefinitionIconName *icon_name = (const GtkImageDefinitionIconName *) def;
|
||||
+
|
||||
if (def->type != GTK_IMAGE_ICON_NAME)
|
||||
return NULL;
|
||||
|
||||
- return def->icon_name.icon_name;
|
||||
+ return icon_name->icon_name;
|
||||
}
|
||||
|
||||
GIcon *
|
||||
gtk_image_definition_get_gicon (const GtkImageDefinition *def)
|
||||
{
|
||||
+ const GtkImageDefinitionGIcon *gicon = (const GtkImageDefinitionGIcon *) def;
|
||||
+
|
||||
if (def->type != GTK_IMAGE_GICON)
|
||||
return NULL;
|
||||
|
||||
- return def->gicon.gicon;
|
||||
+ return gicon->gicon;
|
||||
}
|
||||
|
||||
GdkPaintable *
|
||||
gtk_image_definition_get_paintable (const GtkImageDefinition *def)
|
||||
{
|
||||
+ const GtkImageDefinitionPaintable *paintable = (const GtkImageDefinitionPaintable *) def;
|
||||
+
|
||||
if (def->type != GTK_IMAGE_PAINTABLE)
|
||||
return NULL;
|
||||
|
||||
- return def->paintable.paintable;
|
||||
+ return paintable->paintable;
|
||||
}
|
||||
-
|
||||
--
|
||||
GitLab
|
||||
|
31
pkgs/flatpak/testing/gtk4-popover-unrealize.patch
Normal file
@@ -0,0 +1,31 @@
|
||||
From b413ee2c7d458c7005d3d3d1da8822cd86893ac0 Mon Sep 17 00:00:00 2001
|
||||
From: Rafostar <40623528+Rafostar@users.noreply.github.com>
|
||||
Date: Fri, 4 Dec 2020 19:25:34 +0100
|
||||
Subject: [PATCH] popover: Call unrealize on hide
|
||||
|
||||
When popover is shown "realize" method is called which creates a new
|
||||
surface for popup. Unfortunately this causes performance drop on Wayland until that
|
||||
surface is destroyed what happens inside "unrealize" method during popover destruction.
|
||||
|
||||
This commit changes default behavior in a way that surface will be destroyed
|
||||
when popover is closed and app will ragain the performance it lost when
|
||||
popover was shown.
|
||||
---
|
||||
gtk/gtkpopover.c | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
diff --git a/gtk/gtkpopover.c b/gtk/gtkpopover.c
|
||||
index 504dcd6cc1..a7a764d483 100644
|
||||
--- a/gtk/gtkpopover.c
|
||||
+++ b/gtk/gtkpopover.c
|
||||
@@ -951,6 +951,7 @@ gtk_popover_hide (GtkWidget *widget)
|
||||
|
||||
gtk_popover_set_mnemonics_visible (GTK_POPOVER (widget), FALSE);
|
||||
_gtk_widget_set_visible_flag (widget, FALSE);
|
||||
+ gtk_widget_unrealize (widget);
|
||||
gtk_widget_unmap (widget);
|
||||
g_signal_emit (widget, signals[CLOSED], 0);
|
||||
}
|
||||
--
|
||||
GitLab
|
||||
|
41
pkgs/flatpak/testing/gtk4.json
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "gtk",
|
||||
"buildsystem": "meson",
|
||||
"config-opts": [
|
||||
"--buildtype=release",
|
||||
"--wrap-mode=nodownload",
|
||||
|
||||
"-Dbroadway-backend=true",
|
||||
"-Dwin32-backend=false",
|
||||
"-Dmacos-backend=false",
|
||||
"-Dmedia-ffmpeg=disabled",
|
||||
"-Dprint-cups=disabled",
|
||||
"-Dintrospection=enabled",
|
||||
"-Ddemos=false",
|
||||
"-Dbuild-examples=false",
|
||||
"-Dbuild-tests=false"
|
||||
],
|
||||
"cleanup": [
|
||||
"/bin/gtk4-builder-tool",
|
||||
"/bin/gtk4-encode-symbolic-svg"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://gitlab.gnome.org/GNOME/gtk.git",
|
||||
"branch": "gtk-4-4"
|
||||
},
|
||||
{
|
||||
"type": "patch",
|
||||
"path": "gtk4-popover-unrealize.patch"
|
||||
},
|
||||
{
|
||||
"type": "patch",
|
||||
"path": "gtk4-disable-meson-gnome-post-install.patch"
|
||||
},
|
||||
{
|
||||
"type": "patch",
|
||||
"path": "gtk4-latest-gcc-fix.patch"
|
||||
}
|
||||
]
|
||||
}
|
20
pkgs/flatpak/testing/gtuber.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
21
pkgs/flatpak/testing/libadwaita.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "libadwaita",
|
||||
"buildsystem": "meson",
|
||||
"config-opts": [
|
||||
"--buildtype=release",
|
||||
"--wrap-mode=nofallback",
|
||||
"-Dintrospection=enabled",
|
||||
"-Dvapi=false",
|
||||
"-Dgtk_doc=false",
|
||||
"-Dtests=false",
|
||||
"-Dexamples=false"
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://gitlab.gnome.org/GNOME/libadwaita.git",
|
||||
"tag": "1.0.0.alpha.4",
|
||||
"commit": "6b447fde8f270001a0dc29ef59d3e9bf6d32dae9"
|
||||
}
|
||||
]
|
||||
}
|
3
pkgs/rpm/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
.osc/
|
||||
clapper/
|
||||
.lock
|
@@ -1 +0,0 @@
|
||||
addFilter("explicit-lib-dependency")
|
@@ -1,166 +0,0 @@
|
||||
#
|
||||
# spec file for package clapper
|
||||
#
|
||||
# Copyright (C) 2020 sp1rit
|
||||
# Copyright (C) 2020-21 Rafostar
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
%define debug_package %{nil}
|
||||
|
||||
%global appname com.github.rafostar.Clapper
|
||||
%global gst_version 1.18.0
|
||||
%global gtk4_version 4.0.0
|
||||
%global meson_version 0.50
|
||||
%global glib2_version 2.56.0
|
||||
|
||||
Name: clapper
|
||||
Version: 0.2.1
|
||||
Release: 1%{?dist}
|
||||
Summary: Simple and modern GNOME media player
|
||||
|
||||
License: GPL-3.0
|
||||
URL: https://github.com/Rafostar/clapper
|
||||
BuildRoot: %{_builddir}/%{name}-%{version}-build
|
||||
Source0: _service
|
||||
|
||||
BuildRequires: meson >= %{meson_version}
|
||||
BuildRequires: gtk4-devel >= %{gtk4_version}
|
||||
BuildRequires: glib2-devel >= %{glib2_version}
|
||||
BuildRequires: gobject-introspection-devel
|
||||
BuildRequires: gjs
|
||||
BuildRequires: gcc-c++
|
||||
BuildRequires: desktop-file-utils
|
||||
BuildRequires: hicolor-icon-theme
|
||||
|
||||
Requires: gjs
|
||||
Requires: gtk4 >= %{gtk4_version}
|
||||
Requires: hicolor-icon-theme
|
||||
|
||||
%if 0%{?suse_version}
|
||||
# SUSE recommends group tag, while Fedora discourages their use
|
||||
Group: Productivity/Multimedia/Video/Players
|
||||
|
||||
BuildRequires: update-desktop-files
|
||||
BuildRequires: gstreamer-devel >= %{gst_version}
|
||||
BuildRequires: gstreamer-plugins-base-devel >= %{gst_version}
|
||||
BuildRequires: Mesa-libGLESv2-devel
|
||||
BuildRequires: Mesa-libGLESv3-devel
|
||||
|
||||
Requires: gstreamer >= %{gst_version}
|
||||
Requires: gstreamer-plugins-base >= %{gst_version}
|
||||
Requires: gstreamer-plugins-good >= %{gst_version}
|
||||
Requires: gstreamer-plugins-bad >= %{gst_version}
|
||||
|
||||
# Popular video decoders
|
||||
Recommends: gstreamer-plugins-libav >= %{gst_version}
|
||||
|
||||
# CD Playback
|
||||
Suggests: gstreamer-plugins-ugly
|
||||
# Intel/AMD video acceleration
|
||||
Suggests: gstreamer-plugins-vaapi
|
||||
%else
|
||||
BuildRequires: glibc-all-langpacks
|
||||
BuildRequires: gstreamer1-devel >= %{gst_version}
|
||||
BuildRequires: gstreamer1-plugins-base-devel >= %{gst_version}
|
||||
BuildRequires: mesa-libGL-devel
|
||||
BuildRequires: mesa-libGLES-devel
|
||||
BuildRequires: mesa-libGLU-devel
|
||||
BuildRequires: mesa-libEGL-devel
|
||||
|
||||
Requires: gstreamer1 >= %{gst_version}
|
||||
Requires: gstreamer1-plugins-base >= %{gst_version}
|
||||
Requires: gstreamer1-plugins-good >= %{gst_version}
|
||||
Requires: gstreamer1-plugins-bad-free >= %{gst_version}
|
||||
|
||||
# ASS subtitles (assrender)
|
||||
Recommends: gstreamer1-plugins-bad-free-extras >= %{gst_version}
|
||||
|
||||
# CD Playback
|
||||
Suggests: gstreamer1-plugins-ugly-free
|
||||
# Intel/AMD video acceleration
|
||||
Suggests: gstreamer1-vaapi
|
||||
%endif
|
||||
|
||||
%description
|
||||
A GNOME media player built using GJS with GTK4 toolkit and powered by GStreamer with OpenGL rendering.
|
||||
|
||||
%prep
|
||||
%setup -q -n %{_sourcedir}/%{name}-%{version} -T -D
|
||||
|
||||
%build
|
||||
%meson
|
||||
%meson_build
|
||||
|
||||
%install
|
||||
%meson_install
|
||||
%if 0%{?suse_version}
|
||||
%suse_update_desktop_file %{appname}
|
||||
%endif
|
||||
|
||||
%check
|
||||
desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop
|
||||
|
||||
%files
|
||||
%license COPYING
|
||||
%doc README.md
|
||||
%{_bindir}/%{appname}*
|
||||
%{_datadir}/%{appname}/
|
||||
%{_datadir}/icons/hicolor/*/apps/*.svg
|
||||
%{_datadir}/glib-2.0/schemas/%{appname}.gschema.xml
|
||||
%{_datadir}/mime/packages/%{appname}.xml
|
||||
%{_datadir}/applications/*.desktop
|
||||
%{_datadir}/metainfo/*.metainfo.xml
|
||||
%{_datadir}/gir-1.0/GstClapper-1.0.gir
|
||||
%{_libdir}/%{appname}/
|
||||
|
||||
%changelog
|
||||
* Mon Apr 19 2021 Rafostar <rafostar.github@gmail.com> - 0.2.1-1
|
||||
- New version
|
||||
|
||||
* Tue Apr 13 2021 Rafostar <rafostar.github@gmail.com> - 0.2.0-1
|
||||
- New version
|
||||
|
||||
* Fri Feb 25 2021 Rafostar <rafostar.github@gmail.com> - 0.1.0-1
|
||||
- New version
|
||||
|
||||
* Sun Feb 7 2021 Rafostar <rafostar.github@gmail.com> - 0.0.0-10
|
||||
- Install gstclapper libs to app named subdirectory
|
||||
|
||||
* Fri Feb 5 2021 Rafostar <rafostar.github@gmail.com> - 0.0.0-9
|
||||
- Update build with gstclapper libs support
|
||||
|
||||
* Thu Jan 21 2021 Rafostar <rafostar.github@gmail.com> - 0.0.0-8
|
||||
- Use metainfo instead of deprecated appdata
|
||||
|
||||
* Mon Jan 18 2021 Rafostar <rafostar.github@gmail.com> - 0.0.0-7
|
||||
- Remove gjs-1.0 files
|
||||
|
||||
* Sun Dec 20 2020 Rafostar <rafostar.github@gmail.com> - 0.0.0-6
|
||||
- Include additional app binaries
|
||||
|
||||
* Sat Oct 31 2020 Rafostar <rafostar.github@gmail.com> - 0.0.0-5
|
||||
- Added metainfo
|
||||
|
||||
* Sun Oct 25 2020 Rafostar <rafostar.github@gmail.com> - 0.0.0-4
|
||||
- Added gschema
|
||||
|
||||
* Wed Oct 14 2020 Rafostar <rafostar.github@gmail.com> - 0.0.0-3
|
||||
- Update to GTK4
|
||||
|
||||
* Sat Sep 19 22:02:00 CEST 2020 sp1rit - 0.0.0-2
|
||||
- Added suse_update_desktop_file macro for SuSE packages
|
||||
|
||||
* Fri Sep 18 2020 Rafostar <rafostar.github@gmail.com> - 0.0.0-1
|
||||
- Initial package
|
1
po/LINGUAS
Normal file
@@ -0,0 +1 @@
|
||||
ar ast ca cs de es eu fa fr he hu it ja nl pl pt pt_BR ru sv tr zh_CN
|
10
po/POTFILES
Normal file
@@ -0,0 +1,10 @@
|
||||
ui/clapper.ui
|
||||
ui/elapsed-time-button.ui
|
||||
ui/help-overlay.ui
|
||||
ui/preferences-plugin-ranking-subpage.ui
|
||||
ui/preferences-window.ui
|
||||
|
||||
src/buttons.js
|
||||
src/dialogs.js
|
||||
src/revealers.js
|
||||
src/widget.js
|
438
po/af.po
Normal file
@@ -0,0 +1,438 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: clapper\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-05-26 18:41+0200\n"
|
||||
"PO-Revision-Date: 2022-05-26 16:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Afrikaans\n"
|
||||
"Language: af_ZA\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: af\n"
|
||||
"X-Crowdin-File: /master/po/com.github.rafostar.Clapper.pot\n"
|
||||
"X-Crowdin-File-ID: 31\n"
|
||||
|
||||
#: ui/clapper.ui:6
|
||||
msgid "Open Files…"
|
||||
msgstr ""
|
||||
|
||||
#: ui/clapper.ui:10
|
||||
msgid "Open URI…"
|
||||
msgstr ""
|
||||
|
||||
#: ui/clapper.ui:16 ui/preferences-window.ui:4
|
||||
msgid "Preferences"
|
||||
msgstr ""
|
||||
|
||||
#: ui/clapper.ui:20
|
||||
msgid "Shortcuts"
|
||||
msgstr ""
|
||||
|
||||
#: ui/clapper.ui:26
|
||||
msgid "About Clapper"
|
||||
msgstr ""
|
||||
|
||||
#: ui/elapsed-time-button.ui:27
|
||||
msgid "Speed"
|
||||
msgstr ""
|
||||
|
||||
#: ui/elapsed-time-button.ui:41 ui/preferences-window.ui:82
|
||||
msgid "Normal"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:10 ui/preferences-window.ui:11
|
||||
msgid "General"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:13
|
||||
msgid "Show shortcuts"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:19
|
||||
msgid "Toggle fullscreen"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:20
|
||||
msgid "Double tap | Double click"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:26
|
||||
msgid "Leave fullscreen"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:32
|
||||
msgid "Reveal OSD (fullscreen only)"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:33
|
||||
msgid "Tap"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:39
|
||||
msgid "Quit"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:47
|
||||
msgid "Media"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:50
|
||||
msgid "Open files"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:56 src/dialogs.js:137
|
||||
msgid "Open URI"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:64
|
||||
msgid "Playlist"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:67
|
||||
msgid "Next item"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:68
|
||||
msgid "Double tap (right side)"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:74
|
||||
msgid "Previous item"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:75
|
||||
msgid "Double tap (left side)"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:81
|
||||
msgid "Change repeat mode"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:87
|
||||
msgid "Export to file"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:95 ui/preferences-window.ui:118
|
||||
msgid "Playback"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:98
|
||||
msgid "Toggle play"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:99
|
||||
msgid "Long press | Right click"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:105
|
||||
msgid "Seek forward"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:106
|
||||
msgid "Swipe right | Scroll right"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:112
|
||||
msgid "Seek backward"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:113
|
||||
msgid "Swipe left | Scroll left"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:119
|
||||
msgid "Volume up"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:120
|
||||
msgid "Swipe up | Scroll up"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:126
|
||||
msgid "Volume down"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:127
|
||||
msgid "Swipe down | Scroll down"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:133
|
||||
msgid "Toggle mute"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:139
|
||||
msgid "Next chapter"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:145
|
||||
msgid "Previous chapter"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-plugin-ranking-subpage.ui:11
|
||||
msgid "Decoders"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-plugin-ranking-subpage.ui:18
|
||||
msgid "Return to the preferences"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:15
|
||||
msgid "Behavior"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:18
|
||||
msgid "Auto fullscreen"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:19
|
||||
msgid "Enter fullscreen when playlist is replaced except floating mode"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:25
|
||||
msgid "Ask to resume recent media"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:31
|
||||
msgid "Float on all workspaces"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:32
|
||||
msgid "This option only works on GNOME"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:38
|
||||
msgid "After playback"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:43
|
||||
msgid "Do nothing"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:44
|
||||
msgid "Freeze last frame"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:45
|
||||
msgid "Close the app"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:55
|
||||
msgid "Volume"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:58
|
||||
msgid "Custom initial value"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:59
|
||||
msgid "Set custom volume at startup instead of restoring it"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:63
|
||||
msgid "Volume percentage"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:74
|
||||
msgid "Seeking"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:77
|
||||
msgid "Mode"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:83
|
||||
msgid "Accurate"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:84
|
||||
msgid "Fast"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:92
|
||||
msgid "Unit"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:97
|
||||
msgid "Second"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:98
|
||||
msgid "Minute"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:99
|
||||
msgid "Percentage"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:107
|
||||
msgid "Value"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:122
|
||||
msgid "Audio"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:125
|
||||
msgid "Offset in milliseconds"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:132
|
||||
msgid "Only native audio formats"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:140
|
||||
msgid "Subtitles"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:143
|
||||
msgid "Default font"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:153
|
||||
msgid "Network"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:157
|
||||
msgid "Client"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:160
|
||||
msgid "Progressive download buffering"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:168
|
||||
msgid "Server"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:171
|
||||
msgid "Control player remotely"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:175
|
||||
msgid "Listening port"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:188
|
||||
msgid "Tweaks"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:192
|
||||
msgid "Appearance"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:195
|
||||
msgid "Dark theme"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:201
|
||||
msgid "Render window shadows"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:202
|
||||
msgid "Disable to increase performance when windowed"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:213
|
||||
msgid "Plugin ranking"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:214
|
||||
msgid "Alter default ranks of GStreamer plugins"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:219
|
||||
msgid "Use playbin3"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:220 ui/preferences-window.ui:229
|
||||
msgid "Requires player restart"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:222 ui/preferences-window.ui:231
|
||||
msgid "Experimental"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:228
|
||||
msgid "Use PipeWire for audio output"
|
||||
msgstr ""
|
||||
|
||||
#: src/buttons.js:201
|
||||
#, javascript-format
|
||||
msgid "Decoder: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:152
|
||||
msgid "Enter or drop URI here"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:157
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:158
|
||||
msgid "Open"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:226
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:227
|
||||
msgid "Completed"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:235
|
||||
msgid "Resume playback?"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:289
|
||||
#, javascript-format
|
||||
msgid "GTK version: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:290
|
||||
#, javascript-format
|
||||
msgid "Adwaita version: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:291
|
||||
#, javascript-format
|
||||
msgid "GStreamer version: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:292
|
||||
#, javascript-format
|
||||
msgid "GJS version: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:300
|
||||
msgid "A GNOME media player powered by GStreamer"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: Put your name(s) here for credits or leave untranslated
|
||||
#: src/dialogs.js:305
|
||||
msgid "translator-credits"
|
||||
msgstr ""
|
||||
|
||||
#: src/revealers.js:170
|
||||
#, javascript-format
|
||||
msgid "Ends at: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/widget.js:226 src/widget.js:235 src/widget.js:241 src/widget.js:247
|
||||
msgid "Undetermined"
|
||||
msgstr ""
|
||||
|
||||
#: src/widget.js:242
|
||||
msgid "Channels"
|
||||
msgstr ""
|
||||
|
||||
#: src/widget.js:260
|
||||
msgid "Disabled"
|
||||
msgstr ""
|
||||
|
438
po/ar.po
Normal file
@@ -0,0 +1,438 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: clapper\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-05-26 18:41+0200\n"
|
||||
"PO-Revision-Date: 2022-05-26 16:50\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Arabic\n"
|
||||
"Language: ar_SA\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=6; plural=(n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5);\n"
|
||||
"X-Crowdin-Project: clapper\n"
|
||||
"X-Crowdin-Project-ID: 473374\n"
|
||||
"X-Crowdin-Language: ar\n"
|
||||
"X-Crowdin-File: /master/po/com.github.rafostar.Clapper.pot\n"
|
||||
"X-Crowdin-File-ID: 31\n"
|
||||
|
||||
#: ui/clapper.ui:6
|
||||
msgid "Open Files…"
|
||||
msgstr "فتح الملفات…"
|
||||
|
||||
#: ui/clapper.ui:10
|
||||
msgid "Open URI…"
|
||||
msgstr "فتح عنوان URL…"
|
||||
|
||||
#: ui/clapper.ui:16 ui/preferences-window.ui:4
|
||||
msgid "Preferences"
|
||||
msgstr "الإعدادات"
|
||||
|
||||
#: ui/clapper.ui:20
|
||||
msgid "Shortcuts"
|
||||
msgstr "الاختصارات"
|
||||
|
||||
#: ui/clapper.ui:26
|
||||
msgid "About Clapper"
|
||||
msgstr "حول Clapper"
|
||||
|
||||
#: ui/elapsed-time-button.ui:27
|
||||
msgid "Speed"
|
||||
msgstr "السرعة"
|
||||
|
||||
#: ui/elapsed-time-button.ui:41 ui/preferences-window.ui:82
|
||||
msgid "Normal"
|
||||
msgstr "عادي"
|
||||
|
||||
#: ui/help-overlay.ui:10 ui/preferences-window.ui:11
|
||||
msgid "General"
|
||||
msgstr "عام"
|
||||
|
||||
#: ui/help-overlay.ui:13
|
||||
msgid "Show shortcuts"
|
||||
msgstr "إظهار الاختصارات"
|
||||
|
||||
#: ui/help-overlay.ui:19
|
||||
msgid "Toggle fullscreen"
|
||||
msgstr "تبديل ملء الشاشة"
|
||||
|
||||
#: ui/help-overlay.ui:20
|
||||
msgid "Double tap | Double click"
|
||||
msgstr "نقرة مزدوجة"
|
||||
|
||||
#: ui/help-overlay.ui:26
|
||||
msgid "Leave fullscreen"
|
||||
msgstr "مغادرة ملء الشاشة"
|
||||
|
||||
#: ui/help-overlay.ui:32
|
||||
msgid "Reveal OSD (fullscreen only)"
|
||||
msgstr "إظهار المعلومات (ملء الشاشة فقط)"
|
||||
|
||||
#: ui/help-overlay.ui:33
|
||||
msgid "Tap"
|
||||
msgstr "نقرة"
|
||||
|
||||
#: ui/help-overlay.ui:39
|
||||
msgid "Quit"
|
||||
msgstr "خروج"
|
||||
|
||||
#: ui/help-overlay.ui:47
|
||||
msgid "Media"
|
||||
msgstr "الوسائط"
|
||||
|
||||
#: ui/help-overlay.ui:50
|
||||
msgid "Open files"
|
||||
msgstr "افتح ملفًا"
|
||||
|
||||
#: ui/help-overlay.ui:56 src/dialogs.js:137
|
||||
msgid "Open URI"
|
||||
msgstr "فتح عنوان URL"
|
||||
|
||||
#: ui/help-overlay.ui:64
|
||||
msgid "Playlist"
|
||||
msgstr "قوائم التشغيل"
|
||||
|
||||
#: ui/help-overlay.ui:67
|
||||
msgid "Next item"
|
||||
msgstr "المحتوى التالي"
|
||||
|
||||
#: ui/help-overlay.ui:68
|
||||
msgid "Double tap (right side)"
|
||||
msgstr "نقر مزدوج (الجانب الأيمن)"
|
||||
|
||||
#: ui/help-overlay.ui:74
|
||||
msgid "Previous item"
|
||||
msgstr "المحتوى السابق"
|
||||
|
||||
#: ui/help-overlay.ui:75
|
||||
msgid "Double tap (left side)"
|
||||
msgstr "نقر مزدوج (الجانب الأيسر)"
|
||||
|
||||
#: ui/help-overlay.ui:81
|
||||
msgid "Change repeat mode"
|
||||
msgstr "تغيير وضع التكرار"
|
||||
|
||||
#: ui/help-overlay.ui:87
|
||||
msgid "Export to file"
|
||||
msgstr "التصدير إلى مِلَفّ"
|
||||
|
||||
#: ui/help-overlay.ui:95 ui/preferences-window.ui:118
|
||||
msgid "Playback"
|
||||
msgstr "المشغل"
|
||||
|
||||
#: ui/help-overlay.ui:98
|
||||
msgid "Toggle play"
|
||||
msgstr "بَدْءّ / إيقاف"
|
||||
|
||||
#: ui/help-overlay.ui:99
|
||||
msgid "Long press | Right click"
|
||||
msgstr "اضغط مطولاً | انقر بزر الفائرة الأيمن"
|
||||
|
||||
#: ui/help-overlay.ui:105
|
||||
msgid "Seek forward"
|
||||
msgstr "التقدم للأمام"
|
||||
|
||||
#: ui/help-overlay.ui:106
|
||||
msgid "Swipe right | Scroll right"
|
||||
msgstr "مرر لليمين"
|
||||
|
||||
#: ui/help-overlay.ui:112
|
||||
msgid "Seek backward"
|
||||
msgstr "الرجوع للوراء"
|
||||
|
||||
#: ui/help-overlay.ui:113
|
||||
msgid "Swipe left | Scroll left"
|
||||
msgstr "مرر لليسار"
|
||||
|
||||
#: ui/help-overlay.ui:119
|
||||
msgid "Volume up"
|
||||
msgstr "رفع مستوى الصوت"
|
||||
|
||||
#: ui/help-overlay.ui:120
|
||||
msgid "Swipe up | Scroll up"
|
||||
msgstr "مرر لأعلى"
|
||||
|
||||
#: ui/help-overlay.ui:126
|
||||
msgid "Volume down"
|
||||
msgstr "خفض مستوى الصوت"
|
||||
|
||||
#: ui/help-overlay.ui:127
|
||||
msgid "Swipe down | Scroll down"
|
||||
msgstr "مرر لأسفل"
|
||||
|
||||
#: ui/help-overlay.ui:133
|
||||
msgid "Toggle mute"
|
||||
msgstr "كتم الصوت"
|
||||
|
||||
#: ui/help-overlay.ui:139
|
||||
msgid "Next chapter"
|
||||
msgstr "الفصل التالي"
|
||||
|
||||
#: ui/help-overlay.ui:145
|
||||
msgid "Previous chapter"
|
||||
msgstr "الفصل السابق"
|
||||
|
||||
#: ui/preferences-plugin-ranking-subpage.ui:11
|
||||
msgid "Decoders"
|
||||
msgstr "برامج فك التشفير"
|
||||
|
||||
#: ui/preferences-plugin-ranking-subpage.ui:18
|
||||
msgid "Return to the preferences"
|
||||
msgstr "العودة إلى الإعدادات"
|
||||
|
||||
#: ui/preferences-window.ui:15
|
||||
msgid "Behavior"
|
||||
msgstr "السلوك"
|
||||
|
||||
#: ui/preferences-window.ui:18
|
||||
msgid "Auto fullscreen"
|
||||
msgstr "ملء الشاشة تلقائياً"
|
||||
|
||||
#: ui/preferences-window.ui:19
|
||||
msgid "Enter fullscreen when playlist is replaced except floating mode"
|
||||
msgstr "أدخل ملء الشاشة عند استبدال قائمة التشغيل باستثناء الوضع العائم"
|
||||
|
||||
#: ui/preferences-window.ui:25
|
||||
msgid "Ask to resume recent media"
|
||||
msgstr "اطلب استئناف الوسائط السابقة"
|
||||
|
||||
#: ui/preferences-window.ui:31
|
||||
msgid "Float on all workspaces"
|
||||
msgstr "عائم في جميع مساحات العمل"
|
||||
|
||||
#: ui/preferences-window.ui:32
|
||||
msgid "This option only works on GNOME"
|
||||
msgstr "هذا الخِيار يعمل فقط على GNOME"
|
||||
|
||||
#: ui/preferences-window.ui:38
|
||||
msgid "After playback"
|
||||
msgstr "بعد انتهاء"
|
||||
|
||||
#: ui/preferences-window.ui:43
|
||||
msgid "Do nothing"
|
||||
msgstr "لا تفعل شيئًا"
|
||||
|
||||
#: ui/preferences-window.ui:44
|
||||
msgid "Freeze last frame"
|
||||
msgstr "تجميد الأخر لقطة"
|
||||
|
||||
#: ui/preferences-window.ui:45
|
||||
msgid "Close the app"
|
||||
msgstr "أغلق التطبيق"
|
||||
|
||||
#: ui/preferences-window.ui:55
|
||||
msgid "Volume"
|
||||
msgstr "الصوت"
|
||||
|
||||
#: ui/preferences-window.ui:58
|
||||
msgid "Custom initial value"
|
||||
msgstr "قيمة الافتراضية مخصصة"
|
||||
|
||||
#: ui/preferences-window.ui:59
|
||||
msgid "Set custom volume at startup instead of restoring it"
|
||||
msgstr "تعيين مستوى صوت مخصص عند بَدْء التشغيل بدلاً من إعادته"
|
||||
|
||||
#: ui/preferences-window.ui:63
|
||||
msgid "Volume percentage"
|
||||
msgstr "حجم الصوت"
|
||||
|
||||
#: ui/preferences-window.ui:74
|
||||
msgid "Seeking"
|
||||
msgstr "الوضع"
|
||||
|
||||
#: ui/preferences-window.ui:77
|
||||
msgid "Mode"
|
||||
msgstr "النمط"
|
||||
|
||||
#: ui/preferences-window.ui:83
|
||||
msgid "Accurate"
|
||||
msgstr "دَقيق"
|
||||
|
||||
#: ui/preferences-window.ui:84
|
||||
msgid "Fast"
|
||||
msgstr "سريع"
|
||||
|
||||
#: ui/preferences-window.ui:92
|
||||
msgid "Unit"
|
||||
msgstr "الوحدة"
|
||||
|
||||
#: ui/preferences-window.ui:97
|
||||
msgid "Second"
|
||||
msgstr "ثواني"
|
||||
|
||||
#: ui/preferences-window.ui:98
|
||||
msgid "Minute"
|
||||
msgstr "دقائق"
|
||||
|
||||
#: ui/preferences-window.ui:99
|
||||
msgid "Percentage"
|
||||
msgstr "النسبة المئوية"
|
||||
|
||||
#: ui/preferences-window.ui:107
|
||||
msgid "Value"
|
||||
msgstr "القيمة"
|
||||
|
||||
#: ui/preferences-window.ui:122
|
||||
msgid "Audio"
|
||||
msgstr "الصوت"
|
||||
|
||||
#: ui/preferences-window.ui:125
|
||||
msgid "Offset in milliseconds"
|
||||
msgstr "إزاحة بالمللي ثانية"
|
||||
|
||||
#: ui/preferences-window.ui:132
|
||||
msgid "Only native audio formats"
|
||||
msgstr "تنسيقات الصوت الأصلية فقط"
|
||||
|
||||
#: ui/preferences-window.ui:140
|
||||
msgid "Subtitles"
|
||||
msgstr "التَّرْجَمَةً"
|
||||
|
||||
#: ui/preferences-window.ui:143
|
||||
msgid "Default font"
|
||||
msgstr "الخط الافتراضي"
|
||||
|
||||
#: ui/preferences-window.ui:153
|
||||
msgid "Network"
|
||||
msgstr "الشبكة"
|
||||
|
||||
#: ui/preferences-window.ui:157
|
||||
msgid "Client"
|
||||
msgstr "العميل"
|
||||
|
||||
#: ui/preferences-window.ui:160
|
||||
msgid "Progressive download buffering"
|
||||
msgstr "التخزين المؤقت للتنزيل بالتدريج"
|
||||
|
||||
#: ui/preferences-window.ui:168
|
||||
msgid "Server"
|
||||
msgstr "الخادم"
|
||||
|
||||
#: ui/preferences-window.ui:171
|
||||
msgid "Control player remotely"
|
||||
msgstr "التحكم بالوسائط عن بعد"
|
||||
|
||||
#: ui/preferences-window.ui:175
|
||||
msgid "Listening port"
|
||||
msgstr "منفذ الاستماع"
|
||||
|
||||
#: ui/preferences-window.ui:188
|
||||
msgid "Tweaks"
|
||||
msgstr "تعديلات"
|
||||
|
||||
#: ui/preferences-window.ui:192
|
||||
msgid "Appearance"
|
||||
msgstr "إعدادات المظهر"
|
||||
|
||||
#: ui/preferences-window.ui:195
|
||||
msgid "Dark theme"
|
||||
msgstr "المظهر الداكن"
|
||||
|
||||
#: ui/preferences-window.ui:201
|
||||
msgid "Render window shadows"
|
||||
msgstr "عرض ظلال النافذة"
|
||||
|
||||
#: ui/preferences-window.ui:202
|
||||
msgid "Disable to increase performance when windowed"
|
||||
msgstr "تعطيل لزيادة الأداء عند وضع النافذة"
|
||||
|
||||
#: ui/preferences-window.ui:213
|
||||
msgid "Plugin ranking"
|
||||
msgstr "أعدادات الإضافات"
|
||||
|
||||
#: ui/preferences-window.ui:214
|
||||
msgid "Alter default ranks of GStreamer plugins"
|
||||
msgstr "تغيير الأعدادات الافتراضية للأضافات GStreamer"
|
||||
|
||||
#: ui/preferences-window.ui:219
|
||||
msgid "Use playbin3"
|
||||
msgstr "استخدام playbin3"
|
||||
|
||||
#: ui/preferences-window.ui:220 ui/preferences-window.ui:229
|
||||
msgid "Requires player restart"
|
||||
msgstr "يتطلب إعادة التشغيل"
|
||||
|
||||
#: ui/preferences-window.ui:222 ui/preferences-window.ui:231
|
||||
msgid "Experimental"
|
||||
msgstr "تجريبية"
|
||||
|
||||
#: ui/preferences-window.ui:228
|
||||
msgid "Use PipeWire for audio output"
|
||||
msgstr "استخدام PipeWire لإخراج الصوت"
|
||||
|
||||
#: src/buttons.js:201
|
||||
#, javascript-format
|
||||
msgid "Decoder: %s"
|
||||
msgstr "فك التشفير: %s"
|
||||
|
||||
#: src/dialogs.js:152
|
||||
msgid "Enter or drop URI here"
|
||||
msgstr "أدخل أو الصق URI هنا"
|
||||
|
||||
#: src/dialogs.js:157
|
||||
msgid "Cancel"
|
||||
msgstr "إلغاء"
|
||||
|
||||
#: src/dialogs.js:158
|
||||
msgid "Open"
|
||||
msgstr "فتح"
|
||||
|
||||
#: src/dialogs.js:226
|
||||
msgid "Title"
|
||||
msgstr "العنوان"
|
||||
|
||||
#: src/dialogs.js:227
|
||||
msgid "Completed"
|
||||
msgstr "تم مشاهدة"
|
||||
|
||||
#: src/dialogs.js:235
|
||||
msgid "Resume playback?"
|
||||
msgstr "استئناف التشغيل؟"
|
||||
|
||||
#: src/dialogs.js:289
|
||||
#, javascript-format
|
||||
msgid "GTK version: %s"
|
||||
msgstr "إصدار GTK: %s"
|
||||
|
||||
#: src/dialogs.js:290
|
||||
#, javascript-format
|
||||
msgid "Adwaita version: %s"
|
||||
msgstr "إصدار Adwaita: %s"
|
||||
|
||||
#: src/dialogs.js:291
|
||||
#, javascript-format
|
||||
msgid "GStreamer version: %s"
|
||||
msgstr "إصدار GStreamer: %s"
|
||||
|
||||
#: src/dialogs.js:292
|
||||
#, javascript-format
|
||||
msgid "GJS version: %s"
|
||||
msgstr "إصدار GJS: %s"
|
||||
|
||||
#: src/dialogs.js:300
|
||||
msgid "A GNOME media player powered by GStreamer"
|
||||
msgstr "مشغل وسائط GNOME مدعوم من GStreamer"
|
||||
|
||||
#. TRANSLATORS: Put your name(s) here for credits or leave untranslated
|
||||
#: src/dialogs.js:305
|
||||
msgid "translator-credits"
|
||||
msgstr "Yousef Fawaz"
|
||||
|
||||
#: src/revealers.js:170
|
||||
#, javascript-format
|
||||
msgid "Ends at: %s"
|
||||
msgstr "ينتهي في: %s"
|
||||
|
||||
#: src/widget.js:226 src/widget.js:235 src/widget.js:241 src/widget.js:247
|
||||
msgid "Undetermined"
|
||||
msgstr "غير محدّد"
|
||||
|
||||
#: src/widget.js:242
|
||||
msgid "Channels"
|
||||
msgstr "قنوات"
|
||||
|
||||
#: src/widget.js:260
|
||||
msgid "Disabled"
|
||||
msgstr "مُعطّل"
|
||||
|
438
po/ast.po
Normal file
@@ -0,0 +1,438 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: clapper\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-05-26 18:41+0200\n"
|
||||
"PO-Revision-Date: 2022-06-26 22:43\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Asturian\n"
|
||||
"Language: ast_ES\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: ast\n"
|
||||
"X-Crowdin-File: /master/po/com.github.rafostar.Clapper.pot\n"
|
||||
"X-Crowdin-File-ID: 31\n"
|
||||
|
||||
#: ui/clapper.ui:6
|
||||
msgid "Open Files…"
|
||||
msgstr "Abrir ficheros…"
|
||||
|
||||
#: ui/clapper.ui:10
|
||||
msgid "Open URI…"
|
||||
msgstr "Abrir un URI…"
|
||||
|
||||
#: ui/clapper.ui:16 ui/preferences-window.ui:4
|
||||
msgid "Preferences"
|
||||
msgstr "Preferencies"
|
||||
|
||||
#: ui/clapper.ui:20
|
||||
msgid "Shortcuts"
|
||||
msgstr "Atayos"
|
||||
|
||||
#: ui/clapper.ui:26
|
||||
msgid "About Clapper"
|
||||
msgstr "Tocante a Clapper"
|
||||
|
||||
#: ui/elapsed-time-button.ui:27
|
||||
msgid "Speed"
|
||||
msgstr "Velocidá"
|
||||
|
||||
#: ui/elapsed-time-button.ui:41 ui/preferences-window.ui:82
|
||||
msgid "Normal"
|
||||
msgstr "Normal"
|
||||
|
||||
#: ui/help-overlay.ui:10 ui/preferences-window.ui:11
|
||||
msgid "General"
|
||||
msgstr "Xenerales"
|
||||
|
||||
#: ui/help-overlay.ui:13
|
||||
msgid "Show shortcuts"
|
||||
msgstr "Amosar los atayos"
|
||||
|
||||
#: ui/help-overlay.ui:19
|
||||
msgid "Toggle fullscreen"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:20
|
||||
msgid "Double tap | Double click"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:26
|
||||
msgid "Leave fullscreen"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:32
|
||||
msgid "Reveal OSD (fullscreen only)"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:33
|
||||
msgid "Tap"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:39
|
||||
msgid "Quit"
|
||||
msgstr "Colar"
|
||||
|
||||
#: ui/help-overlay.ui:47
|
||||
msgid "Media"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:50
|
||||
msgid "Open files"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:56 src/dialogs.js:137
|
||||
msgid "Open URI"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:64
|
||||
msgid "Playlist"
|
||||
msgstr "Llista de reproducción"
|
||||
|
||||
#: ui/help-overlay.ui:67
|
||||
msgid "Next item"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:68
|
||||
msgid "Double tap (right side)"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:74
|
||||
msgid "Previous item"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:75
|
||||
msgid "Double tap (left side)"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:81
|
||||
msgid "Change repeat mode"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:87
|
||||
msgid "Export to file"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:95 ui/preferences-window.ui:118
|
||||
msgid "Playback"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:98
|
||||
msgid "Toggle play"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:99
|
||||
msgid "Long press | Right click"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:105
|
||||
msgid "Seek forward"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:106
|
||||
msgid "Swipe right | Scroll right"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:112
|
||||
msgid "Seek backward"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:113
|
||||
msgid "Swipe left | Scroll left"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:119
|
||||
msgid "Volume up"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:120
|
||||
msgid "Swipe up | Scroll up"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:126
|
||||
msgid "Volume down"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:127
|
||||
msgid "Swipe down | Scroll down"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:133
|
||||
msgid "Toggle mute"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:139
|
||||
msgid "Next chapter"
|
||||
msgstr ""
|
||||
|
||||
#: ui/help-overlay.ui:145
|
||||
msgid "Previous chapter"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-plugin-ranking-subpage.ui:11
|
||||
msgid "Decoders"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-plugin-ranking-subpage.ui:18
|
||||
msgid "Return to the preferences"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:15
|
||||
msgid "Behavior"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:18
|
||||
msgid "Auto fullscreen"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:19
|
||||
msgid "Enter fullscreen when playlist is replaced except floating mode"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:25
|
||||
msgid "Ask to resume recent media"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:31
|
||||
msgid "Float on all workspaces"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:32
|
||||
msgid "This option only works on GNOME"
|
||||
msgstr "Esta opción namái funciona en GNOME"
|
||||
|
||||
#: ui/preferences-window.ui:38
|
||||
msgid "After playback"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:43
|
||||
msgid "Do nothing"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:44
|
||||
msgid "Freeze last frame"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:45
|
||||
msgid "Close the app"
|
||||
msgstr "Zarrar l’aplicación"
|
||||
|
||||
#: ui/preferences-window.ui:55
|
||||
msgid "Volume"
|
||||
msgstr "Volume"
|
||||
|
||||
#: ui/preferences-window.ui:58
|
||||
msgid "Custom initial value"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:59
|
||||
msgid "Set custom volume at startup instead of restoring it"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:63
|
||||
msgid "Volume percentage"
|
||||
msgstr "Porcentaxe de volume"
|
||||
|
||||
#: ui/preferences-window.ui:74
|
||||
msgid "Seeking"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:77
|
||||
msgid "Mode"
|
||||
msgstr "Mou"
|
||||
|
||||
#: ui/preferences-window.ui:83
|
||||
msgid "Accurate"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:84
|
||||
msgid "Fast"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:92
|
||||
msgid "Unit"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:97
|
||||
msgid "Second"
|
||||
msgstr "Segundu"
|
||||
|
||||
#: ui/preferences-window.ui:98
|
||||
msgid "Minute"
|
||||
msgstr "Minutu"
|
||||
|
||||
#: ui/preferences-window.ui:99
|
||||
msgid "Percentage"
|
||||
msgstr "Porcentaxe"
|
||||
|
||||
#: ui/preferences-window.ui:107
|
||||
msgid "Value"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:122
|
||||
msgid "Audio"
|
||||
msgstr "Audio"
|
||||
|
||||
#: ui/preferences-window.ui:125
|
||||
msgid "Offset in milliseconds"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:132
|
||||
msgid "Only native audio formats"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:140
|
||||
msgid "Subtitles"
|
||||
msgstr "Sotítulos"
|
||||
|
||||
#: ui/preferences-window.ui:143
|
||||
msgid "Default font"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:153
|
||||
msgid "Network"
|
||||
msgstr "Rede"
|
||||
|
||||
#: ui/preferences-window.ui:157
|
||||
msgid "Client"
|
||||
msgstr "Veceru"
|
||||
|
||||
#: ui/preferences-window.ui:160
|
||||
msgid "Progressive download buffering"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:168
|
||||
msgid "Server"
|
||||
msgstr "Sirvidor"
|
||||
|
||||
#: ui/preferences-window.ui:171
|
||||
msgid "Control player remotely"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:175
|
||||
msgid "Listening port"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:188
|
||||
msgid "Tweaks"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:192
|
||||
msgid "Appearance"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:195
|
||||
msgid "Dark theme"
|
||||
msgstr "Tema escuru"
|
||||
|
||||
#: ui/preferences-window.ui:201
|
||||
msgid "Render window shadows"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:202
|
||||
msgid "Disable to increase performance when windowed"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:213
|
||||
msgid "Plugin ranking"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:214
|
||||
msgid "Alter default ranks of GStreamer plugins"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:219
|
||||
msgid "Use playbin3"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:220 ui/preferences-window.ui:229
|
||||
msgid "Requires player restart"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:222 ui/preferences-window.ui:231
|
||||
msgid "Experimental"
|
||||
msgstr ""
|
||||
|
||||
#: ui/preferences-window.ui:228
|
||||
msgid "Use PipeWire for audio output"
|
||||
msgstr ""
|
||||
|
||||
#: src/buttons.js:201
|
||||
#, javascript-format
|
||||
msgid "Decoder: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:152
|
||||
msgid "Enter or drop URI here"
|
||||
msgstr ""
|
||||
|
||||
#: src/dialogs.js:157
|
||||
msgid "Cancel"
|
||||
msgstr "Encaboxar"
|
||||
|
||||
#: src/dialogs.js:158
|
||||
msgid "Open"
|
||||
msgstr "Abrir"
|
||||
|
||||
#: src/dialogs.js:226
|
||||
msgid "Title"
|
||||
msgstr "Títulu"
|
||||
|
||||
#: src/dialogs.js:227
|
||||
msgid "Completed"
|
||||
msgstr "Completáu al"
|
||||
|
||||
#: src/dialogs.js:235
|
||||
msgid "Resume playback?"
|
||||
msgstr "¿Quies siguir cola reproducción?"
|
||||
|
||||
#: src/dialogs.js:289
|
||||
#, javascript-format
|
||||
msgid "GTK version: %s"
|
||||
msgstr "Versión de GTK: %s"
|
||||
|
||||
#: src/dialogs.js:290
|
||||
#, javascript-format
|
||||
msgid "Adwaita version: %s"
|
||||
msgstr "Versión d’Adwaita: %s"
|
||||
|
||||
#: src/dialogs.js:291
|
||||
#, javascript-format
|
||||
msgid "GStreamer version: %s"
|
||||
msgstr "Versión de GStreamer: %s"
|
||||
|
||||
#: src/dialogs.js:292
|
||||
#, javascript-format
|
||||
msgid "GJS version: %s"
|
||||
msgstr "Versión de GJS: %s"
|
||||
|
||||
#: src/dialogs.js:300
|
||||
msgid "A GNOME media player powered by GStreamer"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: Put your name(s) here for credits or leave untranslated
|
||||
#: src/dialogs.js:305
|
||||
msgid "translator-credits"
|
||||
msgstr "Adolfo Jayme Barrientos <fitojb@ubuntu.com>, 2022"
|
||||
|
||||
#: src/revealers.js:170
|
||||
#, javascript-format
|
||||
msgid "Ends at: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/widget.js:226 src/widget.js:235 src/widget.js:241 src/widget.js:247
|
||||
msgid "Undetermined"
|
||||
msgstr ""
|
||||
|
||||
#: src/widget.js:242
|
||||
msgid "Channels"
|
||||
msgstr ""
|
||||
|
||||
#: src/widget.js:260
|
||||
msgid "Disabled"
|
||||
msgstr ""
|
||||
|