455 Commits

Author SHA1 Message Date
Rafał Dzięgiel
2575b61cd0 Require GTK 4.6.0 for new plugin 2022-03-01 09:36:46 +01:00
Rafał Dzięgiel
9abf2fb11d wip4 2022-03-01 09:36:46 +01:00
Rafał Dzięgiel
167be4c37b wip3 2022-03-01 09:36:46 +01:00
Rafał Dzięgiel
74c4a079e6 wip2 2022-03-01 09:36:46 +01:00
Rafał Dzięgiel
8a597fffe7 WIP 2022-03-01 09:36:46 +01:00
Rafał Dzięgiel
159f96c984 Merge pull request #214 from Rafostar/libsoup3
libsoup3
2022-02-28 16:58:29 +01:00
Rafał Dzięgiel
96ad4fa3db RPM: Require libsoup3 2022-02-17 15:44:45 +01:00
Rafał Dzięgiel
b2e7bef8d4 flatpak: Update git actions runtime to 41
We need to update, so we can build libsoup3. Flathub ver will likely remain as 40 ver to avoid some problems and will be updated directly to 42 later.
2022-02-14 17:37:15 +01:00
Rafał Dzięgiel
40a1dc6960 flatpak: Build libsoup3
Now both Clapper and Gtuber lib require libsoup3, so build it until it will be available in runtime
2022-02-14 17:37:15 +01:00
Rafał Dzięgiel
c4bd604e17 Port to libsoup3
With all apps and modules/plugins porting itself to libsoup3 we also need to do so.

It would appear as a good idea to conditionally import "3.0" and fallback to "2.4"
bindings here, but its not as loaded GStreamer plugins might use libsoup3 already
and we cannot have both libsoup2 and libsoup3 in a single process.
2022-02-14 17:37:15 +01:00
Rafał Dzięgiel
83c0e3b598 Remove unfinished web application
It used Broadway as an easy "reuse the same code into web application" way for me, but Broadway
is not very good for this. This feature should be made using some better dedicated framework for
building websites (so it can work better and support different screen sizes).

All the WebSocket functionality needed for this will remain (and be documented at a later point
when expanded and stable), so if anyone would like to make such remote controlling app, will be
free to do so (outside of Clapper code).
2022-02-14 17:37:15 +01:00
Rafał Dzięgiel
54715023c0 flatpak: Update git actions manifest 2022-02-14 16:36:29 +01:00
Rafał Dzięgiel
95046ceba8 flatpak: Sync with Flathub 2022-02-14 16:06:36 +01:00
Rafał Dzięgiel
b189e24b5e Merge pull request #209 from simonsextras/swdeish-tranlation-nbfreygff
Full Swedish Translation
2022-01-21 12:14:22 +01:00
sastofficial
cb9e2ac979 Update sv.po 2022-01-21 09:35:17 +01:00
sastofficial
545d8826e0 Update sv.po 2022-01-19 13:46:08 +01:00
Rafał Dzięgiel
c2160198b9 Update LINGUAS file 2022-01-19 10:07:09 +01:00
Rafał Dzięgiel
aa7eae2417 New translations com.github.rafostar.Clapper.pot (French) (#208) 2022-01-19 10:00:56 +01:00
Rafał Dzięgiel
3192a32845 Merge pull request #206 from Rafostar/unicode
Use more unicode characters
2022-01-18 18:46:06 +01:00
Rafał Dzięgiel
706c783498 Use unicode U+2215 for division 2022-01-17 22:28:17 +01:00
Rafał Dzięgiel
8ba3ca4af6 Use unicode U+00D7 for multiplication 2022-01-17 21:01:23 +01:00
Rafał Dzięgiel
38efa7ab9f Use unicode U+2236 for time 2022-01-17 20:27:13 +01:00
Rafał Dzięgiel
d19ddbcaca Merge pull request #204 from Rafostar/devel
Misc gstclapper fixes
2022-01-17 11:00:28 +01:00
Rafał Dzięgiel
615f1553fb flatpak: Sync with Flathub 2022-01-17 10:19:29 +01:00
Rafał Dzięgiel
09c9e7560b New Crowdin updates (#201)
* New translations com.github.rafostar.Clapper.pot (Swedish)

* New translations com.github.rafostar.Clapper.pot (Russian)

* New translations com.github.rafostar.Clapper.pot (Chinese Simplified)

* New translations com.github.rafostar.Clapper.pot (Spanish)

* New translations com.github.rafostar.Clapper.pot (Spanish)

* New translations com.github.rafostar.Clapper.pot (Basque)
2022-01-17 08:56:59 +01:00
Rafał Dzięgiel
f40ce756ad Merge pull request #199 from majjejjam/master
change three dots to ellipsis
2022-01-16 14:10:49 +01:00
majjejjam
23d51be185 changed to ellipses in ui/clapper.ui 2022-01-15 18:20:13 +01:00
Rafał Dzięgiel
0db2a4a045 gstclapper: Fix plugin feature leak 2022-01-14 09:28:26 +01:00
Rafostar
469c06d22b gstclapper: Go into NULL state directly when stopped
Instead of waiting another 60 seconds for user, go into NULL state ASAP to free all resources and open devices.
2022-01-09 13:31:37 +01:00
Rafostar
3feaf225b5 gstclapper: Make tags writable before insert
Someone might be holding a copied media info in which case tag
list ref count is not 1. Use gst_tag_list_make_writable to
assure that ref count is 1, so we can insert more tags into the list.
2022-01-09 13:31:29 +01:00
majjejjam
fc8d881efd change three dots to ellipsis 2022-01-08 16:28:07 +01:00
Rafał Dzięgiel
ac76836fd7 Merge pull request #192 from Rafostar/yt-removal
YouTube code removal
2022-01-05 14:27:59 +01:00
Rafał Dzięgiel
f6a1aaf1dc Remove all YouTube code
It has been broken for quite some time. From now on this is gonna be left to handle for GStreamer plugins.
2021-12-22 08:12:55 +01:00
Rafał Dzięgiel
000dca82d9 flatpak: Sync with Flathub 2021-12-20 21:23:31 +01:00
Rafał Dzięgiel
7a508fef39 0.4.1 2021-12-20 11:33:19 +01:00
Rafał Dzięgiel
d465d9f150 Make floating window update its title earlier
Instead of waiting till animation finishes, update the window title right away when changing modes
2021-12-15 16:58:54 +01:00
Rafał Dzięgiel
5e4dfb322c Append "PiP" suffix to window title when in floating mode
We are gonna take advantage of this simple change elsewhere
2021-12-15 12:44:23 +01:00
Rafał Dzięgiel
0c561ab4b3 Also allow enabling gtuber code path via env
Having to use "gtuber" URI scheme might be inconvenient, so also allow to
whitelist it with "GST_PLUGIN_FEATURE_RANK=gtubersrc:300" env
2021-12-10 11:05:00 +01:00
Rafał Dzięgiel
46ce261524 widget: Make sure we have caps before logging them 2021-12-10 11:03:06 +01:00
Rafał Dzięgiel
50aac8cdd8 gstclapper: Merge global tags instead replacing them
There is no guarantee that received later tags also contain values from
earlier ones, as they might come from different element.
Combine them instead while replacing old values with newer ones.
2021-12-02 08:52:35 +01:00
Rafał Dzięgiel
810aea476f Use gtuber lib for URIs with "gtuber" scheme
Take a different code path when URI uses "gtuber" scheme.
This allows testing new WIP lib as an opt-in.
2021-11-29 22:33:53 +01:00
Rafał Dzięgiel
24905f1d60 flatpak: Build gtuber 2021-11-29 22:30:21 +01:00
Rafał Dzięgiel
82e3c9a52f prefs: Add can-swipe-back compat with latest libadwaita
Recent libadwaita has renamed "can-swipe-back" into "can-navigate-back".
Set both in JS code instead of UI file in order to support all libadwaita versions.

Fixes #185
2021-11-29 10:13:06 +01:00
Rafał Dzięgiel
654b8aaf60 prefs: Fix expander rows compat with latest libadwaita 2021-11-19 18:43:01 +01:00
Rafał Dzięgiel
3c0e33e4a4 css: Few override fixes for latest libadwaita 2021-11-19 18:40:34 +01:00
Rafał Dzięgiel
d2df1c3bd8 app: Use Adw.StyleManager to enable dark-theme
Latest libadwaita (for reasons unknown to me) totally ignores/breaks dark theme usage with gtk_application_prefer_dark_theme property. Lets just try using the new Adw.StyleManager without asking questions why.
2021-11-18 22:42:05 +01:00
Rafał Dzięgiel
af24073590 Update LINGUAS file 2021-11-18 15:37:48 +01:00
Rafał Dzięgiel
44cee14eb2 New translations com.github.rafostar.Clapper.pot (Portuguese) (#175) 2021-11-18 15:34:16 +01:00
Rafał Dzięgiel
b853685dd4 gstclapper: Fix decoder stream ID string leak 2021-10-22 13:11:42 +02:00
Rafał Dzięgiel
15461dd38a gstclapper: Fix video/audio decoder change detection
The video/audio decoder changed signal was not working correctly in case of
multiple streams with multiple decoders in single file.

We need to listen to the current-(video/audio) signal, when it changes find
corresponding "input-selector", get stream ID from its active pad and then
find the decoder in the pipeline that handles this stream ID. Similarly for
playbin3, but use stream ID from the "streams-selected" signal.
2021-10-22 11:05:08 +02:00
Rafał Dzięgiel
1c1989bc32 gstclapper: Fix GST_PLUGIN_FEATURE_RANK env usage
We change few default plugin ranks during init to whitelist them, but we do not update
their values from GST_PLUGIN_FEATURE_RANK env afterwards, so do that.
The ENV should be preferred over default config.
2021-10-21 11:21:07 +02:00
Rafał Dzięgiel
e9c9ae073f flatpak-nightly: Enable GPL gstreamer plugins 2021-10-19 10:23:41 +02:00
Rafał Dzięgiel
22d2c40b0f flatpak-nightly: Build from gstreamer mono repo 2021-10-18 11:13:44 +02:00
Rafał Dzięgiel
5301c8b368 flatpak: Sync with Flathub 2021-10-14 20:35:53 +02:00
Rafał Dzięgiel
e910978590 Update LINGUAS file
Install Portuguese-Brazilian translation
2021-10-14 20:30:18 +02:00
Rafał Dzięgiel
3b99f0ee0c New Crowdin updates (#172)
* New translations com.github.rafostar.Clapper.pot (Portuguese, Brazilian)
2021-10-12 08:44:14 +02:00
Rafał Dzięgiel
f93a0fed98 Update LINGUAS file 2021-10-04 16:06:45 +02:00
Rafał Dzięgiel
6b8c35b16e Merge pull request #166 from GNUuser/patch-1
Update es.po
2021-10-04 15:59:21 +02:00
Rafał Dzięgiel
5fdb6e64be New Crowdin updates (#164)
* New translations com.github.rafostar.Clapper.pot (Chinese Simplified)

* New translations com.github.rafostar.Clapper.pot (Italian)
2021-10-04 15:14:51 +02:00
Rafał Dzięgiel
60d4b5aec1 widget: Do gestures in capture phase
Capture phase runs from the toplevel down to the event widget and is meant to be used
in containers that might possibly handle events before their children, like our OSD layer.

This avoids an issue where pressing an OSD button does not emit cancelled signal,
accidentally triggering long press gesture signal handler.

Fixes #170
2021-10-04 10:11:53 +02:00
carlosgonz
f5258889cf Update es.po 2021-10-02 10:08:14 -04:00
carlosgonz
31eddf5dd2 Update es.po 2021-10-02 10:07:15 -04:00
carlosgonz
89f7304e0e Update es.po
initial Spanish file
2021-10-01 18:59:03 -04:00
Rafał Dzięgiel
3c918bda73 New Crowdin updates (#163)
* New translations com.github.rafostar.Clapper.pot (Hungarian)
2021-09-22 10:15:35 +02:00
Rafał Dzięgiel
2a8ef2559f New Crowdin updates (#162)
* New translations com.github.rafostar.Clapper.pot (Czech)
2021-09-20 08:34:28 +02:00
Rafał Dzięgiel
7676e18eee New translations com.github.rafostar.Clapper.pot (Russian) (#161) 2021-09-17 11:03:54 +02:00
Rafał Dzięgiel
00b05b460d Merge pull request #158 from igor-dyatlov/master
Added Russian translate
2021-09-17 10:58:05 +02:00
Igor Dyatlov
62f02cf2a3 Added Russian translate 2021-09-16 12:31:19 +03:00
Rafał Dzięgiel
9fc1d9b8c4 sink: Do not leak pool when set config fails 2021-09-15 16:28:23 +02:00
Rafał Dzięgiel
45a5ebb4b6 New Crowdin updates (#157)
* New translations com.github.rafostar.Clapper.pot (Romanian)

* New translations com.github.rafostar.Clapper.pot (Korean)

* New translations com.github.rafostar.Clapper.pot (Vietnamese)

* New translations com.github.rafostar.Clapper.pot (Chinese Traditional)

* New translations com.github.rafostar.Clapper.pot (Chinese Simplified)

* New translations com.github.rafostar.Clapper.pot (Ukrainian)

* New translations com.github.rafostar.Clapper.pot (Turkish)

* New translations com.github.rafostar.Clapper.pot (Swedish)

* New translations com.github.rafostar.Clapper.pot (Serbian (Cyrillic))

* New translations com.github.rafostar.Clapper.pot (Russian)

* New translations com.github.rafostar.Clapper.pot (Portuguese)

* New translations com.github.rafostar.Clapper.pot (Polish)

* New translations com.github.rafostar.Clapper.pot (Norwegian)

* New translations com.github.rafostar.Clapper.pot (Dutch)

* New translations com.github.rafostar.Clapper.pot (Japanese)

* New translations com.github.rafostar.Clapper.pot (French)

* New translations com.github.rafostar.Clapper.pot (Italian)

* New translations com.github.rafostar.Clapper.pot (Hungarian)

* New translations com.github.rafostar.Clapper.pot (Hebrew)

* New translations com.github.rafostar.Clapper.pot (Finnish)

* New translations com.github.rafostar.Clapper.pot (Greek)

* New translations com.github.rafostar.Clapper.pot (German)

* New translations com.github.rafostar.Clapper.pot (Danish)

* New translations com.github.rafostar.Clapper.pot (Czech)

* New translations com.github.rafostar.Clapper.pot (Catalan)

* New translations com.github.rafostar.Clapper.pot (Arabic)

* New translations com.github.rafostar.Clapper.pot (Afrikaans)

* New translations com.github.rafostar.Clapper.pot (Spanish)

* New translations com.github.rafostar.Clapper.pot (Portuguese, Brazilian)

* New translations com.github.rafostar.Clapper.pot (Catalan)

* New translations com.github.rafostar.Clapper.pot (Dutch)

* New translations com.github.rafostar.Clapper.pot (Polish)
2021-09-15 10:54:52 +02:00
Rafał Dzięgiel
2500a42286 Fix missing gio-unix-2.0 dep
Our autogenerated MPRIS code needs it. On some packaging systems this might be included outside of glib, so depend on it explicitly.
2021-09-14 17:54:03 +02:00
Rafał Dzięgiel
bafffa59b9 Fix missing translator-credits 2021-09-14 16:40:09 +02:00
Rafał Dzięgiel
201cb6f64c actions: Also allow mute with "M" key alone
For faster access
2021-09-14 13:09:23 +02:00
Rafał Dzięgiel
636098182b actions: Keep consistent letters case 2021-09-14 13:06:44 +02:00
Rafał Dzięgiel
5442b0b70a uri-dialog: Catch errors when reading from clipboard
The clipboard.read_text_finish method might throw an error when something goes wrong. Catch it and print in debug message. There is no alternative clipboard reading, so simply ignore.
2021-09-14 13:00:14 +02:00
Rafał Dzięgiel
67d1b9f5bc flatpak: Sync with Flathub 2021-09-13 14:06:48 +02:00
Rafostar
a62483fda9 0.4.0 2021-09-12 12:14:05 +02:00
Rafał Dzięgiel
c825385a8f New Crowdin updates (#154)
* New translations com.github.rafostar.Clapper.pot (Italian)

* New translations com.github.rafostar.Clapper.pot (Italian)
2021-09-11 12:19:01 +02:00
Rafał Dzięgiel
02a7419d66 Merge pull request #155 from Rafostar/scaling
Scale fullscreen differently depending on monitor res
2021-09-11 12:18:13 +02:00
Rafał Dzięgiel
e727b8fb50 Scale fullscreen differently depending on monitor res 2021-09-11 11:22:15 +02:00
Rafostar
3cbfd1a57c Fix chapter popover parenting 2021-09-10 19:28:27 +02:00
Rafał Dzięgiel
75b728457c New Crowdin updates (#150)
* New translations com.github.rafostar.Clapper.pot (Catalan)

* New translations com.github.rafostar.Clapper.pot (Catalan)

* New translations com.github.rafostar.Clapper.pot (Polish)

* New translations com.github.rafostar.Clapper.pot (Romanian)

* New translations com.github.rafostar.Clapper.pot (Korean)

* New translations com.github.rafostar.Clapper.pot (Vietnamese)

* New translations com.github.rafostar.Clapper.pot (Chinese Traditional)

* New translations com.github.rafostar.Clapper.pot (Chinese Simplified)

* New translations com.github.rafostar.Clapper.pot (Ukrainian)

* New translations com.github.rafostar.Clapper.pot (Turkish)

* New translations com.github.rafostar.Clapper.pot (Swedish)

* New translations com.github.rafostar.Clapper.pot (Serbian (Cyrillic))

* New translations com.github.rafostar.Clapper.pot (Russian)

* New translations com.github.rafostar.Clapper.pot (Portuguese)

* New translations com.github.rafostar.Clapper.pot (Polish)

* New translations com.github.rafostar.Clapper.pot (Norwegian)

* New translations com.github.rafostar.Clapper.pot (Dutch)

* New translations com.github.rafostar.Clapper.pot (Japanese)

* New translations com.github.rafostar.Clapper.pot (French)

* New translations com.github.rafostar.Clapper.pot (Italian)

* New translations com.github.rafostar.Clapper.pot (Hungarian)

* New translations com.github.rafostar.Clapper.pot (Hebrew)

* New translations com.github.rafostar.Clapper.pot (Finnish)

* New translations com.github.rafostar.Clapper.pot (Greek)

* New translations com.github.rafostar.Clapper.pot (German)

* New translations com.github.rafostar.Clapper.pot (Danish)

* New translations com.github.rafostar.Clapper.pot (Czech)

* New translations com.github.rafostar.Clapper.pot (Catalan)

* New translations com.github.rafostar.Clapper.pot (Arabic)

* New translations com.github.rafostar.Clapper.pot (Afrikaans)

* New translations com.github.rafostar.Clapper.pot (Spanish)

* New translations com.github.rafostar.Clapper.pot (Portuguese, Brazilian)

* New translations com.github.rafostar.Clapper.pot (Catalan)

* New translations com.github.rafostar.Clapper.pot (Dutch)

* New translations com.github.rafostar.Clapper.pot (Catalan)

* New translations com.github.rafostar.Clapper.pot (German)

* New translations com.github.rafostar.Clapper.pot (German)

* New translations com.github.rafostar.Clapper.pot (Polish)

* New translations com.github.rafostar.Clapper.pot (Polish)
2021-09-10 15:46:56 +02:00
Rafał Dzięgiel
b293a84df6 Skip hiding already hidden tracks radio buttons
Does not change much, but removes some useless logging
2021-09-09 22:08:20 +02:00
Rafał Dzięgiel
928244dc32 prefs: Show icon in plugins with modified feature ranks 2021-09-09 12:10:54 +02:00
Rafał Dzięgiel
11caafe847 css: Remove higher l/r negative margins
This ensures that scale does not overlap with corresponding buttons
2021-09-09 09:13:18 +02:00
Rafał Dzięgiel
dbeb4cb0b7 misc: Cleanup imports resolve code
It does not look like we will be able to provide gresources in near future (ever?). Remove those checks for less logic at startup.
2021-09-08 21:32:42 +02:00
Rafał Dzięgiel
3e04139288 meson: Fix variable name 2021-09-08 21:09:48 +02:00
Rafał Dzięgiel
c41b77a14b meson: Do not set datadir in bin template
GJS does this automatically for us using prefix and even if set it will be overwritten
2021-09-08 20:50:23 +02:00
Rafał Dzięgiel
3c500e0be9 meson: Rename some options 2021-09-08 20:46:43 +02:00
Rafał Dzięgiel
6cddb1a16d meson: Cleanup build files
Remove some twice declared variables and keep code style
2021-09-08 20:44:12 +02:00
Rafał Dzięgiel
b5007d12a1 remote: Use system play/pause icons
Those were taken from gresource, but since Remote is a different app, it does not use original clapper gresource. Use system icons for now.
2021-09-08 20:22:32 +02:00
Rafał Dzięgiel
0d7274fb03 Optimize TV mode controls size 2021-09-08 11:31:13 +02:00
Rafał Dzięgiel
94be4c08c5 Optimize pause icon height 2021-09-08 11:30:35 +02:00
Rafał Dzięgiel
bfcc468528 meson: Add optional validate appstream file test 2021-09-07 20:54:48 +02:00
Rafał Dzięgiel
98d2b80103 Add PiP and play icons as gresource
Add missing PiP icons from web devel kit. Also add play/pause
icons at the right size and remove all workarounds/scaling
implemented or these two.

Playback icons are always two bars for pause and triangle for play,
so hopefully this will not be problematic, as this guaranties the
right size for them that Adwaita unfortunately cannot provide.
2021-09-07 19:34:59 +02:00
Rafał Dzięgiel
fa39c45773 Remove useless signal connection in about dialog 2021-09-06 20:33:11 +02:00
Rafał Dzięgiel
b2a6e19fe1 Update POT file 2021-09-06 20:22:55 +02:00
Rafał Dzięgiel
e8c44dc400 Make end time label translatable 2021-09-06 20:21:18 +02:00
Rafał Dzięgiel
5cc9c17632 Update POT file 2021-09-06 19:47:29 +02:00
Rafał Dzięgiel
35d359daf9 Mark popovers text as translatable 2021-09-06 19:45:04 +02:00
Rafał Dzięgiel
637fa75484 Merge pull request #151 from Rafostar/comply-gjs
Comply with GJS packaging
2021-09-06 17:56:53 +02:00
Rafał Dzięgiel
8733610a9b Move revealers access away from base classes
Those should not appear in base classes, as they are available in main app only
2021-09-06 17:23:36 +02:00
Rafał Dzięgiel
873e724bf2 Comply with GJS packaging spec
According to spec, package name should always be set to main package name in DBus name format
2021-09-06 17:22:34 +02:00
Rafał Dzięgiel
7aa03f3e72 prefs: PipeWire is only used for audio output here
We only use PipeWire for audio output. Make that more obvious in prefs setting description.
2021-09-06 13:42:09 +02:00
Rafał Dzięgiel
99acd36cc0 api: Always start unmuted 2021-09-06 13:18:57 +02:00
Rafał Dzięgiel
cd7d99606d Improve mute shortcut description
This does both mute and unmute, describe it as a toggle
2021-09-06 13:07:07 +02:00
Rafał Dzięgiel
574e084383 Toggle mute with Ctrl+M
Closes #117
2021-09-06 12:34:11 +02:00
Rafał Dzięgiel
d49df86397 api: Replace mute-changed signal with mute notify
Notify is enough, we do not need a separate signal for mute property. This also allows binding this property to some other GObject props.
2021-09-06 12:18:32 +02:00
Rafał Dzięgiel
3483c94913 Completely disable audio track when set to disabled
Instead of just muting audio when disabled, tell GStreamer to diable it completely. This is slower, but makes this option do what it was supposed to.
2021-09-06 10:29:44 +02:00
Rafał Dzięgiel
a99abc652a Do not store subtitle-offset as gsetting
This is not something we should remember
2021-09-06 10:12:33 +02:00
Rafał Dzięgiel
a9173bb481 css: Put small L/R margins on speed scale
GTK does weird glitches when scale slider inside popover is dragged to the very end. Avoid that by putting small margins on both sides. It also looks better when its shorter a little bit.
2021-09-06 09:09:05 +02:00
Rafostar
a16444cefb Do not set subtitle-offset at startup
We should always start without any offset for subtitles
2021-09-05 20:44:12 +02:00
Rafostar
65673dbf4f Add window mapped debug message 2021-09-05 17:55:16 +02:00
Rafostar
dea67e4712 Prepare for resource loading
Cleanup the Clapper install path detection code in a way to make gresources loading eventually possible for compiled builds
2021-09-05 17:55:11 +02:00
Rafostar
3c765e9f17 Add some init debug messages
To get an overall idea how long initialization takes
2021-09-05 17:54:55 +02:00
Rafostar
a77eb5352e cleanup: Remove unused clapperVersion variable 2021-09-05 11:47:24 +02:00
Rafostar
ca168f34ed controls: Use isFullscreen and isMobile from main widget
The "controls.isMobile" was used incorrectly here as it holds value if
controls are in mobile mode, not if running on mobile screen.
Use those values from clapper main widget instead. This fixes incorrect
chapter popover placement on mobile screens.

Also handle fine-tune in CSS as this code path is hit multiple times
when starting/stopping slider drag.
2021-09-05 11:30:46 +02:00
Rafostar
d0304d4ded mpris: Allow reading properties 2021-09-04 18:57:08 +02:00
Rafał Dzięgiel
cd479f3bb4 api: Set fakesink to be sync
Otherwise seeking will not be possible when this sink is used
2021-09-03 20:32:17 +02:00
Rafał Dzięgiel
70942ba419 prefs: Mark experimental options
Options not fully reliable with possibility to break playback. Better mark them as experimental then listen to users complaining on daily basis.
2021-09-03 20:08:07 +02:00
Rafał Dzięgiel
ad0c6151a9 Merge pull request #149 from Rafostar/experimental-options
Add playbin3 and PipeWire options to prefs
2021-09-03 12:24:00 +02:00
Rafał Dzięgiel
cccc584961 api: Use fakesink when PipeWire enabled but unavailable
Instead of misleading users that they are using PipeWire after enabling it, use fakesink in order to not output any sound at all
2021-09-03 11:50:40 +02:00
Rafał Dzięgiel
38e721be03 prefs: Add playbin3 and pipewire options 2021-09-03 11:35:23 +02:00
Rafał Dzięgiel
cf6d41b725 api: Add property to control PipeWire enablement 2021-09-03 11:12:48 +02:00
Rafał Dzięgiel
b68c1a62b2 api: Add property to control playbin3 enablement 2021-09-03 10:59:44 +02:00
Rafał Dzięgiel
dc25ec976a Name our classes
In order to not end up with random names prefixed with Gjs_, give each class a proper name, so its easier to inspect and allows usage with UI files
2021-09-02 20:12:39 +02:00
Rafał Dzięgiel
914292612b Show libadwaita version in about system 2021-09-02 19:27:00 +02:00
Rafał Dzięgiel
e34b164f5a Auto fill open URI entry with clipboard content
If clipboard contains a valid URI, prefill the entry with it. Also select all of it to make it easier to remove this text.
2021-09-02 18:58:51 +02:00
Rafał Dzięgiel
2e0a455f92 dialogs: Use response virtual function instead of signal 2021-09-02 17:29:56 +02:00
Rafał Dzięgiel
3aab01d35c Improved open URI dialog 2021-09-02 17:22:09 +02:00
Rafał Dzięgiel
e264304c9d Mark text in remaining dialogs as translatable 2021-09-02 13:26:06 +02:00
Rafał Dzięgiel
a145890f9d Fix disabling plugin feature with zero rank 2021-09-01 17:53:23 +02:00
Rafał Dzięgiel
0323c816ad api: Use GstElementFactoryListType to detect decoders
Using simply GST_IS_VIDEO_DECODER is not enough as some special cases like vaapidecodebin are not using decoder base class
2021-09-01 17:41:31 +02:00
Rafał Dzięgiel
4f1d9be423 Fix case with missing subtitle tracks
In case when first video has a lot of subtitles, second video has only a few and third one more then second one, we forgot to make the recycled toggle buttons visible again, causing some tracks to be missing
2021-09-01 17:26:10 +02:00
Rafał Dzięgiel
82557df7c4 Merge pull request #148 from Rafostar/popovers-rework
Popovers rework
2021-09-01 17:18:03 +02:00
Rafał Dzięgiel
c457158541 css: Remove elapsed popover bottom padding
Scale already does a bottom padding, we do not need this much of it
2021-09-01 15:44:45 +02:00
Rafał Dzięgiel
451711b9c6 Show current video/audio decoder in popover 2021-09-01 14:28:15 +02:00
Rafał Dzięgiel
bbada8fb4e Redo controls popovers into GtkMenuButtons 2021-09-01 14:25:42 +02:00
Rafał Dzięgiel
04122d46a7 Move popover separator into .ui
Allows reusing a custom separator in multiple popovers
2021-08-31 10:55:25 +02:00
Rafał Dzięgiel
347d90b1ad actions: Fetch gnome-nightly repo for build 2021-08-29 09:34:28 +02:00
Rafał Dzięgiel
27ffb65d8f api: Remove unused declaration 2021-08-28 10:29:12 +02:00
Rafał Dzięgiel
9328a7af75 meson: Warn about unused variables 2021-08-28 10:27:44 +02:00
Rafał Dzięgiel
1f6e439632 New Crowdin updates (#146)
* New translations com.github.rafostar.Clapper.pot (Italian)

* New translations com.github.rafostar.Clapper.pot (German)

* New translations com.github.rafostar.Clapper.pot (Catalan)
2021-08-28 10:26:15 +02:00
Rafał Dzięgiel
97c1207b40 readme: Update flatpak-nightly badge 2021-08-28 08:43:06 +02:00
Rafał Dzięgiel
833559a208 flatpak: Rename from testing to nightly and use master runtime 2021-08-28 08:42:18 +02:00
Rafał Dzięgiel
9a8655b0a2 meson: Use correct SPDX identifier 2021-08-28 08:28:40 +02:00
Rafał Dzięgiel
d2a03d40a3 Merge pull request #145 from Rafostar/actions-aarch64
Add aarch64 builds to git actions
2021-08-27 21:42:31 +02:00
Rafał Dzięgiel
66998ff6c6 actions: Install docker with arm64 quemu
Flatpak builder needs this to compile aarch64 package
2021-08-27 17:02:40 +02:00
Rafał Dzięgiel
7cdd905d73 actions: Do not fail both runs when one fails 2021-08-27 16:25:02 +02:00
Rafał Dzięgiel
10eb5102c1 actions: Also build for aarch64 in nightly flatpak 2021-08-27 16:24:55 +02:00
Rafał Dzięgiel
9e8854c5e2 actions: Use GNOME nightly repo and rename to nightly
Rename flatpak-testing into flatpak-nightly so it reflects that it uses GNOME nightly sources now and builds itself every night (with nightly gstreamer too).
2021-08-27 16:21:57 +02:00
Rafał Dzięgiel
16a9546c97 actions: Build aarch64 flatpak 2021-08-27 15:51:21 +02:00
Rafał Dzięgiel
2686c2ca03 Quietly handle ranking gsetting change after app update
Do not print errors for everyone who updates Clapper from 0.3.0 version.
How plugin ranking is stored in gsettings changed. Instead reset it quietly
into new defaults to avoid problems.

Also since now GstClapper API sets some default ranks based on installed
GStreamer version, hopefully this ranking will be only an edge case for
people who want to experiment or need to test/debug some GStreamer plugin.
2021-08-27 13:30:14 +02:00
Rafał Dzięgiel
ad563d16d0 Move init functions into main where they should be
It seems that libadwaita does not like being initialized too early. Move the init function into the beggining of main() to make it happy.
2021-08-27 12:56:58 +02:00
Rafał Dzięgiel
12ce95b664 rpm: Install translations 2021-08-26 21:14:19 +02:00
Rafał Dzięgiel
2dbabe52bb Update LINGUAS file 2021-08-26 20:40:21 +02:00
Rafał Dzięgiel
2c35ceb35a New Crowdin updates (#141)
* New translations com.github.rafostar.Clapper.pot (Italian)

* New translations com.github.rafostar.Clapper.pot (Italian)

* New translations com.github.rafostar.Clapper.pot (Italian)

* New translations com.github.rafostar.Clapper.pot (Dutch)

* New translations com.github.rafostar.Clapper.pot (Dutch)
2021-08-26 20:20:59 +02:00
Rafał Dzięgiel
5eac1aa675 Merge pull request #140 from sp1ritCS/local_de
l10n: added german localization
2021-08-26 20:15:19 +02:00
Florian "sp1rit"​
0e6a68665d Merge remote-tracking branch 'origin/master' into local_de 2021-08-26 20:04:41 +02:00
Rafał Dzięgiel
86fe1628c3 Merge pull request #139 from juxuanu/master
Translate to Catalan
2021-08-26 19:36:48 +02:00
Rafał Dzięgiel
a427817e53 rpm: Install clapper symlink 2021-08-26 19:23:52 +02:00
Rafał Dzięgiel
7a767c34eb rpm: Fix bogus date 2021-08-26 19:20:19 +02:00
Rafał Dzięgiel
a7298e371e meson: Use DESTDIR env when creating symlink 2021-08-26 19:06:10 +02:00
Rafał Dzięgiel
f6393a177d Merge pull request #143 from Rafostar/symlink
meson: Create symlink to bin named simply "clapper"
2021-08-26 18:17:05 +02:00
Rafał Dzięgiel
45decb8563 meson: Create symlink to bin named simply "clapper" 2021-08-26 18:04:57 +02:00
Florian "sp1rit"​
2e12e52730 l10n: added german localization 2021-08-26 13:06:08 +02:00
Ícar
ffbd8c35e1 Update LINGUAS for Catalan 2021-08-26 10:54:59 +02:00
Ícar
01277f4a71 Add Catalan translation 2021-08-26 10:54:23 +02:00
Rafał Dzięgiel
a54c3e4bcc New Crowdin updates (#136)
* New translations com.github.rafostar.Clapper.pot (Polish)

* New translations com.github.rafostar.Clapper.pot (Romanian)

* New translations com.github.rafostar.Clapper.pot (Korean)

* New translations com.github.rafostar.Clapper.pot (Vietnamese)

* New translations com.github.rafostar.Clapper.pot (Chinese Traditional)

* New translations com.github.rafostar.Clapper.pot (Chinese Simplified)

* New translations com.github.rafostar.Clapper.pot (Ukrainian)

* New translations com.github.rafostar.Clapper.pot (Turkish)

* New translations com.github.rafostar.Clapper.pot (Swedish)

* New translations com.github.rafostar.Clapper.pot (Serbian (Cyrillic))

* New translations com.github.rafostar.Clapper.pot (Russian)

* New translations com.github.rafostar.Clapper.pot (Portuguese)

* New translations com.github.rafostar.Clapper.pot (Polish)

* New translations com.github.rafostar.Clapper.pot (Norwegian)

* New translations com.github.rafostar.Clapper.pot (Dutch)

* New translations com.github.rafostar.Clapper.pot (Japanese)

* New translations com.github.rafostar.Clapper.pot (French)

* New translations com.github.rafostar.Clapper.pot (Italian)

* New translations com.github.rafostar.Clapper.pot (Hungarian)

* New translations com.github.rafostar.Clapper.pot (Hebrew)

* New translations com.github.rafostar.Clapper.pot (Finnish)

* New translations com.github.rafostar.Clapper.pot (Greek)

* New translations com.github.rafostar.Clapper.pot (German)

* New translations com.github.rafostar.Clapper.pot (Danish)

* New translations com.github.rafostar.Clapper.pot (Czech)

* New translations com.github.rafostar.Clapper.pot (Catalan)

* New translations com.github.rafostar.Clapper.pot (Arabic)

* New translations com.github.rafostar.Clapper.pot (Afrikaans)

* New translations com.github.rafostar.Clapper.pot (Spanish)

* New translations com.github.rafostar.Clapper.pot (Portuguese, Brazilian)
2021-08-25 21:47:28 +02:00
Rafostar
17b2aec170 YouTube name should not be translated 2021-08-25 17:14:12 +02:00
Rafał Dzięgiel
75bc0d6b69 Update README.md 2021-08-25 15:28:03 +02:00
Rafał Dzięgiel
edef26fc0e New Crowdin updates (#135)
* New translations com.github.rafostar.Clapper.pot (Romanian)

* New translations com.github.rafostar.Clapper.pot (Korean)

* New translations com.github.rafostar.Clapper.pot (Vietnamese)

* New translations com.github.rafostar.Clapper.pot (Chinese Traditional)

* New translations com.github.rafostar.Clapper.pot (Chinese Simplified)

* New translations com.github.rafostar.Clapper.pot (Ukrainian)

* New translations com.github.rafostar.Clapper.pot (Turkish)

* New translations com.github.rafostar.Clapper.pot (Swedish)

* New translations com.github.rafostar.Clapper.pot (Serbian (Cyrillic))

* New translations com.github.rafostar.Clapper.pot (Russian)

* New translations com.github.rafostar.Clapper.pot (Portuguese)

* New translations com.github.rafostar.Clapper.pot (Polish)

* New translations com.github.rafostar.Clapper.pot (Norwegian)

* New translations com.github.rafostar.Clapper.pot (Dutch)

* New translations com.github.rafostar.Clapper.pot (Japanese)

* New translations com.github.rafostar.Clapper.pot (French)

* New translations com.github.rafostar.Clapper.pot (Italian)

* New translations com.github.rafostar.Clapper.pot (Hungarian)

* New translations com.github.rafostar.Clapper.pot (Hebrew)

* New translations com.github.rafostar.Clapper.pot (Finnish)

* New translations com.github.rafostar.Clapper.pot (Greek)

* New translations com.github.rafostar.Clapper.pot (German)

* New translations com.github.rafostar.Clapper.pot (Danish)

* New translations com.github.rafostar.Clapper.pot (Czech)

* New translations com.github.rafostar.Clapper.pot (Catalan)

* New translations com.github.rafostar.Clapper.pot (Arabic)

* New translations com.github.rafostar.Clapper.pot (Afrikaans)

* New translations com.github.rafostar.Clapper.pot (Spanish)

* New translations com.github.rafostar.Clapper.pot (Portuguese, Brazilian)
2021-08-25 15:02:02 +02:00
Rafał Dzięgiel
0edab83f86 Update POT file 2021-08-25 14:39:07 +02:00
Rafał Dzięgiel
9681d0eea1 ui: Change letter case
Should be lowercase as the rest
2021-08-25 14:38:25 +02:00
Rafał Dzięgiel
b6559ee50f Sync shortcuts names with menu items
This option was renamed recently
2021-08-25 14:29:05 +02:00
Rafał Dzięgiel
ce183595cd readme: Add crowdin "localized" badge 2021-08-25 13:51:14 +02:00
Rafał Dzięgiel
71f50ba414 Revert "crowdin: Add some languages mappings"
This reverts commit 0de8ee2d54.
2021-08-25 13:28:49 +02:00
Rafał Dzięgiel
0de8ee2d54 crowdin: Add some languages mappings 2021-08-25 12:58:25 +02:00
Rafał Dzięgiel
8be49f25b4 Update Crowdin configuration file 2021-08-25 12:36:58 +02:00
Rafał Dzięgiel
69bef3d174 Update Crowdin configuration file 2021-08-25 12:26:59 +02:00
Rafał Dzięgiel
d429e9105c Update Crowdin configuration file 2021-08-25 12:25:12 +02:00
Rafał Dzięgiel
a1ce04edfa Initial translations support
Most of the app is now translatable, so put a POT file in git and make app use translations if available
2021-08-25 11:23:43 +02:00
Rafał Dzięgiel
8ca4c30beb Rename "Open Local" into "Open Files"
This is easier to understand, besides file chooser might show online filesystem mounts which makes open "local" an inaccurate name
2021-08-24 22:05:20 +02:00
Rafał Dzięgiel
9b53a3e387 api: Fix plugin feature unref 2021-08-24 20:52:18 +02:00
Rafał Dzięgiel
f31682624f Do not try to translate "GStreamer" name 2021-08-24 15:49:45 +02:00
Rafał Dzięgiel
5874db45ea api: Introduce custom gstreamer init function
Initializes GStreamer library and tweaks some of its defaults to our liking.
This also allows usage of GstClapper API alone without importing GStreamer on the implementation side.

It changes preferred hardware acceleration order (depending on plugins versions) into:
v4l2sl -> nv -> va -> vaapi -> v4l2
2021-08-24 14:15:29 +02:00
Rafał Dzięgiel
87a0dd473e flatpak: Add missing deps to testing manifest
These are required to build libadwaita
2021-08-24 09:26:33 +02:00
Rafał Dzięgiel
c50df5f2a3 Merge pull request #129 from Rafostar/libadwaita
Initialize with libadwaita, port preferences window to be adaptive and do few CSS tweaks to improve overall app look.
2021-08-23 18:33:21 +02:00
Rafał Dzięgiel
a72a8057e5 rpm: Require libadwaita 2021-08-23 17:13:36 +02:00
Rafał Dzięgiel
f29ec78b92 Update readme 2021-08-23 16:55:56 +02:00
Rafał Dzięgiel
10f54c7c3d Remove .deb package
This was unresolvable (and reportedly broken) for quite some time, now even more so with libadwaita added into the mix. Having .deb packages would require a custom PPA with up-to-date dependencies. Anyone interested is free to set it up, but this git repo is not a good place to do so.
2021-08-23 16:18:13 +02:00
Rafał Dzięgiel
2b91056543 Remove Arch PKGBUILD files from git
Both stable and git versions of Clapper are available in AUR, no point maintaining them in two places
2021-08-23 16:11:09 +02:00
Rafał Dzięgiel
2656762cbc Fix chapter popover coordinates 2021-08-23 15:57:11 +02:00
Rafostar
f5ff59f786 css: Remove custom preferences CSS classes
Those are not used anymore
2021-08-22 16:09:45 +02:00
Rafał Dzięgiel
e4fe7844c4 metainfo: Mark as mobile friendly and fix few typos 2021-08-22 16:09:41 +02:00
Rafał Dzięgiel
b7d9a22d33 flatpak: Build libadwaita 2021-08-22 16:09:36 +02:00
Rafał Dzięgiel
c8a5277908 Port preferences window to libadwaita 2021-08-22 16:09:23 +02:00
Rafał Dzięgiel
62fab289b7 Few small CSS optimizations for new Adwaita theme 2021-08-06 09:11:39 +02:00
Rafał Dzięgiel
56ca13b370 Remove GTK 4.0.2+ theme workarounds 2021-08-06 09:11:34 +02:00
Rafał Dzięgiel
ce46905319 css: Remove slider outline in TV mode 2021-08-06 09:11:28 +02:00
Rafał Dzięgiel
5fe5f89e5d css: Reduce sliders size
They are way too big in new Adwaita theme for windowed mode, but leave them bigger when in TV mode
2021-08-06 09:11:20 +02:00
Rafał Dzięgiel
9ba6f5d09e Apply "osd" CSS class to each button in headerbar
Otherwise it does not work correctly with new Adwaita theme
2021-08-06 09:11:14 +02:00
Rafał Dzięgiel
4c0fc1dac2 Init libadwaita 2021-08-06 09:11:06 +02:00
Rafał Dzięgiel
13a792c0a9 Remove Adwaita scale brightness increase
This was fixed inside Adwaita theme
2021-08-06 09:10:58 +02:00
Rafał Dzięgiel
02b0d5f03f Remove theme selection workaround
Libadwaita does not support any other stylesheets then Adwaita. Not my fault.
2021-08-06 09:10:44 +02:00
Rafał Dzięgiel
b577dd762d Fix D&D on GTK 4.3.2+ 2021-08-05 22:29:23 +02:00
Rafał Dzięgiel
d798539dce Merge pull request #128 from Rafostar/gles
Cleanup GL init and use GLES if GTK is using it
2021-08-01 13:47:24 +02:00
Rafostar
19e159d1bd sink: Cleanup GL context init code 2021-08-01 12:44:35 +02:00
Rafał Dzięgiel
c3cdabb050 Merge pull request #127 from Rafostar/show-decoders
API: Add video/audio decoder changed signals
2021-08-01 11:02:28 +02:00
Rafał Dzięgiel
c53cde4d78 Merge pull request #126 from Rafostar/gestures
Add side double tap gesture and update shortcuts window
2021-08-01 10:29:08 +02:00
Rafał Dzięgiel
b308ae367f sink: When GTK is using GLES on EGL, use it too
If for some reason GTK ended up with using EGL with GLES instead of OpenGL and user
did not specify anything other, use it by default in gstreamer part of the app too.
2021-07-28 18:00:08 +02:00
Rafostar
b2005fd34f Describe gestures in shortcuts window 2021-07-28 17:09:32 +02:00
Rafostar
a6dfeeca37 Switch playlist items via double tap on screen side
Double press left side of the touchscreen to play previous item, right side to play next one. #93
2021-07-28 12:06:18 +02:00
Rafostar
90844e49ad API: Add video/audio decoder changed signals 2021-07-28 12:02:52 +02:00
Rafostar
eb60d036db Update PKGBUILD files 2021-07-20 13:44:03 +02:00
Rafał Dzięgiel
6e20a5fa12 Update README.md 2021-07-20 11:25:44 +02:00
Rafał Dzięgiel
cf370961b4 Merge pull request #106 from mattblaha/cleanup-readme
Grammatical cleanups in readme
2021-07-19 20:51:46 +02:00
Matt Blaha
6db36d6ac2 Align build/built with the project description on github, several other small grammatical cleanups. 2021-07-19 12:14:12 -05:00
Rafostar
4738673f7f Fix bottom OSD margins on multi monitors setup
When player enters high resolution display, margins are being recalculated,
but when it goes back dragged to some lower resolution screen they are not.
This is wrong, as they should be reset back to the initial value which is zero.

Fixes #102
2021-07-19 12:43:01 +02:00
Rafostar
1004000ba2 Leave fullscreen with "Esc" key 2021-07-18 15:34:41 +02:00
Rafał Dzięgiel
e40f11def5 sink: Small code cleanup 2021-07-16 23:50:26 +02:00
Rafał Dzięgiel
5378facb46 Add long press gesture to toggle play/pause #93
Press and hold on touchscreen while in fullscreen to toggle between play and pause
2021-07-16 18:31:26 +02:00
Rafał Dzięgiel
c6b252f261 Reduce icon height a little #92
Make icon a tiny bit smaller in height. This also makes the play logo a little more centered vertically.
2021-07-15 11:24:16 +02:00
Rafał Dzięgiel
0f1447b14e sink: Check navigation state with a lock held
This happens as callback from GTK thread, so its not thread safe
2021-07-13 19:57:31 +02:00
Rafał Dzięgiel
cfd6e1bd23 Merge pull request #91 from bridadan/mobile_youtube_urls
Parse mobile youtube URLs
2021-07-12 21:35:03 +02:00
Brian Daniels
0cb89d3a84 Parse mobile youtube URLs.
Add support for youtube URLs with the format m.youtube.com?v=<video id>
2021-07-12 15:09:39 -04:00
Rafał Dzięgiel
3bf636d46b sink: Do not send navigation events when not playing
Before playback starts our video display size is zero, no point in sending navigation events in such case.
Latest gstreamer git prints criticals in such case.

Also do not send mouse navigaion when element is paused and skip keypresses when below paused state.
2021-07-08 20:33:12 +02:00
Rafał Dzięgiel
8f79a76a51 flatpak: Update metainfo description
Clapper was tested and confirmed to work with V4L2 stateless decoders. Also since latest GTK4 version moved to EGL on Xorg we no longer have the GLX related problems we used to have.
2021-07-06 10:00:28 +02:00
Rafał Dzięgiel
0efe9144d8 Merge pull request #90 from Rafostar/external-oes
sink: Support rendering external-oes textures
2021-07-05 22:31:12 +02:00
Rafał Dzięgiel
2e776ddf7b sink: Support rendering external-oes textures
This significantly improves performance on mobile devices by
removing the texture copy from an OES texture to 2D texture.
2021-07-05 22:08:51 +02:00
Rafostar
2eea3b05b1 flatpak: Include run number in testing github action 2021-06-27 11:04:06 +02:00
Rafał Dzięgiel
e647cdc8c6 Update README.md 2021-06-25 09:18:56 +02:00
Rafał Dzięgiel
1f3ddab566 flatpak: Fix duplicated cache key name 2021-06-25 08:56:07 +02:00
Rafał Dzięgiel
a38480c46b Merge pull request #88 from Rafostar/workflow
Add Flatpak workflows
2021-06-24 20:31:17 +02:00
Rafał Dzięgiel
562587846a Add flatpak testing workflow
Builds nightly with latest GStreamer from git (no patches applied)
2021-06-24 19:45:15 +02:00
Rafał Dzięgiel
c6cd829e7b Add flatpak workflow
Builds on push/pull with Flathub patches
2021-06-24 19:44:52 +02:00
Rafał Dzięgiel
9b31581aac flatpak: Add app testing manifest version
The main difference is that it builds latest GStreamer from git master and uses
runtime provided version of GTK4 (both without any patches applied).

This is done for testing purposes or finding regressions.
2021-06-24 16:55:53 +02:00
Rafał Dzięgiel
5018b3a28a Use new PiP icons when available
Use the new "pip-in" and "pip-out" GNOME symbolic icons if user theme has them for enter/leave floating mode button. When icon is unavailable on the host, old icon will be used instead.
2021-06-23 14:24:59 +02:00
Rafostar
bad836cc6d flatpak: Fix wrong module path 2021-06-20 15:50:21 +02:00
Rafał Dzięgiel
e1845a212f Merge pull request #87 from Rafostar/pipewire
Add PipeWire support
2021-06-20 15:46:03 +02:00
Rafostar
f86e32ed88 flatpak: Sync with Flathub 2021-06-20 15:17:15 +02:00
Rafostar
af2c74a4b1 flatpak: Support PipeWire GStreamer plugins
We need to allow PipeWire socket access and symlink the pipewire
plugin to the dir where our custom GStreamer installation is.
2021-06-20 14:44:49 +02:00
Rafostar
1c414ef36b API: allow using PipeWire for audio output
Instead of using PulseAudio sink and making a round way through pipewire-pulse,
access and use PipeWire directly for audio output when user sets env var
GST_CLAPPER_USE_PIPEWIRE=1
2021-06-20 11:27:43 +02:00
Rafał Dzięgiel
11e89dab26 Update README.md 2021-06-18 21:35:15 +02:00
Rafał Dzięgiel
37d2f7aebd 0.3.0 2021-06-18 15:04:01 +02:00
Rafał Dzięgiel
f2ac3b20a3 meson: Install new symbolic icon 2021-06-18 10:28:25 +02:00
Rafał Dzięgiel
ade60b93a4 Merge pull request #86 from SeaDve/master
Add symbolic icon
2021-06-18 10:25:42 +02:00
Dave Patrick
48bc96f074 Add symbolic icon 2021-06-18 08:59:46 +08:00
Rafał Dzięgiel
0d9cb91705 Update TODO.md 2021-06-17 17:20:42 +02:00
Rafał Dzięgiel
21ccab1cc2 Add option to keep showing last video frame after playback
Previously Clapper showed last frame, now it defaults to black screen, so add an option for users to choose what they like better
2021-06-16 15:34:36 +02:00
Rafał Dzięgiel
bea3b1670d API: Raise ignored duration changes to 250 milliseconds
We do not show milliseconds in GUI, so we should not try to handle stream gaps that short.
2021-06-16 11:07:50 +02:00
Rafał Dzięgiel
0d4d3f1a8c sink: Avoid props code duplication 2021-06-16 09:43:55 +02:00
Rafał Dzięgiel
fc525ffcb1 sink: EGL on X11 from GTK 4.3.1
Recent GTK 4.3.1 release already uses EGL on x11, so lower the version check to that version
2021-06-15 16:52:47 +02:00
Rafał Dzięgiel
6f1a5626bc API: get MPRIS with a lock
Otherwise it is not thread safe
2021-06-15 16:16:29 +02:00
Rafał Dzięgiel
fbe6a8804c yt: Set some initial player version
It does not have to be up-to-date and even if it fails, we have a fallback that will update it anyway
2021-06-08 16:54:20 +02:00
Rafał Dzięgiel
c0b92c190b mpris: Fix wrong object type
Fix a copy-paste bug
2021-06-05 22:24:26 +02:00
Rafał Dzięgiel
8fc64eaf73 sink: Automatically draw black when going to NULL state
Remove all workarounds including ignore_textures prop and draw black functions and handle that automatically in sink when going into NULL state.
2021-06-02 20:56:50 +02:00
Rafał Dzięgiel
0b7f31b7c2 API: Properly return a boolean instead of a number 2021-06-02 20:24:40 +02:00
Rafał Dzięgiel
2d4353aaec Append some common subtitle track titles
In order to not end up with multiple subitle tracks simply named for e.g. "English", add some common postfix to it when detected in track title.
2021-06-02 15:47:09 +02:00
Rafał Dzięgiel
7062af472b API: Add function to get subtitle track title 2021-06-02 11:08:30 +02:00
Rafał Dzięgiel
1f4698448a Detect used GStreamer plugin names
Allows seeing what plugins are used with GST_DEBUG=Clapper:4. This is also needed for yet to come functionality of setting elements props.
2021-06-01 21:57:14 +02:00
Rafał Dzięgiel
95c8316af6 Change Enter key help description
OSD makes more sense to users then controls as it also shows top title and time layers
2021-06-01 08:31:02 +02:00
Rafał Dzięgiel
06d9f302c2 Revert "sink: Use g_main_context_invoke_full for drawing"
Not much benefit from using this function and unlike g_idle_add_full it
does not allow to skip a frame when previous one was not finished (slow HW).

This reverts commit f8a7abe195.
2021-05-31 18:07:37 +02:00
Rafał Dzięgiel
6246777f06 Prefer custom title over media info one
Otherwise YT videos will all show filename of dash manifest as title
2021-05-31 17:46:19 +02:00
Rafostar
1f781716d7 Add env variable to display FPS 2021-05-30 11:14:51 +02:00
Rafał Dzięgiel
0d7ef22c88 Merge pull request #77 from Rafostar/mpris
Add MPRIS
2021-05-26 22:01:49 +02:00
Rafał Dzięgiel
57664f32da sink: Do not send the same cursor coords on each GUI redraw 2021-05-26 21:15:58 +02:00
Rafał Dzięgiel
f8a7abe195 sink: Use g_main_context_invoke_full for drawing
Queue draws to application (and GTK) main context using g_main_context_invoke_full method
2021-05-26 20:59:50 +02:00
Rafostar
5f259b28fe mpris: Add "SupportedUriSchemes" and handle "OpenUri" method 2021-05-26 15:14:28 +02:00
Rafostar
9f776e9ecb mpris: Support changing volume 2021-05-26 15:13:30 +02:00
Rafał Dzięgiel
edb799bafa API: Parse title from URI when no title in tags 2021-05-24 15:35:04 +02:00
Rafał Dzięgiel
7535c4e598 mpris: Support position reporting and seeking 2021-05-24 15:34:47 +02:00
Rafał Dzięgiel
f0475ee055 API: Support seeking by offset 2021-05-24 15:33:52 +02:00
Rafał Dzięgiel
68d7205ead mpris: Support metadata url, title and length 2021-05-24 15:33:38 +02:00
Rafał Dzięgiel
f08ffad178 Initial MPRIS support
Implement a working MPRIS DBus connection with a separate API to control it. Right now only player playback state is reflected and Play/Pause/PlayPause calls work.
2021-05-24 15:33:15 +02:00
Rafał Dzięgiel
c2de0b7b33 yt: Use "html5=1" request query string 2021-05-24 13:04:10 +02:00
Rafał Dzięgiel
ac7be5956c Update Flathub submodule 2021-05-20 18:55:17 +02:00
Rafał Dzięgiel
76a1efab58 flatpak: Build from local dir instead of git
Allows doing test builds with unmerged changes
2021-05-20 17:27:47 +02:00
Rafał Dzięgiel
a2bbd2708d sink: Support EGL under x11 with GTK 4.4+ 2021-05-14 18:11:58 +02:00
Rafał Dzięgiel
9e77660cac Mark extras dir contents as linguist-vendored 2021-05-13 21:28:35 +02:00
Rafał Dzięgiel
5ea22450c0 Use custom getUriProtocol function
Gst.Uri.get_protocol function is very simple. It just splits string by ":" and return the first part. We can do the same in JS and by doing that we do not have to initialize GStreamer just to get this function.
2021-05-13 21:24:28 +02:00
Rafał Dzięgiel
6ae38327ca Leave CSS fullscreen optimization applied
Do not apply and remove fullscreen optimization class when going/leaving fullscreen. Instead set it to be applied to fullscreen only with CSS.
2021-05-13 21:13:18 +02:00
Rafał Dzięgiel
9006e56534 Add "text/x-ssa" to list of known subtitle mimes
GStreamer does not do external .ass subs ATM, but we should treat them as subtitles anyway.
2021-05-13 21:01:11 +02:00
Rafał Dzięgiel
af0e082c43 Readapt to changed monitor on the fly
Check and apply/remove TV mode UI on the fly when switching monitors.
This allows for e.g. having a mobile device connected to external big screen,
drag Clapper window from one screen to another and UI should automatically
adapt between mobile and TV modes without interrupting playback.

This also helps in situations where monitor size is not initially known
on window map #74.
2021-05-12 15:31:15 +02:00
Rafostar
2f5d6d60ed API: add debug messages about dropped buffers 2021-05-06 14:34:08 +02:00
Rafał Dzięgiel
9d537c7318 Update TODO.md 2021-05-04 19:05:46 +02:00
Rafostar
970b1487ac Restore manual play call
Autoplay was causing some racy conditions when loaded with subtitle uri. Make it play after uri loaded signal, but still prevent going from stopped to play 2nd time.
2021-05-04 18:46:22 +02:00
Rafostar
fc51fd857c Fix missing actions shortcuts in main menu
Actions were loaded too early, causing GTK to not recognize them and show their keyboard shortcuts next to main menu items
2021-05-03 19:26:22 +02:00
Rafostar
b419ed7922 Add keyboard shortcuts window 2021-05-03 19:26:06 +02:00
Rafostar
4f69183b85 Flatpak: use libs and patches from Flathub as submodule
I do not want to maintain them in two different places
2021-05-03 14:12:05 +02:00
Rafostar
98df55b231 Do not forget to destroy file chooser 2021-05-02 22:20:51 +02:00
Rafostar
6a4f5f2560 Export playlist to file with Ctrl+E 2021-05-02 20:25:15 +02:00
Rafostar
efe9439633 Do not ref all dialogs
The bug that we try to workaround here affects only file chooser dialog and not every dialog, so do not increase ref count on them
2021-05-02 17:29:15 +02:00
Rafostar
4179176ce8 Sink: queue render on GTK settings change
We have turned off auto rendering in video widget, hence we need to manually refresh when user changes some GTK settings (theme, icons etc.)
2021-05-02 15:51:14 +02:00
Rafostar
a7288adf4c Do not check Ids length in shuffle repeat mode
We always have at least 2 playlist items, thus at least 1 Id in array when we reach this point in code
2021-05-01 12:27:56 +02:00
Rafostar
9bb3f999b1 Also seek to 0 for other repeat modes with 1 item playlists 2021-05-01 12:15:07 +02:00
Rafostar
0e6507682a Do not show visualizations button when no audio tracks 2021-05-01 12:11:16 +02:00
Rafał Dzięgiel
2d8471dea0 Add playlist shuffle repeat mode #52 2021-04-29 12:26:40 +02:00
Rafał Dzięgiel
68f49c1495 Replace media playlist playing icon with repeat button #52
Show current and toggle change of repeat mode inside the playlist popover. The previous currently playing icon did not reflect actual playing state, so this should be better and does not take more space in UI.
2021-04-29 11:44:07 +02:00
Rafał Dzięgiel
a8bb6c40f4 Add more WebSocket API actions 2021-04-28 12:40:27 +02:00
Rafał Dzięgiel
71db78a0f6 Use "window.close" action instead of "close-request" 2021-04-28 12:15:20 +02:00
Rafał Dzięgiel
4133557086 Do not send or apply undefined args over WebSocket 2021-04-28 11:50:14 +02:00
Rafał Dzięgiel
d926e6b389 Add keybinding to change repeat mode #52 2021-04-27 15:25:22 +02:00
Rafał Dzięgiel
fd2de8b9b6 Add repeat mode options to playlist #52 2021-04-27 14:50:54 +02:00
Rafał Dzięgiel
de65eee106 API: simplify playbin flags detect function 2021-04-27 12:28:59 +02:00
Rafał Dzięgiel
9b07ff7dc5 Mention that packages from my repo are unstable 2021-04-27 11:23:42 +02:00
Rafał Dzięgiel
047dd12fbb Restore initial GUI state after playback 2021-04-27 11:14:09 +02:00
Rafał Dzięgiel
3238270c0d Ignore duration changes below 1ms during playback 2021-04-27 10:43:13 +02:00
Rafał Dzięgiel
997e47b93c API: let client decide what to do on EOS #52 2021-04-27 09:24:13 +02:00
Rafał Dzięgiel
ec1d4619a7 API: make "state" into a property 2021-04-26 22:30:01 +02:00
Rafał Dzięgiel
f4e48c9f8c Sink: render black on READY_TO_NULL state change
Show black background when playback finishes
2021-04-26 20:42:00 +02:00
Rafał Dzięgiel
1da6b94efc API: simplify EOS handling
Do not try to play smart with EOS by seeking to beginning. This leads to various errors or crashes. Just signal it and stop afterwards.
2021-04-26 20:40:44 +02:00
Rafał Dzięgiel
e4335721be Simplify auto-fullscreen logic 2021-04-26 17:51:20 +02:00
Rafał Dzięgiel
45d2702e01 API: fix missing drop of signals inhibit 2021-04-26 17:36:28 +02:00
Rafał Dzięgiel
a8aca7b3c0 API: make it autoplay on the same context invoke 2021-04-26 14:47:42 +02:00
Rafał Dzięgiel
c6e8824e3b API: add toggle_play method 2021-04-26 14:21:33 +02:00
Rafostar
e92ad68220 Print a warning when plugin rank cannot be changed 2021-04-25 22:00:00 +02:00
Rafostar
a98ca53dfb Use Gio.SimpleAction as only keypress handler 2021-04-25 20:19:44 +02:00
Rafał Dzięgiel
32995fc6a6 Sort chapters arr when switching to prev/next one
TOC representation obtained for some video files might be out of order. Sort them when switching between chapers, otherwise "next" chapter might not be the nearest one.
2021-04-22 15:48:16 +02:00
Rafał Dzięgiel
6b5240ddbc Add missing return value 2021-04-22 15:35:16 +02:00
Rafał Dzięgiel
46ef6bcd1d Use "const" for chapters keys
Array is reversed but variable holding it is not replaced in this function, so "const" can be used here
2021-04-22 15:28:19 +02:00
Rafał Dzięgiel
bd13a3c15a Use Shift+Left/Right to switch video chapters 2021-04-22 14:40:21 +02:00
Rafał Dzięgiel
edfa85b5cc Use Ctrl+Left/Right to switch playlist items. Closes #63 2021-04-22 14:32:02 +02:00
Rafał Dzięgiel
084f78a851 Change actions naming scheme
Use _ instead of capital letters in words for actions names. This will make some other stuff much easier.
2021-04-22 14:29:40 +02:00
Rafał Dzięgiel
c9b2f25192 Act on key press, not release 2021-04-22 14:13:53 +02:00
Rafał Dzięgiel
6f39b3939a Do not get ancestor on key release if unneeded 2021-04-21 18:14:13 +02:00
Rafał Dzięgiel
ee78ffb1e4 Fix seeks when window tiling with Super key
Super key is consumed by shell and never reaches app key press detection. Use that to fix seeking when tiling window by ignoring all key releases that did not have a key press beforehand.
2021-04-21 17:04:09 +02:00
Rafał Dzięgiel
bfbbc517d5 Small cleanup 2021-04-21 16:59:06 +02:00
Rafał Dzięgiel
7559a61c9f Hold Ctrl while doing D&D to append items to playlist instead of replacing it 2021-04-21 14:56:26 +02:00
Rafał Dzięgiel
deb273179f Add append_playlist function 2021-04-21 14:55:20 +02:00
Rafał Dzięgiel
231af36ef6 Enumerate local directories only 2021-04-20 19:31:24 +02:00
Rafał Dzięgiel
2e892c923b Support opening folders with media files
D&D folder with videos onto Clapper window to play them as video playlist. If folder contains exactly one video and subtitle file, then that video will be played with subtitles automatically applied.
2021-04-20 18:44:53 +02:00
Rafał Dzięgiel
0ab0b66825 0.2.1 2021-04-19 13:06:40 +02:00
Rafał Dzięgiel
d901eb4712 Update README.md 2021-04-19 10:31:30 +02:00
Rafał Dzięgiel
fe03719b38 Show tooltip with full playlist item text on hover
Some titles might be more than few words and will not fit in current playlist popover. Instead of stretching it, show full playlist item filename (or path) on hover in a tooltip.
2021-04-18 18:44:16 +02:00
Rafał Dzięgiel
f0ea7ae798 Remove set_seek_mode check
We now use a custom GstPlayer fork that has it added
2021-04-18 15:28:55 +02:00
Rafał Dzięgiel
380236b8ba Cleanup: do not extend player class twice
We only use the base class once, no need to have it separately then. Merge into single file.
2021-04-18 15:25:02 +02:00
Rafał Dzięgiel
e721130a63 YT: live videos with duration are not live anymore 2021-04-18 14:13:30 +02:00
Rafał Dzięgiel
eaf090d2e2 YT: be a little more quiet about some errors
Some errors are to be expected for some videos. Quietly use fallback methods for them without printing those errors.
2021-04-18 14:04:53 +02:00
Rafał Dzięgiel
87115f43d7 YT: store adaptive option value in itag opts
So its easier to access and obtained only once
2021-04-17 20:35:15 +02:00
Rafał Dzięgiel
33a5ec18fa Change prefs adaptive streaming text
This option sets the preferred streaming mode. When unavailable, other might still be used as a fallback.
2021-04-17 18:06:34 +02:00
Rafał Dzięgiel
ab8cafa0b8 YT: support non-adaptive live streaming 2021-04-17 18:03:33 +02:00
Rafał Dzięgiel
62b6de6db2 YT: support live HLS videos 2021-04-17 16:14:21 +02:00
Rafał Dzięgiel
643c2029d0 Fix wrong indentation size
All the other code uses 4 spaces indent
2021-04-17 13:12:58 +02:00
Rafał Dzięgiel
9799783ee5 Use Gst.(M)SECOND constants instead of numbers
It makes code easier to read
2021-04-17 13:08:12 +02:00
Rafał Dzięgiel
457cbde25e Remove unused return value
This function already appends to passed array. No need to return it.
2021-04-16 11:03:44 +02:00
Rafał Dzięgiel
2fd94fdc70 Add some YouTube related preferences 2021-04-16 10:37:17 +02:00
Rafał Dzięgiel
3a998fb91e YT: auto select best matching resolution for used monitor 2021-04-16 09:53:21 +02:00
Rafał Dzięgiel
b02f54a3a6 Do not show mobile controls transition on launch
Start app with the correct controls layout instead of showing the "hide elapsed time"
transition when started on mobile width. It is annoying.

We cannot detect surface width during app widgets assembly, so update the controls
revealers state on first surface update after window is mapped and only if running
on mobile width. Otherwise do not do anything like before which will result in
showing fully revealed controls (default).
2021-04-15 15:27:28 +02:00
Rafał Dzięgiel
ca7b44092e API: do not lock when changing scaled size values
Those values are private and should be accessed only from GTK thread, so locking widget should not be necessary here.
2021-04-15 11:58:12 +02:00
Rafał Dzięgiel
adbcfecb5e API: unset needs_info_update when stopped 2021-04-15 11:30:55 +02:00
Rafał Dzięgiel
a717e481e8 Fix missing top left menu buttons. Fixes #66
On some non-default system configurations the "menu" layout item might be replaced with one named "icon". Handle "icon" the same as "menu" when organizing headerbar buttons.
2021-04-14 17:48:57 +02:00
Rafał Dzięgiel
4766efbbc4 0.2.0 2021-04-13 12:45:03 +02:00
Rafał Dzięgiel
28c1daf709 Save and offer resume only for local files
We do not know if the online URI still leads to the same file as before (or if different one leads to previous). Additionally we would also need to check if media is seekable. Disable for now to avoid problems.
2021-04-12 22:29:20 +02:00
Rafał Dzięgiel
aa49d25df5 Stop pipeline before replacing playlist 2021-04-12 19:28:12 +02:00
Rafał Dzięgiel
774687710f Add setting to enable YouTube adaptive streaming
For now hidden because other related YouTube settings like min/max resolution, codecs etc. are not done yet
2021-04-12 18:45:36 +02:00
Rafał Dzięgiel
901fc8d760 YT: try harder to find suitable DASH streams
Instead of searching for 1080p only, accept also other H.264 formats for DASH streaming
2021-04-12 17:41:42 +02:00
Rafał Dzięgiel
ab32b2dbbc API: emit media info updated signal after video info updates
This fixes problem with wrong video resolution reported in media info due to being emitted before values were updated.
2021-04-12 17:38:44 +02:00
Rafał Dzięgiel
24bb9f298b Avoid situations with no menu in headerbar
Some shells might want to show menu button outside of the app. We do not support that.
2021-04-12 17:30:00 +02:00
Rafostar
2efa3e0bf6 YT: fix non-working best combined URIs
Fix an undefined variable introduced during recent code cleanup
2021-04-11 16:34:02 +02:00
Rafostar
92e3f7d93c YT: move info ready debug message before signal emit
Otherwise it will appear in wrong order in debug output and be misleading
2021-04-11 15:44:08 +02:00
Rafostar
85804ea297 Move YT related functions from player to youtube script 2021-04-11 15:35:41 +02:00
Rafostar
7cf86e92eb YT: resolve redirects on the Clapper side
Instead of providing URIs directly to GStreamer, follow redirects and provide that final URI. With this change souphttpsrc will not have to go through redirects from the beginning for each video segment.
2021-04-11 14:46:08 +02:00
Rafostar
b5711b145b Set Soup import version to 2.4
With Soup 3.0 release, there is a possibility of having both on the host. Set used version to 2.4 to avoid warnings and compatibility errors for now.
2021-04-10 21:41:28 +02:00
Rafostar
9271392397 Change default window size
Controls panel height is now 3px less then before (as intended). Change default window size setting to make it look good with 16:9 video on fresh install.
2021-04-10 14:58:24 +02:00
Rafał Dzięgiel
175de5bd6d Flatpak: update FFmpeg to 4.4 2021-04-09 21:54:54 +02:00
Rafał Dzięgiel
7b97f29aaf Flatpak: fix GTK4 build script 2021-04-09 21:14:56 +02:00
Rafał Dzięgiel
8d7fb761f7 Always reset auto fullscreen boolean value
Otherwise we would keep checking the settings with each playlist file and accidentally entered fullscreen when that setting value was changed in middle of playback.
2021-04-09 19:58:33 +02:00
Rafał Dzięgiel
aec2166c11 Only auto fullscreen with new playlist
This fixes a bug when player would enter fullscreen when changing playlist item
2021-04-09 19:34:17 +02:00
Rafał Dzięgiel
c21b214477 Tweak how auto fullscreen option works
When auto fullscreen is enabled, enter fullscreen on each media load
unless player is in floating mode in which probably user wants it to remain.
2021-04-09 19:11:34 +02:00
Rafał Dzięgiel
d9939a94c2 Fix some error messages not being displayed 2021-04-09 18:49:44 +02:00
Rafał Dzięgiel
dafa2cfdf5 Fix missing file extension in online URIs 2021-04-09 18:16:02 +02:00
Rafał Dzięgiel
ebe72f20b5 Hide end time together with title when stopped 2021-04-09 17:54:48 +02:00
Rafał Dzięgiel
fa1455556b Treat media without duration as live content 2021-04-09 16:07:11 +02:00
Rafał Dzięgiel
8fb6b971fe Always try to update end time after new media info 2021-04-09 13:53:43 +02:00
Rafał Dzięgiel
1bf46a2f12 Fix top time not showing up on fullscreen startup 2021-04-09 11:37:19 +02:00
Rafał Dzięgiel
a39c67e5e7 Check if local subtitle file exists before loading it
Otherwise GStreamer will error out and playback will stop. Non-existing subtitle file is common on Flatpak when user tries to open/drop a file from a folder that container does not have access to.
2021-04-09 10:55:06 +02:00
Rafał Dzięgiel
a3f78432f8 Flatpak: update GTK4 to latest git
GTK 4.2.0 has some problems that were fixed recently. Update to post release git.
2021-04-08 20:56:50 +02:00
Rafał Dzięgiel
7a7a04554f Flatpak: remove glib-networking dependency
Runtime has now up-to-date version
2021-04-08 20:54:46 +02:00
Rafał Dzięgiel
93de3dc056 Flatpak: update runtime to 40 2021-04-08 20:52:35 +02:00
Rafał Dzięgiel
eda80f314e API: replace source-setup with element-setup callback
Otherwise user agent is only set for source elements and not for further pipeline elements (e.g. dashdemux => souphttpsrc)
2021-04-08 18:24:49 +02:00
Rafał Dzięgiel
b3e6890571 Flatpak: add dashdemux sdix range download patch 2021-04-08 17:19:22 +02:00
Rafał Dzięgiel
c767b3e4b2 Separate debug messages for YouTube 2021-04-08 12:09:42 +02:00
Rafał Dzięgiel
ec6157763b YT: do not try to get info again for current download 2021-04-08 11:05:20 +02:00
Rafał Dzięgiel
cca3077936 Fix startup window size on XOrg
The window size was restored too early which caused the window to be a little bigger then it should on each launch. Restore window size after that window was mapped.
2021-04-07 20:07:05 +02:00
Rafał Dzięgiel
b5e1b3ab86 Flatpak: workaround crashes in adaptive streaming on Intel GPUs #51 2021-04-07 17:43:32 +02:00
Rafał Dzięgiel
b15b94fc90 Convenient ways of opening external subtitles
Play video with external subtiles by:
* selecting and opening both video+subs from file chooser/manager
* dropping subtitles file on player window
* opening subtiles from file chooser/manager while video plays
* send their file path/uri to player via WebSocket message
2021-04-07 16:33:21 +02:00
Rafał Dzięgiel
28d8986072 Update .gitattributes 2021-04-07 08:56:30 +02:00
Rafał Dzięgiel
30a7229b33 API: add media info updated signal
Emit media info updated signal only when media info is initially created and when number/format of tracks changes later. This is needed for GUI to detect resolution change (adaptive streaming) or when user adds external subtitles to current video.
2021-04-06 18:49:08 +02:00
Rafał Dzięgiel
9502e062f4 Support "Default" theme
Adwaita was renamed to Default in latest GTK4 code.
We need to detect it and treat it the same way as we did for Adwaita.

More info in related GTK4 MR:
https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/3079
2021-04-02 11:43:36 +02:00
Rafał Dzięgiel
6a9c77dfad Fix file paths in debug messages 2021-04-01 22:31:54 +02:00
Rafał Dzięgiel
133cda1b41 Fix forgotten ByteArray import during moving code 2021-04-01 22:31:22 +02:00
Rafał Dzięgiel
e68a7fe31a Work around GioFile promisify bug caused by GLib 2.68 2021-04-01 22:05:43 +02:00
Rafał Dzięgiel
7f69bee11c Move all file operations code to single file 2021-04-01 21:58:50 +02:00
Rafał Dzięgiel
295af9fd24 Improve debug messages for file operations 2021-04-01 20:20:42 +02:00
Rafostar
d04297620b Apply controls offset for Adwaita icons only
Only some icons from Adwaita get size modifications. Others themes do not need this, so they should not have an offset applied.
2021-03-31 21:38:44 +02:00
Rafostar
43acfddb06 Sink: inform about used OpenGL(ES) version 2021-03-31 17:34:49 +02:00
Rafostar
d54781eda7 Revert "Sink: limit GL APIs to OpenGL3+ and GLES2+"
This reverts commit 3fd30e41bf.

Some pipeline elements might still need to use (and work) on
older OpenGL versions.
2021-03-31 16:51:50 +02:00
Rafał Dzięgiel
96e5c5aa7c Restore 5px button margins for TV mode
They were reduced too much recently, causing chapter marks to not fit properly onto the controls bar
2021-03-31 09:04:29 +02:00
Rafał Dzięgiel
66ce006f00 Move buttons margins to CSS and tweak them a little 2021-03-30 22:15:12 +02:00
Rafał Dzięgiel
3fd30e41bf Sink: limit GL APIs to OpenGL3+ and GLES2+
Only allow using OpenGL 3.2+ or OpenGL ES 2.0+. This is what GTK4 supports.

#59
2021-03-29 14:38:19 +02:00
Rafał Dzięgiel
a6316c940c YT: always use up to date timestamp 2021-03-26 11:52:32 +01:00
Rafał Dzięgiel
84d9cc7416 Add DBus init debug message 2021-03-26 11:42:41 +01:00
Rafał Dzięgiel
a3499e9b47 Remove unused variable 2021-03-24 21:20:15 +01:00
Rafał Dzięgiel
4a60e01131 Auto set brighter sliders on Adwaita dark only 2021-03-24 21:16:41 +01:00
Rafał Dzięgiel
b404eb2f56 Increase play/pause icon size only on Adwaita theme
Adwaita theme has unusually small media constrols icons, but other popular themes do not. Instead increasing those icons on every theme do it only for Adwaita where problem occurs.
2021-03-24 20:36:08 +01:00
Rafał Dzięgiel
1f18796e0d Make buttons separator black on all themes
Using default @borders value causes it to be white even when all OSD buttons are half transparent black.
2021-03-24 20:04:09 +01:00
Rafał Dzięgiel
254aa538a5 YT: fix expire calc for long movies
Do not multiply video length when calculating expiration date. Otherwise for very long movies we might end up with with a past date.
2021-03-24 20:02:19 +01:00
Rafał Dzięgiel
58cc45ec7d Support accels for menu items
Add support for keyboard shortcuts for menu items. Additionally enable a common Ctrl+O for opening new file(s) and Ctrl+U for opening an URI.

Closes #45
2021-03-24 14:07:20 +01:00
Rafał Dzięgiel
7a75c6d4ff Merge pull request #55 from Rafostar/gl-mods
Simplify GL Sink code
2021-03-22 08:55:04 +01:00
Rafał Dzięgiel
2e97fc362c Dash: add segment ranges only to streams that have them 2021-03-19 15:19:00 +01:00
Rafał Dzięgiel
d762a59cc4 YT: do not keep URI in temp data twice 2021-03-19 14:27:55 +01:00
Rafał Dzięgiel
b42843be1f YT: do not check playability of saved temp data
Saved video info is always playable, otherwise its not saved in first place.
2021-03-19 11:43:37 +01:00
Rafał Dzięgiel
6dc825dfb3 YT: reduce amount of temp data stored per video 2021-03-19 11:25:36 +01:00
Rafał Dzięgiel
e89b3599c9 Remove "escape" key handler
It was conflicting with GTK build-in escape key functionality for closing popovers
2021-03-19 10:29:44 +01:00
Rafał Dzięgiel
79e12a6e36 YT: support obtaining info from player API 2021-03-19 10:26:46 +01:00
Rafał Dzięgiel
36d4a5c848 YT: replace some URL chars with slashes instead encoding it
Avoid dashdemux string query omission bug by using URL path with slashes instead of query string
2021-03-18 09:40:46 +01:00
Rafał Dzięgiel
38e5bae199 Replace lookbehind regexp
This was only supported in latest GJS version
2021-03-18 09:28:00 +01:00
Rafał Dzięgiel
fcff4b4450 Store all temp files in app named temp subdirectory 2021-03-17 11:57:02 +01:00
Rafał Dzięgiel
4021745a56 YT: set the same user agent as in player client 2021-03-17 10:47:38 +01:00
Rafał Dzięgiel
bd20d305ba YT: store reusable alive info in temp folder 2021-03-17 10:38:39 +01:00
Rafał Dzięgiel
d9b35b7fb8 YT: try only once
Avoid triggering 429 ban, by not trying second time
2021-03-16 19:45:33 +01:00
Rafał Dzięgiel
f1e00434ba Fix reference to undefined object 2021-03-16 13:44:22 +01:00
Rafał Dzięgiel
918157be04 Cooooookies!!! 2021-03-16 13:12:44 +01:00
Rafał Dzięgiel
72b55939b4 YT: abort on 429 error 2021-03-16 10:33:31 +01:00
Rafał Dzięgiel
e0a3ef78db YT: pass download info using object intead of array 2021-03-16 10:20:01 +01:00
Rafał Dzięgiel
4f46a7eaa8 YT: handle embedded videos URIs 2021-03-15 20:15:24 +01:00
Rafał Dzięgiel
050ef440dc Merge pull request #54 from Rafostar/yt-cache
YouTube cache
2021-03-15 16:37:55 +01:00
Rafał Dzięgiel
a4d55f8114 YT: store and load decipher actions from Clapper cache dir 2021-03-15 16:35:36 +01:00
Rafał Dzięgiel
aa60c56a58 Treat "yt" and "youtube" URI schemes as YouTube videos
You can set URI to "yt://VIDEO_ID" for YouTube videos
2021-03-15 13:40:50 +01:00
Rafał Dzięgiel
8c307dc90f YT: save decipher actions only after successful deciphering 2021-03-15 13:14:41 +01:00
Rafał Dzięgiel
5b6141ee8c YT: do not check player ID if actions are cached 2021-03-15 13:07:12 +01:00
Rafał Dzięgiel
8f294604dc Remove drop target from top revealer
It is causing various issues unfortunately
2021-03-14 23:33:29 +01:00
Rafostar
06f8e5d259 YT: cache current decipher actions 2021-03-14 21:00:18 +01:00
Rafostar
6370e1126b YT: check if decipher produced result 2021-03-14 16:50:23 +01:00
Rafostar
270e59137d YT: check if player URI is valid 2021-03-14 16:39:22 +01:00
Rafostar
ec18ca989a YT: decipher videos with signatures
Increase amount of playable YouTube videos by deciphering the ones that require to do so.

Many thanks to "node-ytdl-core" devs for JS regular expressions needed for YouTube player parsing.
2021-03-14 15:51:19 +01:00
Rafał Dzięgiel
46d24536c0 Do not keep invalid YT video info 2021-03-12 15:10:18 +01:00
Rafał Dzięgiel
c89d488c30 Prefetch YouTube video info on hover
Speed up loading of YouTube videos by downloading and parsing their info before video is dropped into player.
2021-03-12 13:05:58 +01:00
Rafał Dzięgiel
01c26cbbc3 Small text formatting fix 2021-03-12 08:50:52 +01:00
Rafał Dzięgiel
4375077dbc Decode custom video title from info
The values in JSON info are URI encoded with "+" signs, add custom decode function that decodes them.
2021-03-11 18:49:08 +01:00
Rafał Dzięgiel
fceb8ff70a YouTube support. Closes #46 2021-03-11 17:34:54 +01:00
Rafał Dzięgiel
6dc37088cf Fix error when playback finishes during controls reveal animation 2021-03-08 10:37:40 +01:00
Rafostar
4e85f6b749 API: set some common user agent
Some internet sites might prevent us from access unless some sort of a user agent is set
2021-03-07 21:37:01 +01:00
Rafostar
0cd82b1b8a API: remove video sink plugin selection
Clapper only has and supports one video sink. I would rather replace it than forcing support for multiple plugins.
2021-03-06 22:09:27 +01:00
Rafostar
39da52dd62 Sink: unlock widget before setting queue
Let GTK handle setting queue resize/render on the widget. We are not accessing widget values at this time, so it can be unlocked. It will be locked back during the render.
2021-03-06 19:16:28 +01:00
Rafał Dzięgiel
9c12afbf80 Sink: rename into GstClapperGLSink
The customized GTK4 sink version has few differences from the one shipped as part of GStreamer. Rename custom sink to GstClapperGLSink to avoid confusion.
2021-03-06 00:24:31 +01:00
Rafał Dzięgiel
e3c9b112e2 Sink: remove gtkconfig header
Remove another leftover meant for GTK3 compatibility
2021-03-05 23:27:08 +01:00
Rafał Dzięgiel
13d675beff Sink: merge gtkwidget into single class
Same as with video sink. Clapper uses only one so no need for subclassing.
2021-03-05 23:23:34 +01:00
Rafał Dzięgiel
95c3845398 Sink: merge gstsink into single class
Clapper only uses single video sink. No need for subclassing it.
2021-03-05 21:06:03 +01:00
Rafał Dzięgiel
93549a67af Sink: remove ignore_alpha property
GTK4 no longer supports ignoring alpha
2021-03-05 19:26:29 +01:00
Rafał Dzięgiel
07fb0a9a46 Sink: remove GTK4 if-defs
Clapper works with GTK4, so reduce codebase by removing GTK3 leftovers
2021-03-05 19:02:14 +01:00
Rafał Dzięgiel
fe3fd32932 Sink: keep track of widget allocation size
Instead of obtaining allocation size on each frame draw, keep track of its current size and update value on its change.
2021-03-05 18:41:26 +01:00
Rafał Dzięgiel
637212f7e8 Sink: move GL drawing logic into single function
No need having it in another function with additional call if it is used only from single place.
2021-03-05 18:10:35 +01:00
Rafał Dzięgiel
8b2e63ac48 Update README.md 2021-02-28 14:56:19 +01:00
191 changed files with 24975 additions and 6855 deletions

6
.gitattributes vendored
View File

@@ -1 +1,5 @@
lib/* linguist-vendored
extras/**/* linguist-vendored
lib/**/* linguist-vendored
lib/**/**/* linguist-vendored
lib/gst/clapper/gstclapper-mpris* linguist-vendored=false
lib/gst/clapper/gtk4/* linguist-vendored=false

36
.github/workflows/flatpak-nightly.yml vendored Normal file
View File

@@ -0,0 +1,36 @@
on:
schedule:
- cron: "0 0 * * *"
name: "Flatpak Nightly"
jobs:
flatpak:
name: "Flatpak"
runs-on: ubuntu-latest
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
- 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 }}

38
.github/workflows/flatpak.yml vendored Normal file
View File

@@ -0,0 +1,38 @@
on:
push:
branches:
- master
pull_request:
branches:
- master
name: "Flatpak"
jobs:
flatpak:
name: "Flatpak"
runs-on: ubuntu-latest
container:
image: bilelmoussaoui/flatpak-github-actions:gnome-41
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 }}

6
.gitmodules vendored
View File

@@ -1,3 +1,3 @@
[submodule "pkgs/flatpak/shared-modules"]
path = pkgs/flatpak/shared-modules
url = https://github.com/flathub/shared-modules.git
[submodule "pkgs/flatpak/flathub"]
path = pkgs/flatpak/flathub
url = https://github.com/flathub/com.github.rafostar.Clapper.git

View File

@@ -1,6 +1,10 @@
# 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).
[![Flatpak](https://github.com/Rafostar/clapper/actions/workflows/flatpak.yml/badge.svg?event=push)](https://github.com/Rafostar/clapper/actions/workflows/flatpak.yml)
[![Flatpak Nightly](https://github.com/Rafostar/clapper/actions/workflows/flatpak-nightly.yml/badge.svg?event=schedule)](https://github.com/Rafostar/clapper/actions/workflows/flatpak-nightly.yml)
[![Crowdin](https://badges.crowdin.net/clapper/localized.svg)](https://crowdin.com/project/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,23 +27,26 @@ 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).
```sh
flatpak install https://rafostar.github.io/flatpak/com.github.rafostar.Clapper.flatpakref
```
<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>
## 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:
#### 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))
#### 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 built on each git commit, and are thus considered unstable.
#### Arch Linux
You can get Clapper from the AUR: [clapper-git](https://aur.archlinux.org/packages/clapper-git)
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
```sh
@@ -47,23 +54,19 @@ 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.
## Questions?
Feel free to ask me any questions. Come and talk on Matrix: [#clapper-player:matrix.org](https://matrix.to/#/#clapper-player:matrix.org)
**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 try to disable the "render window shadows" option to have more GPU power available for non-fullscreen video rendering.
## Translations
Preferred translation method is to use [Clapper Crowdin](https://crowdin.com/project/clapper) web page.
## Other Questions?
Feel free to ask me any questions.<br>
Use either GitHub [discussions](https://github.com/Rafostar/clapper/discussions) or come and talk on Matrix: **#clapper-player:matrix.org**
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.

12
TODO.md
View File

@@ -15,11 +15,11 @@
- [X] Picture-in-Picture mode window (floating window)
- [ ] Touch gestures/swipes support
- Media playlists:
- [ ] Add more items to playlist via GUI
- [X] Add more items to playlist via D&D
- [X] Select video from playlist
- [ ] Reorder playlist items via D&D
- [X] Load special playlist file (.claps)
- [ ] Save to playlist file from GUI
- [X] Save to playlist file from GUI
- Seeking:
- [X] Customizable seek time
- [X] Set seek mode (default, accurate, fast)
@@ -31,7 +31,7 @@
- [X] Audio offset
- [ ] MDNS and UPNP (discovering media in local network)
- [X] DND files from Nautilus to play (ignore incompatible ones)
- [ ] Support dropping whole folders
- [X] Support dropping whole folders
- [ ] Search for subtitles, download and activate (SMplayer)
- [ ] Auto add subtitles from same folder
- [ ] Set global subtitles folders
@@ -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)

View File

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

View File

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

View File

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

View File

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

@@ -0,0 +1,3 @@
files:
- source: /po/com.github.rafostar.Clapper.pot
translation: /po/%osx_locale%.po

View File

@@ -5,19 +5,35 @@ scale marks {
radio {
margin-left: -2px;
}
scrolledwindow scrollbar.vertical slider {
min-height: 16px;
}
/* Adwaita is missing osd ListBox */
.osd list {
.clapperplaylist {
background: none;
}
.osd list row {
.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;
}
@@ -25,7 +41,7 @@ radio {
border: transparent;
}
.linkseparator {
background: alpha(@borders, 0.75);
background: rgba(24,24,24,0.72);
min-width: 1px;
}
.linkedleft image {
@@ -34,18 +50,18 @@ 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;
}
/* Rounded corners */
.adwrounded.csd {
border-radius: 8px;
}
@@ -62,22 +78,54 @@ radio {
border-radius: 8px;
}
/* Reduce sliders size */
scale trough slider {
min-height: 18px;
min-width: 18px;
}
.fullscreen.tvmode scale trough slider {
min-height: 20px;
min-width: 20px;
}
.videowidget {
min-width: 320px;
min-height: 180px;
}
.tvmode popover box {
.fullscreen.tvmode popover box {
text-shadow: none;
font-size: 21px;
font-weight: 500;
}
.tvmode button {
.clappercontrols {
margin-left: 2px;
margin-right: 2px;
}
.fullscreen.tvmode .clappercontrols {
margin-left: 1px;
margin-right: 1px;
}
.clappercontrolsbutton {
margin: 3px;
margin-left: 1px;
margin-right: 1px;
}
.fullscreen.tvmode .clappercontrolsbutton {
min-width: 32px;
min-height: 32px;
margin: 5px;
margin-left: 4px;
margin-right: 4px;
}
.clappercontrolsbutton.text-button {
padding-left: 4px;
padding-right: 4px;
}
.fullscreen.tvmode button image {
-gtk-icon-shadow: none;
}
.tvmode radio {
.fullscreen.tvmode radio {
margin-left: 0px;
margin-right: 4px;
border: 2px solid;
@@ -85,40 +133,30 @@ radio {
min-height: 17px;
}
.tvmode .playercontrols {
-gtk-icon-size: 24px;
/* Also affects popover buttons */
.fullscreen.tvmode .clappercontrols button image {
-gtk-icon-size: 26px;
}
.playbackicon {
-gtk-icon-size: 20px;
}
.tvmode .playbackicon {
-gtk-icon-size: 28px;
}
.labelbutton {
margin-left: -4px;
margin-right: -4px;
margin-top: 1px;
min-width: 8px;
.clappercontrolsbutton.text-button label {
font-family: 'Cantarell', sans-serif;
font-variant-numeric: tabular-nums;
font-weight: 600;
}
.tvmode .labelbutton {
margin-top: 0px;
font-size: 23px;
.fullscreen.tvmode .clappercontrolsbutton.text-button label {
font-size: 22px;
text-shadow: none;
}
/* Top Revealer */
.tvmode .revealertopgrid {
.fullscreen.tvmode .revealertopgrid {
font-family: 'Cantarell', sans-serif;
}
.tvmode .tvtitle {
.fullscreen.tvmode .tvtitle {
font-size: 28px;
font-weight: 500;
text-shadow: none;
}
.tvtime {
.fullscreen.tvmode .tvtime {
margin-top: -2px;
margin-bottom: -2px;
min-height: 4px;
@@ -126,7 +164,7 @@ radio {
font-weight: 700;
font-variant-numeric: tabular-nums;
}
.tvendtime {
.fullscreen.tvmode .tvendtime {
margin-top: -4px;
margin-bottom: 2px;
min-height: 6px;
@@ -135,31 +173,27 @@ radio {
font-variant-numeric: tabular-nums;
}
/* Button Inside Popover */
.popoverbutton {
min-width: 24px;
min-height: 24px;
}
/* Position Scale */
.positionscale {
margin-top: -2px;
margin-bottom: -2px;
}
.tvmode .positionscale {
margin-top: -1px;
margin: -2px;
}
.positionscale trough highlight {
min-height: 4px;
}
.osd .positionscale trough highlight {
min-height: 6px;
}
.tvmode .positionscale trough slider {
.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;
@@ -167,11 +201,11 @@ radio {
.positionscale.fine-tune mark indicator {
min-height: 6px;
}
.tvmode .positionscale mark indicator {
.fullscreen.tvmode .positionscale mark indicator {
min-height: 7px;
min-width: 2px;
}
.tvmode .positionscale.fine-tune mark indicator {
.fullscreen.tvmode .positionscale.fine-tune mark indicator {
min-height: 7px;
min-width: 2px;
}
@@ -183,17 +217,20 @@ radio {
margin-top: 4px;
margin-bottom: -6px;
}
.tvmode .positionscale marks.top {
.fullscreen.tvmode .positionscale marks.top {
margin-bottom: 2px;
}
.tvmode .positionscale marks.bottom {
.fullscreen.tvmode .positionscale marks.bottom {
margin-top: 2px;
}
.tvmode .positionscale trough highlight {
.fullscreen.tvmode .positionscale trough {
border-radius: 3px;
}
.fullscreen.tvmode .positionscale trough highlight {
border-radius: 3px;
min-height: 20px;
}
.tvmode .positionscale.fine-tune trough highlight {
.fullscreen.tvmode .positionscale.fine-tune trough highlight {
border-radius: 3px;
min-height: 20px;
}
@@ -205,7 +242,7 @@ radio {
margin-right: -6px;
min-height: 180px;
}
.tvmode .volumescale {
.fullscreen.tvmode .volumescale {
margin: 2px;
margin-left: -6px;
margin-right: -4px;
@@ -216,7 +253,10 @@ radio {
margin-top: -4px;
margin-bottom: -6px;
}
.tvmode .volumescale trough highlight {
.volumescale trough highlight {
min-width: 4px;
}
.fullscreen.tvmode .volumescale trough highlight {
min-width: 6px;
}
.overamp trough highlight {
@@ -224,16 +264,23 @@ radio {
}
/* Elapsed Popover */
.elapsedpopoverbox {
min-width: 260px;
.elapsedpopover {
min-width: 326px;
}
.elapsedpopoverbox box separator {
background: @insensitive_fg_color;
.fullscreen.tvmode .elapsedpopover {
min-width: 448px;
}
.tvmode .elapsedpopoverbox {
min-width: 360px;
.elapsedpopover contents {
padding-bottom: 0px;
}
.tvmode .speedscale trough highlight {
.speedscale {
margin-left: 4px;
margin-right: 4px;
}
.speedscale trough highlight {
min-height: 4px;
}
.fullscreen.tvmode .speedscale trough highlight {
min-height: 6px;
}
@@ -256,33 +303,16 @@ radio {
.chapterlabel {
min-width: 32px;
}
.tvmode .chapterlabel {
.fullscreen.tvmode .chapterlabel {
min-width: 40px;
text-shadow: none;
font-size: 22px;
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 */
@@ -298,14 +328,60 @@ radio {
.gpufriendly {
box-shadow: -8px -8px transparent, 8px 8px transparent;
}
.gpufriendlyfs {
.fullscreen.gpufriendlyfs {
box-shadow: none;
}
.brightscale trough highlight {
filter: brightness(120%);
}
/* Error BG */
.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 */
}

View 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

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

View File

@@ -6,31 +6,31 @@
<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>
@@ -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>
@@ -84,10 +80,6 @@
<default>true</default>
<summary>Enable to force the app to use dark theme variant</summary>
</key>
<key name="brighter-sliders" type="b">
<default>true</default>
<summary>Enable to make all sliders/bars brighter</summary>
</key>
<key name="render-shadows" type="b">
<default>true</default>
<summary>Enable rendering window shadows (only if theme has them)</summary>
@@ -95,9 +87,17 @@
<!-- 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="use-playbin3" type="b">
<default>false</default>
<summary>Use playbin3 element instead of playbin2</summary>
</key>
<key name="use-pipewire" type="b">
<default>false</default>
<summary>Use PipeWire for audio output</summary>
</key>
<key name="play-flags" type="i">
<default>1687</default>
<summary>Set PlayFlags for playbin</summary>
@@ -105,7 +105,7 @@
<!-- Other -->
<key name="window-size" type="s">
<default>'[960, 583]'</default>
<default>'[800, 490]'</default>
<summary>Stores window size to restore on next launch</summary>
</key>
<key name="volume-last" type="d">

View File

@@ -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,129 @@
</screenshot>
</screenshots>
<releases>
<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>
<ul>
<li>Fix missing top left menu buttons on some system configurations</li>
<li>Fix potential video sink deadlock</li>
<li>Do not show mobile controls transition on launch</li>
<li>Show tooltip with full playlist item text on hover</li>
</ul>
<p>YouTube:</p>
<ul>
<li>Auto select best matching resolution for used monitor</li>
<li>Added some YouTube related preferences</li>
<li>Added support for live HLS videos</li>
<li>Added support for non-adaptive live HLS streaming</li>
</ul>
</description>
</release>
<release version="0.2.0" date="2021-04-13">
<description>
<p>New features:</p>
<ul>
<li>YouTube support - drag and drop videos from youtube or use open URI dialog to play them</li>
<li>Added convenient ways of opening external subtitles</li>
</ul>
<p>Changes:</p>
<ul>
<li>Few GUI layout improvements</li>
<li>Simplified video sink code</li>
<li>Fixed missing Ctrl+O common keybinding</li>
<li>Fixed error when playback finishes during controls reveal animation</li>
<li>Fixed startup window size on Xorg</li>
<li>Fixed top time not showing up on fullscreen startup</li>
<li>Fixed missing file extensions in online URIs</li>
<li>Fixed some error messages not being displayed</li>
</ul>
</description>
</release>
<release version="0.1.0" date="2021-02-26">
<description>
<p>First stable release</p>
@@ -64,6 +183,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>

View File

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

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<node>
<interface name="org.mpris.MediaPlayer2">
<method name="Raise"/>
<method name="Quit"/>
<property name="CanQuit" type="b" access="read"/>
<property name="Fullscreen" type="b" access="readwrite"/>
<property name="CanSetFullscreen" type="b" access="read"/>
<property name="CanRaise" type="b" access="read"/>
<property name="HasTrackList" type="b" access="read"/>
<property name="Identity" type="s" access="read"/>
<property name="DesktopEntry" type="s" access="read"/>
<property name="SupportedUriSchemes" type="as" access="read"/>
<property name="SupportedMimeTypes" type="as" access="read"/>
</interface>
<interface name="org.mpris.MediaPlayer2.Player">
<method name="Next"/>
<method name="Previous"/>
<method name="Pause"/>
<method name="PlayPause"/>
<method name="Stop"/>
<method name="Play"/>
<method name="Seek">
<arg name="Offset" type="x" direction="in"/>
</method>
<method name="SetPosition">
<arg name="TrackId" type="o" direction="in"/>
<arg name="Position" type="x" direction="in"/>
</method>
<method name="OpenUri">
<arg name="Uri" type="s" direction="in"/>
</method>
<signal name="Seeked">
<arg type="x" name="Position"/>
</signal>
<property name="PlaybackStatus" type="s" access="read"/>
<property name="LoopStatus" type="s" access="readwrite"/>
<property name="Rate" type="d" access="readwrite"/>
<property name="Shuffle" type="b" access="readwrite"/>
<property name="Metadata" type="a{sv}" access="read"/>
<property name="Volume" type="d" access="readwrite"/>
<property name="Position" type="x" access="read"/>
<property name="MinimumRate" type="d" access="read"/>
<property name="MaximumRate" type="d" access="read"/>
<property name="CanGoNext" type="b" access="read"/>
<property name="CanGoPrevious" type="b" access="read"/>
<property name="CanPlay" type="b" access="read"/>
<property name="CanPause" type="b" access="read"/>
<property name="CanSeek" type="b" access="read"/>
<property name="CanControl" type="b" access="read"/>
</interface>
</node>

View 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

View 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

View 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

View 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

View File

@@ -1,18 +1,34 @@
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',
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,
)

View File

@@ -28,6 +28,7 @@
#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-mpris.h>
#include <gst/clapper/gstclapper-gtk4-plugin.h>
#endif /* __CLAPPER_H__ */

View File

@@ -30,7 +30,7 @@
#endif
#include "gstclapper-gtk4-plugin.h"
#include "gtk4/gstgtkglsink.h"
#include "gtk4/gstclapperglsink.h"
enum
{
@@ -77,9 +77,7 @@ gst_clapper_gtk4_plugin_constructed (GObject * object)
{
GstClapperGtk4Plugin *self = GST_CLAPPER_GTK4_PLUGIN (object);
if (!self->video_sink)
self->video_sink = g_object_new (GST_TYPE_GTK_GL_SINK, NULL);
self->video_sink = g_object_new (GST_TYPE_CLAPPER_GL_SINK, NULL);
gst_object_ref_sink (self->video_sink);
G_OBJECT_CLASS (parent_class)->constructed (object);
@@ -111,35 +109,15 @@ gst_clapper_gtk4_plugin_finalize (GObject * object)
G_OBJECT_CLASS (parent_class)->finalize (object);
}
#define C_ENUM(v) ((gint) v)
GType
gst_clapper_gtk4_plugin_type_get_type (void)
{
static gsize id = 0;
static const GEnumValue values[] = {
{C_ENUM (GST_CLAPPER_GTK4_PLUGIN_TYPE_GLAREA), "GST_CLAPPER_GTK4_PLUGIN_TYPE_GLAREA", "glarea"},
{0, NULL, NULL}
};
if (g_once_init_enter (&id)) {
GType tmp = g_enum_register_static ("GstClapperGtk4PluginType", values);
g_once_init_leave (&id, tmp);
}
return (GType) id;
}
/**
* gst_clapper_gtk4_plugin_new:
* @plugin_type: (allow-none): Requested GstClapperGtk4PluginType
*
* Creates a new GTK4 plugin.
*
* Returns: (transfer full): the new GstClapperGtk4Plugin
*/
GstClapperGtk4Plugin *
gst_clapper_gtk4_plugin_new (G_GNUC_UNUSED const GstClapperGtk4PluginType plugin_type)
gst_clapper_gtk4_plugin_new (void)
{
return g_object_new (GST_TYPE_CLAPPER_GTK4_PLUGIN, NULL);
}

View File

@@ -26,20 +26,6 @@
G_BEGIN_DECLS
/* PluginType */
GST_CLAPPER_API
GType gst_clapper_gtk4_plugin_type_get_type (void);
#define GST_TYPE_CLAPPER_GTK4_PLUGIN_TYPE (gst_clapper_gtk4_plugin_type_get_type ())
/**
* GstClapperGtk4PluginType:
* @GST_CLAPPER_GTK4_PLUGIN_TYPE_GLAREA: GTK4 GLArea sink.
*/
typedef enum
{
GST_CLAPPER_GTK4_PLUGIN_TYPE_GLAREA,
} GstClapperGtk4PluginType;
#define GST_TYPE_CLAPPER_GTK4_PLUGIN (gst_clapper_gtk4_plugin_get_type ())
#define GST_IS_CLAPPER_GTK4_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CLAPPER_GTK4_PLUGIN))
#define GST_IS_CLAPPER_GTK4_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_CLAPPER_GTK4_PLUGIN))
@@ -79,7 +65,7 @@ GST_CLAPPER_API
GType gst_clapper_gtk4_plugin_get_type (void);
GST_CLAPPER_API
GstClapperGtk4Plugin * gst_clapper_gtk4_plugin_new (const GstClapperGtk4PluginType plugin_type);
GstClapperGtk4Plugin * gst_clapper_gtk4_plugin_new (void);
G_END_DECLS

View File

@@ -45,6 +45,7 @@ struct _GstClapperSubtitleInfo
{
GstClapperStreamInfo parent;
gchar *title;
gchar *language;
};
@@ -108,7 +109,7 @@ struct _GstClapperMediaInfo
GList *video_stream_list;
GList *subtitle_stream_list;
GstClockTime duration;
GstClockTime duration;
};
struct _GstClapperMediaInfoClass

View File

@@ -379,6 +379,7 @@ gst_clapper_subtitle_info_finalize (GObject * object)
{
GstClapperSubtitleInfo *info = GST_CLAPPER_SUBTITLE_INFO (object);
g_free (info->title);
g_free (info->language);
G_OBJECT_CLASS (gst_clapper_subtitle_info_parent_class)->finalize (object);
@@ -392,6 +393,20 @@ gst_clapper_subtitle_info_class_init (GstClapperSubtitleInfoClass * klass)
gobject_class->finalize = gst_clapper_subtitle_info_finalize;
}
/**
* gst_clapper_subtitle_info_get_title:
* @info: a #GstClapperSubtitleInfo
*
* Returns: the title of the stream, or NULL if unknown.
*/
const gchar *
gst_clapper_subtitle_info_get_title (const GstClapperSubtitleInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_SUBTITLE_INFO (info), NULL);
return info->title;
}
/**
* gst_clapper_subtitle_info_get_language:
* @info: a #GstClapperSubtitleInfo
@@ -513,6 +528,8 @@ gst_clapper_subtitle_info_copy (GstClapperSubtitleInfo * ref)
GstClapperSubtitleInfo *ret;
ret = gst_clapper_subtitle_info_new ();
if (ref->title)
ret->title = g_strdup (ref->title);
if (ref->language)
ret->language = g_strdup (ref->language);
@@ -767,7 +784,8 @@ gst_clapper_media_info_get_toc (const GstClapperMediaInfo * info)
* gst_clapper_media_info_get_title:
* @info: a #GstClapperMediaInfo
*
* Returns: the media title.
* Returns: the media title. When metadata does not contain title,
* returns title parsed from URI.
*/
const gchar *
gst_clapper_media_info_get_title (const GstClapperMediaInfo * info)

View File

@@ -170,7 +170,10 @@ GST_CLAPPER_API
GType gst_clapper_subtitle_info_get_type (void);
GST_CLAPPER_API
const gchar * gst_clapper_subtitle_info_get_language (const GstClapperSubtitleInfo* info);
const gchar * gst_clapper_subtitle_info_get_title (const GstClapperSubtitleInfo *info);
GST_CLAPPER_API
const gchar * gst_clapper_subtitle_info_get_language (const GstClapperSubtitleInfo *info);
#define GST_TYPE_CLAPPER_MEDIA_INFO \
(gst_clapper_media_info_get_type())

View File

@@ -0,0 +1,43 @@
/*
* 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_MPRIS_PRIVATE_H__
#define __GST_CLAPPER_MPRIS_PRIVATE_H__
#include <gst/clapper/gstclapper-mpris.h>
#include <gst/clapper/gstclapper.h>
G_BEGIN_DECLS
G_GNUC_INTERNAL
void gst_clapper_mpris_set_clapper (GstClapperMpris *self, GstClapper *clapper,
GstClapperSignalDispatcher *signal_dispatcher);
G_GNUC_INTERNAL
void gst_clapper_mpris_set_media_info (GstClapperMpris *self, GstClapperMediaInfo *info);
G_GNUC_INTERNAL
void gst_clapper_mpris_set_playback_status (GstClapperMpris *self, const gchar *status);
G_GNUC_INTERNAL
void gst_clapper_mpris_set_position (GstClapperMpris *self, gint64 position);
G_END_DECLS
#endif /* __GST_CLAPPER_MPRIS_PRIVATE_H__ */

View File

@@ -0,0 +1,793 @@
/*
* 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-mpris-gdbus.h"
#include "gstclapper-mpris.h"
#include "gstclapper-mpris-private.h"
#include "gstclapper-signal-dispatcher-private.h"
GST_DEBUG_CATEGORY_STATIC (gst_clapper_mpris_debug);
#define GST_CAT_DEFAULT gst_clapper_mpris_debug
#define MPRIS_DEFAULT_VOLUME 1.0
enum
{
PROP_0,
PROP_OWN_NAME,
PROP_ID_PATH,
PROP_IDENTITY,
PROP_DESKTOP_ENTRY,
PROP_DEFAULT_ART_URL,
PROP_VOLUME,
PROP_LAST
};
struct _GstClapperMpris
{
GObject parent;
GstClapperMprisMediaPlayer2 *base_skeleton;
GstClapperMprisMediaPlayer2Player *player_skeleton;
GstClapperSignalDispatcher *signal_dispatcher;
GstClapperMediaInfo *media_info;
guint name_id;
/* Properties */
gchar *own_name;
gchar *id_path;
gchar *identity;
gchar *desktop_entry;
gchar *default_art_url;
gboolean parse_media_info;
/* Current status */
gchar *playback_status;
gboolean can_play;
guint64 position;
GThread *thread;
GMutex lock;
GCond cond;
GMainContext *context;
GMainLoop *loop;
};
struct _GstClapperMprisClass
{
GObjectClass parent_class;
};
#define parent_class gst_clapper_mpris_parent_class
G_DEFINE_TYPE (GstClapperMpris, gst_clapper_mpris, G_TYPE_OBJECT);
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
static void gst_clapper_mpris_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_clapper_mpris_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void gst_clapper_mpris_dispose (GObject * object);
static void gst_clapper_mpris_finalize (GObject * object);
static void gst_clapper_mpris_constructed (GObject * object);
static gpointer gst_clapper_mpris_main (gpointer data);
static void unregister (GstClapperMpris * self);
static void
gst_clapper_mpris_init (GstClapperMpris * self)
{
GST_DEBUG_CATEGORY_INIT (gst_clapper_mpris_debug, "ClapperMpris", 0,
"GstClapperMpris");
GST_TRACE_OBJECT (self, "Initializing");
self = gst_clapper_mpris_get_instance_private (self);
g_mutex_init (&self->lock);
g_cond_init (&self->cond);
self->context = g_main_context_new ();
self->loop = g_main_loop_new (self->context, FALSE);
self->base_skeleton = gst_clapper_mpris_media_player2_skeleton_new ();
self->player_skeleton = gst_clapper_mpris_media_player2_player_skeleton_new ();
self->name_id = 0;
self->own_name = NULL;
self->id_path = NULL;
self->identity = NULL;
self->desktop_entry = NULL;
self->default_art_url = NULL;
self->signal_dispatcher = NULL;
self->media_info = NULL;
self->parse_media_info = FALSE;
self->playback_status = g_strdup ("Stopped");
self->can_play = FALSE;
self->position = 0;
GST_TRACE_OBJECT (self, "Initialized");
}
static void
gst_clapper_mpris_class_init (GstClapperMprisClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
gobject_class->set_property = gst_clapper_mpris_set_property;
gobject_class->get_property = gst_clapper_mpris_get_property;
gobject_class->dispose = gst_clapper_mpris_dispose;
gobject_class->finalize = gst_clapper_mpris_finalize;
gobject_class->constructed = gst_clapper_mpris_constructed;
param_specs[PROP_OWN_NAME] =
g_param_spec_string ("own-name", "DBus own name",
"DBus name to own on connection",
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_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_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_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_READWRITE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
param_specs[PROP_VOLUME] =
g_param_spec_double ("volume", "Volume", "Volume",
0, 1.5, MPRIS_DEFAULT_VOLUME, G_PARAM_READWRITE |
G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
}
static void
gst_clapper_mpris_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstClapperMpris *self = GST_CLAPPER_MPRIS (object);
switch (prop_id) {
case PROP_OWN_NAME:
self->own_name = g_value_dup_string (value);
break;
case PROP_ID_PATH:
self->id_path = g_value_dup_string (value);
break;
case PROP_IDENTITY:
self->identity = g_value_dup_string (value);
break;
case PROP_DESKTOP_ENTRY:
self->desktop_entry = g_value_dup_string (value);
break;
case PROP_DEFAULT_ART_URL:
self->default_art_url = g_value_dup_string (value);
break;
case PROP_VOLUME:
g_object_set_property (G_OBJECT (self->player_skeleton), "volume", value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_clapper_mpris_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
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;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_clapper_mpris_dispose (GObject * object)
{
GstClapperMpris *self = GST_CLAPPER_MPRIS (object);
GST_TRACE_OBJECT (self, "Stopping main thread");
if (self->loop) {
g_main_loop_quit (self->loop);
if (self->thread != g_thread_self ())
g_thread_join (self->thread);
else
g_thread_unref (self->thread);
self->thread = NULL;
g_main_loop_unref (self->loop);
self->loop = NULL;
g_main_context_unref (self->context);
self->context = NULL;
}
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
gst_clapper_mpris_finalize (GObject * object)
{
GstClapperMpris *self = GST_CLAPPER_MPRIS (object);
GST_TRACE_OBJECT (self, "Finalize");
g_free (self->own_name);
g_free (self->id_path);
g_free (self->identity);
g_free (self->desktop_entry);
g_free (self->default_art_url);
g_free (self->playback_status);
if (self->base_skeleton)
g_object_unref (self->base_skeleton);
if (self->player_skeleton)
g_object_unref (self->player_skeleton);
if (self->signal_dispatcher)
g_object_unref (self->signal_dispatcher);
if (self->media_info)
g_object_unref (self->media_info);
g_mutex_clear (&self->lock);
g_cond_clear (&self->cond);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_clapper_mpris_constructed (GObject * object)
{
GstClapperMpris *self = GST_CLAPPER_MPRIS (object);
GST_TRACE_OBJECT (self, "Constructed");
g_mutex_lock (&self->lock);
self->thread = g_thread_new ("GstClapperMpris",
gst_clapper_mpris_main, self);
while (!self->loop || !g_main_loop_is_running (self->loop))
g_cond_wait (&self->cond, &self->lock);
g_mutex_unlock (&self->lock);
G_OBJECT_CLASS (parent_class)->constructed (object);
}
static gboolean
main_loop_running_cb (gpointer user_data)
{
GstClapperMpris *self = GST_CLAPPER_MPRIS (user_data);
GST_TRACE_OBJECT (self, "Main loop running now");
g_mutex_lock (&self->lock);
g_cond_signal (&self->cond);
g_mutex_unlock (&self->lock);
return G_SOURCE_REMOVE;
}
static gboolean
handle_play_cb (GstClapperMprisMediaPlayer2Player * player_skeleton,
GDBusMethodInvocation * invocation, gpointer user_data)
{
GstClapper *clapper = GST_CLAPPER (user_data);
GST_DEBUG ("Handle Play");
gst_clapper_play (clapper);
gst_clapper_mpris_media_player2_player_complete_play (player_skeleton, invocation);
return TRUE;
}
static gboolean
handle_pause_cb (GstClapperMprisMediaPlayer2Player * player_skeleton,
GDBusMethodInvocation * invocation, gpointer user_data)
{
GstClapper *clapper = GST_CLAPPER (user_data);
GST_DEBUG ("Handle Pause");
gst_clapper_pause (clapper);
gst_clapper_mpris_media_player2_player_complete_pause (player_skeleton, invocation);
return TRUE;
}
static gboolean
handle_play_pause_cb (GstClapperMprisMediaPlayer2Player * player_skeleton,
GDBusMethodInvocation * invocation, gpointer user_data)
{
GstClapper *clapper = GST_CLAPPER (user_data);
GST_DEBUG ("Handle PlayPause");
gst_clapper_toggle_play (clapper);
gst_clapper_mpris_media_player2_player_complete_play_pause (player_skeleton, invocation);
return TRUE;
}
static gboolean
handle_seek_cb (GstClapperMprisMediaPlayer2Player * player_skeleton,
GDBusMethodInvocation * invocation, gint64 offset, gpointer user_data)
{
GstClapper *clapper = GST_CLAPPER (user_data);
GST_DEBUG ("Handle Seek");
gst_clapper_seek_offset (clapper, offset * GST_USECOND);
gst_clapper_mpris_media_player2_player_complete_seek (player_skeleton, invocation);
return TRUE;
}
static gboolean
handle_set_position_cb (GstClapperMprisMediaPlayer2Player * player_skeleton,
GDBusMethodInvocation * invocation, const gchar * track_id,
gint64 position, gpointer user_data)
{
GstClapper *clapper = GST_CLAPPER (user_data);
GST_DEBUG ("Handle SetPosition");
gst_clapper_seek (clapper, position * GST_USECOND);
gst_clapper_mpris_media_player2_player_complete_set_position (player_skeleton, invocation);
return TRUE;
}
static gboolean
handle_open_uri_cb (GstClapperMprisMediaPlayer2Player * player_skeleton,
GDBusMethodInvocation * invocation, const gchar * uri,
gpointer user_data)
{
GstClapper *clapper = GST_CLAPPER (user_data);
GST_DEBUG ("Handle OpenUri");
/* 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;
}
static void
volume_notify_dispatch (gpointer user_data)
{
GstClapperMpris *self = user_data;
g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_VOLUME]);
}
static void
handle_volume_notify_cb (G_GNUC_UNUSED GObject * obj,
G_GNUC_UNUSED GParamSpec * pspec, GstClapperMpris * self)
{
gst_clapper_signal_dispatcher_dispatch (self->signal_dispatcher, NULL,
volume_notify_dispatch, g_object_ref (self),
(GDestroyNotify) g_object_unref);
}
static void
unregister (GstClapperMpris * self)
{
if (!self->name_id)
return;
GST_DEBUG_OBJECT (self, "Unregister");
g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (self->base_skeleton));
g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (self->player_skeleton));
g_bus_unown_name (self->name_id);
self->name_id = 0;
}
static const gchar *
_get_mpris_trackid (GstClapperMpris * self)
{
/* TODO: Support more tracks */
return g_strdup_printf ("%s%s%i", self->id_path, "/Track/", 0);
}
static void
_set_supported_uri_schemes (GstClapperMpris * self)
{
const gchar *uri_schemes[96] = {};
GList *elements, *el;
guint index = 0;
elements = gst_element_factory_list_get_elements (
GST_ELEMENT_FACTORY_TYPE_SRC, GST_RANK_NONE);
for (el = elements; el != NULL; el = el->next) {
const gchar *const *protocols;
GstElementFactory *factory = GST_ELEMENT_FACTORY (el->data);
if (gst_element_factory_get_uri_type (factory) != GST_URI_SRC)
continue;
protocols = gst_element_factory_get_uri_protocols (factory);
if (protocols == NULL || *protocols == NULL)
continue;
while (*protocols != NULL) {
guint j = index;
while (j--) {
if (strcmp (uri_schemes[j], *protocols) == 0)
goto next;
}
uri_schemes[index] = *protocols;
GST_DEBUG_OBJECT (self, "Added supported URI scheme: %s", *protocols);
++index;
next:
++protocols;
}
}
gst_plugin_feature_list_free (elements);
gst_clapper_mpris_media_player2_set_supported_uri_schemes (
self->base_skeleton, uri_schemes);
}
static void
name_acquired_cb (GDBusConnection * connection,
const gchar *name, gpointer user_data)
{
GstClapperMpris *self = GST_CLAPPER_MPRIS (user_data);
GVariantBuilder builder;
g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self->base_skeleton),
connection, "/org/mpris/MediaPlayer2", NULL);
g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self->player_skeleton),
connection, "/org/mpris/MediaPlayer2", NULL);
if (self->identity)
gst_clapper_mpris_media_player2_set_identity (self->base_skeleton, self->identity);
if (self->desktop_entry)
gst_clapper_mpris_media_player2_set_desktop_entry (self->base_skeleton, self->desktop_entry);
_set_supported_uri_schemes (self);
gst_clapper_mpris_media_player2_player_set_playback_status (self->player_skeleton, "Stopped");
gst_clapper_mpris_media_player2_player_set_minimum_rate (self->player_skeleton, 0.01);
gst_clapper_mpris_media_player2_player_set_maximum_rate (self->player_skeleton, 2.0);
gst_clapper_mpris_media_player2_player_set_can_seek (self->player_skeleton, TRUE);
gst_clapper_mpris_media_player2_player_set_can_control (self->player_skeleton, TRUE);
g_object_bind_property (self->player_skeleton, "can-play",
self->player_skeleton, "can-pause", G_BINDING_DEFAULT);
g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
g_variant_builder_add (&builder, "{sv}", "mpris:trackid", g_variant_new_string (_get_mpris_trackid (self)));
g_variant_builder_add (&builder, "{sv}", "mpris:length", g_variant_new_uint64 (0));
if (self->default_art_url)
g_variant_builder_add (&builder, "{sv}", "mpris:artUrl", g_variant_new_string (self->default_art_url));
gst_clapper_mpris_media_player2_player_set_metadata (self->player_skeleton, g_variant_builder_end (&builder));
GST_DEBUG_OBJECT (self, "Ready");
}
static void
name_lost_cb (GDBusConnection * connection,
const gchar * name, gpointer user_data)
{
GstClapperMpris *self = GST_CLAPPER_MPRIS (user_data);
unregister (self);
}
static gboolean
mpris_update_props_dispatch (gpointer user_data)
{
GstClapperMpris *self = GST_CLAPPER_MPRIS (user_data);
GST_DEBUG_OBJECT (self, "Updating MPRIS props");
g_mutex_lock (&self->lock);
if (self->parse_media_info) {
GVariantBuilder builder;
guint64 duration;
const gchar *track_id, *uri, *title;
GST_DEBUG_OBJECT (self, "Parsing media info");
g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
track_id = _get_mpris_trackid (self);
uri = gst_clapper_media_info_get_uri (self->media_info);
title = gst_clapper_media_info_get_title (self->media_info);
if (track_id) {
g_variant_builder_add (&builder, "{sv}", "mpris:trackid",
g_variant_new_string (track_id));
GST_DEBUG_OBJECT (self, "mpris:trackid: %s", track_id);
}
if (uri) {
g_variant_builder_add (&builder, "{sv}", "xesam:url",
g_variant_new_string (uri));
GST_DEBUG_OBJECT (self, "xesam:url: %s", uri);
}
if (title) {
g_variant_builder_add (&builder, "{sv}", "xesam:title",
g_variant_new_string (title));
GST_DEBUG_OBJECT (self, "xesam:title: %s", title);
}
duration = gst_clapper_media_info_get_duration (self->media_info);
duration = (duration != GST_CLOCK_TIME_NONE) ? duration / GST_USECOND : 0;
g_variant_builder_add (&builder, "{sv}", "mpris:length", g_variant_new_uint64 (duration));
GST_DEBUG_OBJECT (self, "mpris:length: %ld", duration);
/* TODO: Check for image sample */
if (self->default_art_url) {
g_variant_builder_add (&builder, "{sv}", "mpris:artUrl", g_variant_new_string (self->default_art_url));
GST_DEBUG_OBJECT (self, "mpris:artUrl: %s", self->default_art_url);
}
GST_DEBUG_OBJECT (self, "Media info parsed");
self->parse_media_info = FALSE;
gst_clapper_mpris_media_player2_player_set_metadata (
self->player_skeleton, g_variant_builder_end (&builder));
}
if (gst_clapper_mpris_media_player2_player_get_can_play (
self->player_skeleton) != self->can_play) {
/* "can-play" is bound with "can-pause" */
gst_clapper_mpris_media_player2_player_set_can_play (
self->player_skeleton, self->can_play);
GST_DEBUG_OBJECT (self, "CanPlay/CanPause: %s", self->can_play ? "yes" : "no");
}
if (strcmp (gst_clapper_mpris_media_player2_player_get_playback_status (
self->player_skeleton), self->playback_status) != 0) {
gst_clapper_mpris_media_player2_player_set_playback_status (
self->player_skeleton, self->playback_status);
GST_DEBUG_OBJECT (self, "PlaybackStatus: %s", self->playback_status);
}
if (gst_clapper_mpris_media_player2_player_get_position (
self->player_skeleton) != self->position) {
gst_clapper_mpris_media_player2_player_set_position (
self->player_skeleton, self->position);
GST_DEBUG_OBJECT (self, "Position: %ld", self->position);
}
g_mutex_unlock (&self->lock);
GST_DEBUG_OBJECT (self, "MPRIS props updated");
return G_SOURCE_REMOVE;
}
static void
mpris_dispatcher_update_dispatch (GstClapperMpris * self)
{
if (!self->name_id)
return;
GST_DEBUG_OBJECT (self, "Queued update props dispatch");
g_main_context_invoke_full (self->context,
G_PRIORITY_DEFAULT, mpris_update_props_dispatch,
g_object_ref (self), g_object_unref);
}
static gpointer
gst_clapper_mpris_main (gpointer data)
{
GstClapperMpris *self = GST_CLAPPER_MPRIS (data);
GDBusConnectionFlags flags;
GDBusConnection *connection;
GSource *source;
gchar *address;
GST_TRACE_OBJECT (self, "Starting main thread");
g_main_context_push_thread_default (self->context);
source = g_idle_source_new ();
g_source_set_callback (source, (GSourceFunc) main_loop_running_cb, self,
NULL);
g_source_attach (source, self->context);
g_source_unref (source);
address = g_dbus_address_get_for_bus_sync (G_BUS_TYPE_SESSION, NULL, NULL);
if (!address) {
GST_WARNING_OBJECT (self, "No MPRIS bus address");
goto no_mpris;
}
GST_DEBUG_OBJECT (self, "Obtained MPRIS DBus address");
flags = G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION;
connection = g_dbus_connection_new_for_address_sync (address,
flags, NULL, NULL, NULL);
g_free (address);
if (!connection) {
GST_WARNING_OBJECT (self, "No MPRIS bus connection");
goto no_mpris;
}
GST_DEBUG_OBJECT (self, "Obtained MPRIS DBus connection");
self->name_id = g_bus_own_name_on_connection (connection, self->own_name,
G_BUS_NAME_OWNER_FLAGS_NONE,
(GBusNameAcquiredCallback) name_acquired_cb,
(GBusNameLostCallback) name_lost_cb,
self, NULL);
g_object_unref (connection);
goto done;
no_mpris:
g_warning ("GstClapperMpris: failed to create DBus connection");
done:
GST_TRACE_OBJECT (self, "Starting main loop");
g_main_loop_run (self->loop);
GST_TRACE_OBJECT (self, "Stopped main loop");
unregister (self);
g_main_context_pop_thread_default (self->context);
GST_TRACE_OBJECT (self, "Stopped main thread");
return NULL;
}
void
gst_clapper_mpris_set_clapper (GstClapperMpris * self, GstClapper * clapper,
GstClapperSignalDispatcher * signal_dispatcher)
{
if (signal_dispatcher)
self->signal_dispatcher = g_object_ref (signal_dispatcher);
g_signal_connect (self->player_skeleton, "handle-play",
G_CALLBACK (handle_play_cb), clapper);
g_signal_connect (self->player_skeleton, "handle-pause",
G_CALLBACK (handle_pause_cb), clapper);
g_signal_connect (self->player_skeleton, "handle-play-pause",
G_CALLBACK (handle_play_pause_cb), clapper);
g_signal_connect (self->player_skeleton, "handle-seek",
G_CALLBACK (handle_seek_cb), clapper);
g_signal_connect (self->player_skeleton, "handle-set-position",
G_CALLBACK (handle_set_position_cb), clapper);
g_signal_connect (self->player_skeleton, "handle-open-uri",
G_CALLBACK (handle_open_uri_cb), clapper);
g_object_bind_property (clapper, "volume", self, "volume", G_BINDING_BIDIRECTIONAL);
g_signal_connect (self->player_skeleton, "notify::volume",
G_CALLBACK (handle_volume_notify_cb), self);
}
void
gst_clapper_mpris_set_playback_status (GstClapperMpris * self, const gchar * status)
{
g_mutex_lock (&self->lock);
if (strcmp (self->playback_status, status) == 0) {
g_mutex_unlock (&self->lock);
return;
}
g_free (self->playback_status);
self->playback_status = g_strdup (status);
self->can_play = strcmp (status, "Stopped") != 0;
g_mutex_unlock (&self->lock);
mpris_dispatcher_update_dispatch (self);
}
void
gst_clapper_mpris_set_position (GstClapperMpris * self, gint64 position)
{
position /= GST_USECOND;
g_mutex_lock (&self->lock);
if (self->position == position) {
g_mutex_unlock (&self->lock);
return;
}
self->position = position;
g_mutex_unlock (&self->lock);
mpris_dispatcher_update_dispatch (self);
}
void
gst_clapper_mpris_set_media_info (GstClapperMpris *self, GstClapperMediaInfo *info)
{
g_mutex_lock (&self->lock);
if (self->media_info)
g_object_unref (self->media_info);
self->media_info = info;
self->parse_media_info = TRUE;
g_mutex_unlock (&self->lock);
mpris_dispatcher_update_dispatch (self);
}
/**
* gst_clapper_mpris_new:
* @own_name: DBus own name
* @id_path: DBus id path used for prefix
* @identity: (allow-none): friendly name
* @desktop_entry: (allow-none): Desktop entry filename
* @default_art_url: (allow-none): filepath to default art
*
* Creates a new #GstClapperMpris instance.
*
* Returns: (transfer full): a new #GstClapperMpris instance
*/
GstClapperMpris *
gst_clapper_mpris_new (const gchar * own_name, const gchar * id_path,
const gchar * identity, const gchar * desktop_entry,
const gchar * default_art_url)
{
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);
}

View File

@@ -0,0 +1,54 @@
/*
* 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_MPRIS_H__
#define __GST_CLAPPER_MPRIS_H__
#include <glib.h>
#include <gio/gio.h>
#include <gst/clapper/clapper-prelude.h>
G_BEGIN_DECLS
typedef struct _GstClapperMpris GstClapperMpris;
typedef struct _GstClapperMprisClass GstClapperMprisClass;
#define GST_TYPE_CLAPPER_MPRIS (gst_clapper_mpris_get_type ())
#define GST_IS_CLAPPER_MPRIS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CLAPPER_MPRIS))
#define GST_IS_CLAPPER_MPRIS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_CLAPPER_MPRIS))
#define GST_CLAPPER_MPRIS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CLAPPER_MPRIS, GstClapperMprisClass))
#define GST_CLAPPER_MPRIS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_CLAPPER_MPRIS, GstClapperMpris))
#define GST_CLAPPER_MPRIS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_CLAPPER_MPRIS, GstClapperMprisClass))
#define GST_CLAPPER_MPRIS_CAST(obj) ((GstClapperMpris*)(obj))
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstClapperMpris, g_object_unref)
#endif
GST_CLAPPER_API
GType gst_clapper_mpris_get_type (void);
GST_CLAPPER_API
GstClapperMpris * gst_clapper_mpris_new (const gchar *own_name, const gchar *id_path, const gchar *identity,
const gchar *desktop_entry, const gchar *default_art_url);
G_END_DECLS
#endif /* __GST_CLAPPER_MPRIS_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -30,6 +30,7 @@
#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-mpris.h>
G_BEGIN_DECLS
@@ -153,7 +154,11 @@ GST_CLAPPER_API
GType gst_clapper_get_type (void);
GST_CLAPPER_API
GstClapper * gst_clapper_new (GstClapperVideoRenderer *video_renderer, GstClapperSignalDispatcher *signal_dispatcher);
void gst_clapper_gst_init (int *argc, char **argv[]);
GST_CLAPPER_API
GstClapper * gst_clapper_new (GstClapperVideoRenderer *video_renderer, GstClapperSignalDispatcher *signal_dispatcher,
GstClapperMpris *mpris);
GST_CLAPPER_API
void gst_clapper_play (GstClapper *clapper);
@@ -161,12 +166,22 @@ void gst_clapper_play (GstClapper *clapper
GST_CLAPPER_API
void gst_clapper_pause (GstClapper *clapper);
GST_CLAPPER_API
void gst_clapper_toggle_play (GstClapper *clapper);
GST_CLAPPER_API
void gst_clapper_stop (GstClapper *clapper);
GST_CLAPPER_API
void gst_clapper_seek (GstClapper *clapper, GstClockTime position);
GST_CLAPPER_API
void gst_clapper_seek_offset (GstClapper *clapper, GstClockTime offset);
GST_CLAPPER_API
GstClapperState
gst_clapper_get_state (GstClapper *clapper);
GST_CLAPPER_API
GstClapperSeekMode
gst_clapper_get_seek_mode (GstClapper *clapper);
@@ -213,6 +228,10 @@ void gst_clapper_set_mute (GstClapper *clapper
GST_CLAPPER_API
GstElement * gst_clapper_get_pipeline (GstClapper *clapper);
GST_CLAPPER_API
GstClapperMpris *
gst_clapper_get_mpris (GstClapper *clapper);
GST_CLAPPER_API
void gst_clapper_set_video_track_enabled (GstClapper *clapper, gboolean enabled);

View File

@@ -0,0 +1,759 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
* Copyright (C) 2020 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.
*/
/**
* SECTION:gstclapperglsink
* @title: GstClapperGLSink
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gl/gstglfuncs.h>
#include "gstclapperglsink.h"
#include "gstgtkutils.h"
GST_DEBUG_CATEGORY (gst_debug_clapper_gl_sink);
#define GST_CAT_DEFAULT gst_debug_clapper_gl_sink
#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_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,
const GValue * value, GParamSpec * param_spec);
static void gst_clapper_gl_sink_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * param_spec);
static gboolean gst_clapper_gl_sink_propose_allocation (GstBaseSink * bsink,
GstQuery * query);
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,
GstStateChange transition);
static void gst_clapper_gl_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
GstClockTime * start, GstClockTime * end);
static GstCaps *gst_clapper_gl_sink_get_caps (GstBaseSink * bsink,
GstCaps * filter);
static gboolean gst_clapper_gl_sink_set_caps (GstBaseSink * bsink,
GstCaps * caps);
static GstFlowReturn gst_clapper_gl_sink_show_frame (GstVideoSink * bsink,
GstBuffer * buf);
static void
gst_clapper_gl_sink_navigation_interface_init (GstNavigationInterface * iface);
#define gst_clapper_gl_sink_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstClapperGLSink, gst_clapper_gl_sink,
GST_TYPE_VIDEO_SINK,
G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
gst_clapper_gl_sink_navigation_interface_init);
GST_DEBUG_CATEGORY_INIT (gst_debug_clapper_gl_sink,
"clapperglsink", 0, "Clapper GL Sink"));
static void
gst_clapper_gl_sink_class_init (GstClapperGLSinkClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
GstBaseSinkClass *gstbasesink_class;
GstVideoSinkClass *gstvideosink_class;
GstClapperGLSinkClass *gstclapperglsink_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gstbasesink_class = (GstBaseSinkClass *) klass;
gstvideosink_class = (GstVideoSinkClass *) klass;
gstclapperglsink_class = (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",
"The GtkWidget to place in the widget hierarchy "
"(must only be get from the GTK main thread)",
GTK_TYPE_WIDGET, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
gst_gtk_install_shared_properties (gobject_class);
gstelement_class->change_state = gst_clapper_gl_sink_change_state;
gstbasesink_class->get_caps = gst_clapper_gl_sink_get_caps;
gstbasesink_class->set_caps = gst_clapper_gl_sink_set_caps;
gstbasesink_class->get_times = gst_clapper_gl_sink_get_times;
gstbasesink_class->propose_allocation = gst_clapper_gl_sink_propose_allocation;
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;
gstclapperglsink_class->create_widget = gtk_clapper_gl_widget_new;
gstclapperglsink_class->window_title = "GTK4 GL Renderer";
gst_element_class_set_metadata (gstelement_class,
"GTK4 GL Video Sink",
"Sink/Video", "A video sink that renders to a GtkWidget using OpenGL",
"Matthew Waters <matthew@centricular.com>, "
"Rafał Dzięgiel <rafostar.github@gmail.com>");
gst_element_class_add_static_pad_template (gstelement_class,
&gst_clapper_gl_sink_template);
gst_type_mark_as_plugin_api (GST_TYPE_CLAPPER_GL_SINK, 0);
}
static void
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
gst_clapper_gl_sink_finalize (GObject * object)
{
GstClapperGLSink *clapper_sink = GST_CLAPPER_GL_SINK (object);
GST_DEBUG ("Finalizing Clapper GL sink");
GST_OBJECT_LOCK (clapper_sink);
if (clapper_sink->window && clapper_sink->window_destroy_id)
g_signal_handler_disconnect (clapper_sink->window, clapper_sink->window_destroy_id);
if (clapper_sink->widget && clapper_sink->widget_destroy_id)
g_signal_handler_disconnect (clapper_sink->widget, clapper_sink->widget_destroy_id);
g_clear_object (&clapper_sink->widget);
GST_OBJECT_UNLOCK (clapper_sink);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
widget_destroy_cb (GtkWidget * widget, GstClapperGLSink * clapper_sink)
{
GST_OBJECT_LOCK (clapper_sink);
g_clear_object (&clapper_sink->widget);
GST_OBJECT_UNLOCK (clapper_sink);
}
static void
window_destroy_cb (GtkWidget * widget, GstClapperGLSink * clapper_sink)
{
GST_OBJECT_LOCK (clapper_sink);
if (clapper_sink->widget) {
if (clapper_sink->widget_destroy_id) {
g_signal_handler_disconnect (clapper_sink->widget,
clapper_sink->widget_destroy_id);
clapper_sink->widget_destroy_id = 0;
}
g_clear_object (&clapper_sink->widget);
}
clapper_sink->window = NULL;
GST_OBJECT_UNLOCK (clapper_sink);
}
static GtkClapperGLWidget *
gst_clapper_gl_sink_get_widget (GstClapperGLSink * clapper_sink)
{
if (clapper_sink->widget != NULL)
return clapper_sink->widget;
/* Ensure GTK is initialized, this has no side effect if it was already
* initialized. Also, we do that lazily, so the application can be first */
if (!gtk_init_check ()) {
GST_ERROR_OBJECT (clapper_sink, "Could not ensure GTK initialization.");
return NULL;
}
g_assert (GST_CLAPPER_GL_SINK_GET_CLASS (clapper_sink)->create_widget);
clapper_sink->widget = (GtkClapperGLWidget *)
GST_CLAPPER_GL_SINK_GET_CLASS (clapper_sink)->create_widget ();
g_object_bind_property (clapper_sink, "force-aspect-ratio", clapper_sink->widget,
"force-aspect-ratio", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
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. */
gst_object_ref_sink (clapper_sink->widget);
clapper_sink->widget_destroy_id = g_signal_connect (clapper_sink->widget, "destroy",
G_CALLBACK (widget_destroy_cb), clapper_sink);
/* back pointer */
gtk_clapper_gl_widget_set_element (GTK_CLAPPER_GL_WIDGET (clapper_sink->widget),
GST_ELEMENT (clapper_sink));
return clapper_sink->widget;
}
static void
gst_clapper_gl_sink_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstClapperGLSink *clapper_sink = GST_CLAPPER_GL_SINK (object);
switch (prop_id) {
case PROP_WIDGET:
{
GObject *widget = NULL;
GST_OBJECT_LOCK (clapper_sink);
if (clapper_sink->widget != NULL)
widget = G_OBJECT (clapper_sink->widget);
GST_OBJECT_UNLOCK (clapper_sink);
if (!widget)
widget =
gst_gtk_invoke_on_main ((GThreadFunc) gst_clapper_gl_sink_get_widget,
clapper_sink);
g_value_set_object (value, widget);
break;
}
case PROP_FORCE_ASPECT_RATIO:
g_value_set_boolean (value, clapper_sink->force_aspect_ratio);
break;
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;
}
}
static void
gst_clapper_gl_sink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstClapperGLSink *clapper_sink = GST_CLAPPER_GL_SINK (object);
switch (prop_id) {
case PROP_FORCE_ASPECT_RATIO:
clapper_sink->force_aspect_ratio = g_value_get_boolean (value);
break;
case PROP_PIXEL_ASPECT_RATIO:
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;
}
}
static void
gst_clapper_gl_sink_navigation_send_event (GstNavigation * navigation,
GstStructure * structure)
{
GstClapperGLSink *sink = GST_CLAPPER_GL_SINK (navigation);
GstEvent *event;
GstPad *pad;
event = gst_event_new_navigation (structure);
pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (sink));
GST_TRACE_OBJECT (sink, "navigation event %" GST_PTR_FORMAT, structure);
if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
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_event_unref (event);
gst_object_unref (pad);
}
}
static void
gst_clapper_gl_sink_navigation_interface_init (GstNavigationInterface * iface)
{
iface->send_event = gst_clapper_gl_sink_navigation_send_event;
}
static gboolean
gst_clapper_gl_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
{
GstClapperGLSink *clapper_sink = GST_CLAPPER_GL_SINK (bsink);
GstBufferPool *pool = NULL;
GstStructure *config;
GstCaps *caps;
GstVideoInfo info;
guint size;
gboolean need_pool;
GstStructure *allocation_meta = NULL;
gint display_width, display_height;
if (!clapper_sink->display || !clapper_sink->context)
return FALSE;
gst_query_parse_allocation (query, &caps, &need_pool);
if (caps == NULL)
goto no_caps;
if (!gst_video_info_from_caps (&info, caps))
goto invalid_caps;
/* the normal size of a frame */
size = info.size;
if (need_pool) {
GST_DEBUG_OBJECT (clapper_sink, "create new pool");
pool = gst_gl_buffer_pool_new (clapper_sink->context);
config = gst_buffer_pool_get_config (pool);
gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
gst_buffer_pool_config_add_option (config,
GST_BUFFER_POOL_OPTION_GL_SYNC_META);
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 */
gst_query_add_allocation_pool (query, pool, size, 2, 0);
if (pool)
gst_object_unref (pool);
GST_OBJECT_LOCK (clapper_sink);
display_width = clapper_sink->display_width;
display_height = clapper_sink->display_height;
GST_OBJECT_UNLOCK (clapper_sink);
if (display_width != 0 && display_height != 0) {
GST_DEBUG_OBJECT (clapper_sink, "sending alloc query with size %dx%d",
display_width, display_height);
allocation_meta = gst_structure_new ("GstVideoOverlayCompositionMeta",
"width", G_TYPE_UINT, display_width,
"height", G_TYPE_UINT, display_height, NULL);
}
gst_query_add_allocation_meta (query,
GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, allocation_meta);
if (allocation_meta)
gst_structure_free (allocation_meta);
/* we also support various metadata */
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, 0);
if (clapper_sink->context->gl_vtable->FenceSync)
gst_query_add_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, 0);
return TRUE;
/* ERRORS */
no_caps:
{
GST_DEBUG_OBJECT (bsink, "no caps specified");
return FALSE;
}
invalid_caps:
{
GST_DEBUG_OBJECT (bsink, "invalid caps specified");
return FALSE;
}
config_failed:
{
GST_DEBUG_OBJECT (bsink, "failed setting config");
return FALSE;
}
}
static gboolean
gst_clapper_gl_sink_query (GstBaseSink * bsink, GstQuery * query)
{
GstClapperGLSink *clapper_sink = GST_CLAPPER_GL_SINK (bsink);
gboolean res = FALSE;
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_CONTEXT:
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;
}
return res;
}
static gboolean
gst_clapper_gl_sink_start_on_main (GstBaseSink * bsink)
{
GstClapperGLSink *gst_sink = GST_CLAPPER_GL_SINK (bsink);
GstClapperGLSinkClass *klass = GST_CLAPPER_GL_SINK_GET_CLASS (bsink);
GtkWidget *toplevel;
GtkRoot *root;
if (gst_clapper_gl_sink_get_widget (gst_sink) == NULL)
return FALSE;
/* After this point, clapper_sink->widget will always be set */
root = gtk_widget_get_root (GTK_WIDGET (gst_sink->widget));
if (!GTK_IS_ROOT (root)) {
GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (gst_sink->widget));
if (parent) {
GtkWidget *temp_parent;
while ((temp_parent = gtk_widget_get_parent (parent)))
parent = temp_parent;
}
toplevel = (parent) ? parent : GTK_WIDGET (gst_sink->widget);
/* sanity check */
g_assert (klass->window_title);
/* User did not add widget its own UI, let's popup a new GtkWindow to
* make gst-launch-1.0 work. */
gst_sink->window = gtk_window_new ();
gtk_window_set_default_size (GTK_WINDOW (gst_sink->window), 640, 480);
gtk_window_set_title (GTK_WINDOW (gst_sink->window), klass->window_title);
gtk_window_set_child (GTK_WINDOW (gst_sink->window), toplevel);
gst_sink->window_destroy_id = g_signal_connect (
GTK_WINDOW (gst_sink->window),
"destroy", G_CALLBACK (window_destroy_cb), gst_sink);
}
return TRUE;
}
static gboolean
gst_clapper_gl_sink_start (GstBaseSink * bsink)
{
GstClapperGLSink *clapper_sink = GST_CLAPPER_GL_SINK (bsink);
GtkClapperGLWidget *clapper_widget;
if (!(! !gst_gtk_invoke_on_main ((GThreadFunc) (GCallback)
gst_clapper_gl_sink_start_on_main, bsink)))
return FALSE;
/* After this point, clapper_sink->widget will always be set */
clapper_widget = GTK_CLAPPER_GL_WIDGET (clapper_sink->widget);
if (!gtk_clapper_gl_widget_init_winsys (clapper_widget)) {
GST_ELEMENT_ERROR (bsink, RESOURCE, NOT_FOUND, ("%s",
"Failed to initialize OpenGL with GTK"), (NULL));
return FALSE;
}
if (!clapper_sink->display)
clapper_sink->display = gtk_clapper_gl_widget_get_display (clapper_widget);
if (!clapper_sink->context)
clapper_sink->context = gtk_clapper_gl_widget_get_context (clapper_widget);
if (!clapper_sink->gtk_context)
clapper_sink->gtk_context = gtk_clapper_gl_widget_get_gtk_context (clapper_widget);
if (!clapper_sink->display || !clapper_sink->context || !clapper_sink->gtk_context) {
GST_ELEMENT_ERROR (bsink, RESOURCE, NOT_FOUND, ("%s",
"Failed to retrieve OpenGL context from GTK"), (NULL));
return FALSE;
}
gst_gl_element_propagate_display_context (GST_ELEMENT (bsink),
clapper_sink->display);
return TRUE;
}
static gboolean
gst_clapper_gl_sink_stop_on_main (GstBaseSink * bsink)
{
GstClapperGLSink *gst_sink = GST_CLAPPER_GL_SINK (bsink);
if (gst_sink->window) {
gtk_window_destroy (GTK_WINDOW (gst_sink->window));
gst_sink->window = NULL;
gst_sink->widget = NULL;
}
return TRUE;
}
static gboolean
gst_clapper_gl_sink_stop (GstBaseSink * bsink)
{
GstClapperGLSink *clapper_sink = GST_CLAPPER_GL_SINK (bsink);
if (clapper_sink->display) {
gst_object_unref (clapper_sink->display);
clapper_sink->display = NULL;
}
if (clapper_sink->context) {
gst_object_unref (clapper_sink->context);
clapper_sink->context = NULL;
}
if (clapper_sink->gtk_context) {
gst_object_unref (clapper_sink->gtk_context);
clapper_sink->gtk_context = NULL;
}
if (clapper_sink->window)
return ! !gst_gtk_invoke_on_main ((GThreadFunc) (GCallback)
gst_clapper_gl_sink_stop_on_main, bsink);
return TRUE;
}
static void
gst_gtk_window_show_all_and_unref (GtkWidget * window)
{
gtk_window_present (GTK_WINDOW (window));
g_object_unref (window);
}
static GstStateChangeReturn
gst_clapper_gl_sink_change_state (GstElement * element, GstStateChange transition)
{
GstClapperGLSink *clapper_sink = GST_CLAPPER_GL_SINK (element);
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
GST_DEBUG_OBJECT (element, "changing state: %s => %s",
gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
if (ret == GST_STATE_CHANGE_FAILURE)
return ret;
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;
GTK_CLAPPER_GL_WIDGET_UNLOCK (clapper_sink->widget);
}
GST_OBJECT_UNLOCK (clapper_sink);
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:{
GtkWindow *window = NULL;
GST_OBJECT_LOCK (clapper_sink);
if (clapper_sink->window)
window = g_object_ref (GTK_WINDOW (clapper_sink->window));
GST_OBJECT_UNLOCK (clapper_sink);
if (window) {
gst_gtk_invoke_on_main ((GThreadFunc) (GCallback)
gst_gtk_window_show_all_and_unref, window);
}
break;
}
case GST_STATE_CHANGE_READY_TO_NULL:
GST_OBJECT_LOCK (clapper_sink);
if (clapper_sink->widget) {
GTK_CLAPPER_GL_WIDGET_LOCK (clapper_sink->widget);
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);
/* Fall through to render black bg */
case GST_STATE_CHANGE_PAUSED_TO_READY:
GST_OBJECT_LOCK (clapper_sink);
if (clapper_sink->widget)
gtk_clapper_gl_widget_set_buffer (clapper_sink->widget, NULL);
GST_OBJECT_UNLOCK (clapper_sink);
break;
default:
break;
}
return ret;
}
static void
gst_clapper_gl_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
GstClockTime * start, GstClockTime * end)
{
GstClapperGLSink *clapper_sink = GST_CLAPPER_GL_SINK (bsink);
if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
*start = GST_BUFFER_TIMESTAMP (buf);
if (GST_BUFFER_DURATION_IS_VALID (buf))
*end = *start + GST_BUFFER_DURATION (buf);
else {
if (GST_VIDEO_INFO_FPS_N (&clapper_sink->v_info) > 0) {
*end = *start +
gst_util_uint64_scale_int (GST_SECOND,
GST_VIDEO_INFO_FPS_D (&clapper_sink->v_info),
GST_VIDEO_INFO_FPS_N (&clapper_sink->v_info));
}
}
}
}
static GstCaps *
gst_clapper_gl_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
{
GstCaps *tmp = NULL;
GstCaps *result = NULL;
tmp = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (bsink));
if (filter) {
GST_DEBUG_OBJECT (bsink, "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;
}
result = gst_gl_overlay_compositor_add_caps (result);
GST_DEBUG_OBJECT (bsink, "returning caps: %" GST_PTR_FORMAT, result);
return result;
}
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);
if (!gst_video_info_from_caps (&clapper_sink->v_info, caps))
return FALSE;
GST_OBJECT_LOCK (clapper_sink);
if (clapper_sink->widget == NULL) {
GST_OBJECT_UNLOCK (clapper_sink);
GST_ELEMENT_ERROR (clapper_sink, RESOURCE, NOT_FOUND,
("%s", "Output widget was destroyed"), (NULL));
return FALSE;
}
if (!gtk_clapper_gl_widget_set_format (clapper_sink->widget, &clapper_sink->v_info)) {
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 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
gst_clapper_gl_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
{
GstClapperGLSink *clapper_sink;
GST_TRACE ("rendering buffer:%p", buf);
clapper_sink = GST_CLAPPER_GL_SINK (vsink);
GST_OBJECT_LOCK (clapper_sink);
if (clapper_sink->widget == NULL) {
GST_OBJECT_UNLOCK (clapper_sink);
GST_ELEMENT_ERROR (clapper_sink, RESOURCE, NOT_FOUND,
("%s", "Output widget was destroyed"), (NULL));
return GST_FLOW_ERROR;
}
gtk_clapper_gl_widget_set_buffer (clapper_sink->widget, buf);
GST_OBJECT_UNLOCK (clapper_sink);
return GST_FLOW_OK;
}

View File

@@ -0,0 +1,105 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
* Copyright (C) 2020 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_GL_SINK_H__
#define __GST_CLAPPER_GL_SINK_H__
#include <gtk/gtk.h>
#include <gst/gst.h>
#include <gst/video/gstvideosink.h>
#include <gst/video/video.h>
#include <gst/gl/gl.h>
#include "gtkclapperglwidget.h"
#define GST_TYPE_CLAPPER_GL_SINK (gst_clapper_gl_sink_get_type())
#define GST_CLAPPER_GL_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CLAPPER_GL_SINK,GstClapperGLSink))
#define GST_CLAPPER_GL_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CLAPPER_GL_SINK,GstClapperGLClass))
#define GST_CLAPPER_GL_SINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CLAPPER_GL_SINK, GstClapperGLSinkClass))
#define GST_IS_CLAPPER_GL_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CLAPPER_GL_SINK))
#define GST_IS_CLAPPER_GL_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CLAPPER_GL_SINK))
#define GST_CLAPPER_GL_SINK_CAST(obj) ((GstClapperGLSink*)(obj))
G_BEGIN_DECLS
typedef struct _GstClapperGLSink GstClapperGLSink;
typedef struct _GstClapperGLSinkClass GstClapperGLSinkClass;
GType gst_clapper_gl_sink_get_type (void);
/**
* GstClapperGLSink:
*
* Opaque #GstClapperGLSink object
*/
struct _GstClapperGLSink
{
/* <private> */
GstVideoSink parent;
GstVideoInfo v_info;
GtkClapperGLWidget *widget;
gboolean had_eos;
/* properties */
gboolean force_aspect_ratio;
gint par_n, par_d;
gboolean keep_last_frame;
gboolean ignore_textures;
GtkWidget *window;
gulong widget_destroy_id;
gulong window_destroy_id;
GstGLDisplay *display;
GstGLContext *context;
GstGLContext *gtk_context;
GstGLUpload *upload;
GstBuffer *uploaded_buffer;
/* read/write with object lock */
gint display_width, display_height;
};
/**
* GstClapperGLSinkClass:
*
* The #GstClapperGLSinkClass struct only contains private data
*/
struct _GstClapperGLSinkClass
{
GstVideoSinkClass object_class;
/* metadata */
const gchar *window_title;
/* virtuals */
GtkWidget* (*create_widget) (void);
};
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GstClapperGLSink, gst_object_unref)
G_END_DECLS
#endif /* __GST_CLAPPER_GL_SINK_H__ */

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

View File

@@ -0,0 +1,29 @@
/*
* 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
* 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_GL_UTILS_H__
#define __GST_CLAPPER_GL_UTILS_H__
#include <gst/video/gstvideoaffinetransformationmeta.h>
void gst_clapper_gl_get_affine_transformation_meta_as_ndc (GstVideoAffineTransformationMeta *meta, gfloat *matrix);
#endif /* __GST_CLAPPER_GL_UTILS_H__ */

View File

@@ -1,574 +0,0 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
* Copyright (C) 2020 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.
*/
/**
* SECTION:gtkgstsink
* @title: GstGtkBaseSink
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstgtkbasesink.h"
#include "gstgtkutils.h"
GST_DEBUG_CATEGORY (gst_debug_gtk_base_sink);
#define GST_CAT_DEFAULT gst_debug_gtk_base_sink
#define DEFAULT_FORCE_ASPECT_RATIO TRUE
#define DEFAULT_PAR_N 0
#define DEFAULT_PAR_D 1
#define DEFAULT_IGNORE_ALPHA TRUE
#define DEFAULT_IGNORE_TEXTURES FALSE
static void gst_gtk_base_sink_finalize (GObject * object);
static void gst_gtk_base_sink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * param_spec);
static void gst_gtk_base_sink_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * param_spec);
static gboolean gst_gtk_base_sink_start (GstBaseSink * bsink);
static gboolean gst_gtk_base_sink_stop (GstBaseSink * bsink);
static GstStateChangeReturn
gst_gtk_base_sink_change_state (GstElement * element,
GstStateChange transition);
static void gst_gtk_base_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
GstClockTime * start, GstClockTime * end);
static gboolean gst_gtk_base_sink_set_caps (GstBaseSink * bsink,
GstCaps * caps);
static GstFlowReturn gst_gtk_base_sink_show_frame (GstVideoSink * bsink,
GstBuffer * buf);
static void
gst_gtk_base_sink_navigation_interface_init (GstNavigationInterface * iface);
enum
{
PROP_0,
PROP_WIDGET,
PROP_FORCE_ASPECT_RATIO,
PROP_PIXEL_ASPECT_RATIO,
PROP_IGNORE_ALPHA,
PROP_IGNORE_TEXTURES,
};
#define gst_gtk_base_sink_parent_class parent_class
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstGtkBaseSink, gst_gtk_base_sink,
GST_TYPE_VIDEO_SINK,
G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
gst_gtk_base_sink_navigation_interface_init);
GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_base_sink,
"gtkbasesink", 0, "GTK Video Sink base class"));
static void
gst_gtk_base_sink_class_init (GstGtkBaseSinkClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
GstBaseSinkClass *gstbasesink_class;
GstVideoSinkClass *gstvideosink_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gstbasesink_class = (GstBaseSinkClass *) klass;
gstvideosink_class = (GstVideoSinkClass *) klass;
gobject_class->set_property = gst_gtk_base_sink_set_property;
gobject_class->get_property = gst_gtk_base_sink_get_property;
g_object_class_install_property (gobject_class, PROP_WIDGET,
g_param_spec_object ("widget", "GTK Widget",
"The GtkWidget to place in the widget hierarchy "
"(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));
/* Disabling alpha was removed in GTK4 */
#if !defined(BUILD_FOR_GTK4)
g_object_class_install_property (gobject_class, PROP_IGNORE_ALPHA,
g_param_spec_boolean ("ignore-alpha", "Ignore Alpha",
"When enabled, alpha will be ignored and converted to black",
DEFAULT_IGNORE_ALPHA, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
#endif
g_object_class_install_property (gobject_class, PROP_IGNORE_TEXTURES,
g_param_spec_boolean ("ignore-textures", "Ignore Textures",
"When enabled, textures will be ignored and not drawn",
DEFAULT_IGNORE_TEXTURES, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gobject_class->finalize = gst_gtk_base_sink_finalize;
gstelement_class->change_state = gst_gtk_base_sink_change_state;
gstbasesink_class->set_caps = gst_gtk_base_sink_set_caps;
gstbasesink_class->get_times = gst_gtk_base_sink_get_times;
gstbasesink_class->start = gst_gtk_base_sink_start;
gstbasesink_class->stop = gst_gtk_base_sink_stop;
gstvideosink_class->show_frame = gst_gtk_base_sink_show_frame;
gst_type_mark_as_plugin_api (GST_TYPE_GTK_BASE_SINK, 0);
}
static void
gst_gtk_base_sink_init (GstGtkBaseSink * gtk_sink)
{
gtk_sink->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
gtk_sink->par_n = DEFAULT_PAR_N;
gtk_sink->par_d = DEFAULT_PAR_D;
gtk_sink->ignore_alpha = DEFAULT_IGNORE_ALPHA;
gtk_sink->ignore_textures = DEFAULT_IGNORE_TEXTURES;
}
static void
gst_gtk_base_sink_finalize (GObject * object)
{
GstGtkBaseSink *gtk_sink = GST_GTK_BASE_SINK (object);
GST_DEBUG ("finalizing base sink");
GST_OBJECT_LOCK (gtk_sink);
if (gtk_sink->window && gtk_sink->window_destroy_id)
g_signal_handler_disconnect (gtk_sink->window, gtk_sink->window_destroy_id);
if (gtk_sink->widget && gtk_sink->widget_destroy_id)
g_signal_handler_disconnect (gtk_sink->widget, gtk_sink->widget_destroy_id);
g_clear_object (&gtk_sink->widget);
GST_OBJECT_UNLOCK (gtk_sink);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
widget_destroy_cb (GtkWidget * widget, GstGtkBaseSink * gtk_sink)
{
GST_OBJECT_LOCK (gtk_sink);
g_clear_object (&gtk_sink->widget);
GST_OBJECT_UNLOCK (gtk_sink);
}
static void
window_destroy_cb (GtkWidget * widget, GstGtkBaseSink * gtk_sink)
{
GST_OBJECT_LOCK (gtk_sink);
if (gtk_sink->widget) {
if (gtk_sink->widget_destroy_id) {
g_signal_handler_disconnect (gtk_sink->widget,
gtk_sink->widget_destroy_id);
gtk_sink->widget_destroy_id = 0;
}
g_clear_object (&gtk_sink->widget);
}
gtk_sink->window = NULL;
GST_OBJECT_UNLOCK (gtk_sink);
}
static GtkGstBaseWidget *
gst_gtk_base_sink_get_widget (GstGtkBaseSink * gtk_sink)
{
if (gtk_sink->widget != NULL)
return gtk_sink->widget;
/* Ensure GTK is initialized, this has no side effect if it was already
* initialized. Also, we do that lazily, so the application can be first */
if (!gtk_init_check (
#if !defined(BUILD_FOR_GTK4)
NULL, NULL
#endif
)) {
GST_ERROR_OBJECT (gtk_sink, "Could not ensure GTK initialization.");
return NULL;
}
g_assert (GST_GTK_BASE_SINK_GET_CLASS (gtk_sink)->create_widget);
gtk_sink->widget = (GtkGstBaseWidget *)
GST_GTK_BASE_SINK_GET_CLASS (gtk_sink)->create_widget ();
gtk_sink->bind_aspect_ratio =
g_object_bind_property (gtk_sink, "force-aspect-ratio", gtk_sink->widget,
"force-aspect-ratio", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
gtk_sink->bind_pixel_aspect_ratio =
g_object_bind_property (gtk_sink, "pixel-aspect-ratio", gtk_sink->widget,
"pixel-aspect-ratio", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
#if !defined(BUILD_FOR_GTK4)
gtk_sink->bind_ignore_alpha =
g_object_bind_property (gtk_sink, "ignore-alpha", gtk_sink->widget,
"ignore-alpha", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
#endif
gtk_sink->bind_ignore_textures =
g_object_bind_property (gtk_sink, "ignore-textures", gtk_sink->widget,
"ignore-textures", 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. */
gst_object_ref_sink (gtk_sink->widget);
gtk_sink->widget_destroy_id = g_signal_connect (gtk_sink->widget, "destroy",
G_CALLBACK (widget_destroy_cb), gtk_sink);
/* back pointer */
gtk_gst_base_widget_set_element (GTK_GST_BASE_WIDGET (gtk_sink->widget),
GST_ELEMENT (gtk_sink));
return gtk_sink->widget;
}
static void
gst_gtk_base_sink_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstGtkBaseSink *gtk_sink = GST_GTK_BASE_SINK (object);
switch (prop_id) {
case PROP_WIDGET:
{
GObject *widget = NULL;
GST_OBJECT_LOCK (gtk_sink);
if (gtk_sink->widget != NULL)
widget = G_OBJECT (gtk_sink->widget);
GST_OBJECT_UNLOCK (gtk_sink);
if (!widget)
widget =
gst_gtk_invoke_on_main ((GThreadFunc) gst_gtk_base_sink_get_widget,
gtk_sink);
g_value_set_object (value, widget);
break;
}
case PROP_FORCE_ASPECT_RATIO:
g_value_set_boolean (value, gtk_sink->force_aspect_ratio);
break;
case PROP_PIXEL_ASPECT_RATIO:
gst_value_set_fraction (value, gtk_sink->par_n, gtk_sink->par_d);
break;
case PROP_IGNORE_ALPHA:
g_value_set_boolean (value, gtk_sink->ignore_alpha);
break;
case PROP_IGNORE_TEXTURES:
g_value_set_boolean (value, gtk_sink->ignore_textures);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_gtk_base_sink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstGtkBaseSink *gtk_sink = GST_GTK_BASE_SINK (object);
switch (prop_id) {
case PROP_FORCE_ASPECT_RATIO:
gtk_sink->force_aspect_ratio = g_value_get_boolean (value);
break;
case PROP_PIXEL_ASPECT_RATIO:
gtk_sink->par_n = gst_value_get_fraction_numerator (value);
gtk_sink->par_d = gst_value_get_fraction_denominator (value);
break;
case PROP_IGNORE_ALPHA:
gtk_sink->ignore_alpha = g_value_get_boolean (value);
break;
case PROP_IGNORE_TEXTURES:
gtk_sink->ignore_textures = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_gtk_base_sink_navigation_send_event (GstNavigation * navigation,
GstStructure * structure)
{
GstGtkBaseSink *sink = GST_GTK_BASE_SINK (navigation);
GstEvent *event;
GstPad *pad;
event = gst_event_new_navigation (structure);
pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (sink));
GST_TRACE_OBJECT (sink, "navigation event %" GST_PTR_FORMAT, structure);
if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
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_event_unref (event);
gst_object_unref (pad);
}
}
static void
gst_gtk_base_sink_navigation_interface_init (GstNavigationInterface * iface)
{
iface->send_event = gst_gtk_base_sink_navigation_send_event;
}
static gboolean
gst_gtk_base_sink_start_on_main (GstBaseSink * bsink)
{
GstGtkBaseSink *gst_sink = GST_GTK_BASE_SINK (bsink);
GstGtkBaseSinkClass *klass = GST_GTK_BASE_SINK_GET_CLASS (bsink);
GtkWidget *toplevel;
#if defined(BUILD_FOR_GTK4)
GtkRoot *root;
#endif
if (gst_gtk_base_sink_get_widget (gst_sink) == NULL)
return FALSE;
/* After this point, gtk_sink->widget will always be set */
#if defined(BUILD_FOR_GTK4)
root = gtk_widget_get_root (GTK_WIDGET (gst_sink->widget));
if (!GTK_IS_ROOT (root)) {
GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (gst_sink->widget));
if (parent) {
GtkWidget *temp_parent;
while ((temp_parent = gtk_widget_get_parent (parent)))
parent = temp_parent;
}
toplevel = (parent) ? parent : GTK_WIDGET (gst_sink->widget);
#else
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (gst_sink->widget));
if (!gtk_widget_is_toplevel (toplevel)) {
#endif
/* sanity check */
g_assert (klass->window_title);
/* User did not add widget its own UI, let's popup a new GtkWindow to
* make gst-launch-1.0 work. */
gst_sink->window = gtk_window_new (
#if !defined(BUILD_FOR_GTK4)
GTK_WINDOW_TOPLEVEL
#endif
);
gtk_window_set_default_size (GTK_WINDOW (gst_sink->window), 640, 480);
gtk_window_set_title (GTK_WINDOW (gst_sink->window), klass->window_title);
#if defined(BUILD_FOR_GTK4)
gtk_window_set_child (GTK_WINDOW (
#else
gtk_container_add (GTK_CONTAINER (
#endif
gst_sink->window), toplevel);
gst_sink->window_destroy_id = g_signal_connect (
#if defined(BUILD_FOR_GTK4)
GTK_WINDOW (gst_sink->window),
#else
gst_sink->window,
#endif
"destroy", G_CALLBACK (window_destroy_cb), gst_sink);
}
return TRUE;
}
static gboolean
gst_gtk_base_sink_start (GstBaseSink * bsink)
{
return ! !gst_gtk_invoke_on_main ((GThreadFunc) (GCallback)
gst_gtk_base_sink_start_on_main, bsink);
}
static gboolean
gst_gtk_base_sink_stop_on_main (GstBaseSink * bsink)
{
GstGtkBaseSink *gst_sink = GST_GTK_BASE_SINK (bsink);
if (gst_sink->window) {
#if defined(BUILD_FOR_GTK4)
gtk_window_destroy (GTK_WINDOW (gst_sink->window));
#else
gtk_widget_destroy (gst_sink->window);
#endif
gst_sink->window = NULL;
gst_sink->widget = NULL;
}
return TRUE;
}
static gboolean
gst_gtk_base_sink_stop (GstBaseSink * bsink)
{
GstGtkBaseSink *gst_sink = GST_GTK_BASE_SINK (bsink);
if (gst_sink->window)
return ! !gst_gtk_invoke_on_main ((GThreadFunc) (GCallback)
gst_gtk_base_sink_stop_on_main, bsink);
return TRUE;
}
static void
gst_gtk_window_show_all_and_unref (GtkWidget * window)
{
#if defined(BUILD_FOR_GTK4)
gtk_window_present (GTK_WINDOW (window));
#else
gtk_widget_show_all (window);
#endif
g_object_unref (window);
}
static GstStateChangeReturn
gst_gtk_base_sink_change_state (GstElement * element, GstStateChange transition)
{
GstGtkBaseSink *gtk_sink = GST_GTK_BASE_SINK (element);
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
GST_DEBUG_OBJECT (element, "changing state: %s => %s",
gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
if (ret == GST_STATE_CHANGE_FAILURE)
return ret;
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
{
GtkWindow *window = NULL;
GST_OBJECT_LOCK (gtk_sink);
if (gtk_sink->window)
window = g_object_ref (GTK_WINDOW (gtk_sink->window));
GST_OBJECT_UNLOCK (gtk_sink);
if (window) {
gst_gtk_invoke_on_main ((GThreadFunc) (GCallback)
gst_gtk_window_show_all_and_unref, window);
}
break;
}
case GST_STATE_CHANGE_PAUSED_TO_READY:
GST_OBJECT_LOCK (gtk_sink);
if (gtk_sink->widget)
gtk_gst_base_widget_set_buffer (gtk_sink->widget, NULL);
GST_OBJECT_UNLOCK (gtk_sink);
break;
default:
break;
}
return ret;
}
static void
gst_gtk_base_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
GstClockTime * start, GstClockTime * end)
{
GstGtkBaseSink *gtk_sink;
gtk_sink = GST_GTK_BASE_SINK (bsink);
if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
*start = GST_BUFFER_TIMESTAMP (buf);
if (GST_BUFFER_DURATION_IS_VALID (buf))
*end = *start + GST_BUFFER_DURATION (buf);
else {
if (GST_VIDEO_INFO_FPS_N (&gtk_sink->v_info) > 0) {
*end = *start +
gst_util_uint64_scale_int (GST_SECOND,
GST_VIDEO_INFO_FPS_D (&gtk_sink->v_info),
GST_VIDEO_INFO_FPS_N (&gtk_sink->v_info));
}
}
}
}
gboolean
gst_gtk_base_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
{
GstGtkBaseSink *gtk_sink = GST_GTK_BASE_SINK (bsink);
GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps);
if (!gst_video_info_from_caps (&gtk_sink->v_info, caps))
return FALSE;
GST_OBJECT_LOCK (gtk_sink);
if (gtk_sink->widget == NULL) {
GST_OBJECT_UNLOCK (gtk_sink);
GST_ELEMENT_ERROR (gtk_sink, RESOURCE, NOT_FOUND,
("%s", "Output widget was destroyed"), (NULL));
return FALSE;
}
if (!gtk_gst_base_widget_set_format (gtk_sink->widget, &gtk_sink->v_info)) {
GST_OBJECT_UNLOCK (gtk_sink);
return FALSE;
}
GST_OBJECT_UNLOCK (gtk_sink);
return TRUE;
}
static GstFlowReturn
gst_gtk_base_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
{
GstGtkBaseSink *gtk_sink;
GST_TRACE ("rendering buffer:%p", buf);
gtk_sink = GST_GTK_BASE_SINK (vsink);
GST_OBJECT_LOCK (gtk_sink);
if (gtk_sink->widget == NULL) {
GST_OBJECT_UNLOCK (gtk_sink);
GST_ELEMENT_ERROR (gtk_sink, RESOURCE, NOT_FOUND,
("%s", "Output widget was destroyed"), (NULL));
return GST_FLOW_ERROR;
}
gtk_gst_base_widget_set_buffer (gtk_sink->widget, buf);
GST_OBJECT_UNLOCK (gtk_sink);
return GST_FLOW_OK;
}

View File

@@ -1,99 +0,0 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.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_GTK_BASE_SINK_H__
#define __GST_GTK_BASE_SINK_H__
#include <gtk/gtk.h>
#include <gst/gst.h>
#include <gst/video/gstvideosink.h>
#include <gst/video/video.h>
#include "gtkgstbasewidget.h"
#define GST_TYPE_GTK_BASE_SINK (gst_gtk_base_sink_get_type())
#define GST_GTK_BASE_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GTK_BASE_SINK,GstGtkBaseSink))
#define GST_GTK_BASE_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GTK_BASE_SINK,GstGtkBaseSinkClass))
#define GST_GTK_BASE_SINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_GTK_BASE_SINK, GstGtkBaseSinkClass))
#define GST_IS_GTK_BASE_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GTK_BASE_SINK))
#define GST_IS_GTK_BASE_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GTK_BASE_SINK))
#define GST_GTK_BASE_SINK_CAST(obj) ((GstGtkBaseSink*)(obj))
G_BEGIN_DECLS
typedef struct _GstGtkBaseSink GstGtkBaseSink;
typedef struct _GstGtkBaseSinkClass GstGtkBaseSinkClass;
GType gst_gtk_base_sink_get_type (void);
/**
* GstGtkBaseSink:
*
* Opaque #GstGtkBaseSink object
*/
struct _GstGtkBaseSink
{
/* <private> */
GstVideoSink parent;
GstVideoInfo v_info;
GtkGstBaseWidget *widget;
/* properties */
gboolean force_aspect_ratio;
GBinding *bind_aspect_ratio;
gint par_n;
gint par_d;
GBinding *bind_pixel_aspect_ratio;
gboolean ignore_alpha;
GBinding *bind_ignore_alpha;
gboolean ignore_textures;
GBinding *bind_ignore_textures;
GtkWidget *window;
gulong widget_destroy_id;
gulong window_destroy_id;
};
/**
* GstGtkBaseSinkClass:
*
* The #GstGtkBaseSinkClass struct only contains private data
*/
struct _GstGtkBaseSinkClass
{
GstVideoSinkClass object_class;
/* metadata */
const gchar *window_title;
/* virtuals */
GtkWidget* (*create_widget) (void);
};
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GstGtkBaseSink, gst_object_unref)
G_END_DECLS
#endif /* __GST_GTK_BASE_SINK_H__ */

View File

@@ -1,336 +0,0 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
* Copyright (C) 2020 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.
*/
/**
* SECTION:element-gtkglsink
* @title: gtkglsink
*/
/**
* SECTION:element-gtk4glsink
* @title: gtk4glsink
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <gst/gl/gstglfuncs.h>
#include "gtkconfig.h"
#include "gstgtkglsink.h"
#include "gtkgstglwidget.h"
GST_DEBUG_CATEGORY (gst_debug_gtk_gl_sink);
#define GST_CAT_DEFAULT gst_debug_gtk_gl_sink
static gboolean gst_gtk_gl_sink_start (GstBaseSink * bsink);
static gboolean gst_gtk_gl_sink_stop (GstBaseSink * bsink);
static gboolean gst_gtk_gl_sink_query (GstBaseSink * bsink, GstQuery * query);
static gboolean gst_gtk_gl_sink_propose_allocation (GstBaseSink * bsink,
GstQuery * query);
static GstCaps *gst_gtk_gl_sink_get_caps (GstBaseSink * bsink,
GstCaps * filter);
static void gst_gtk_gl_sink_finalize (GObject * object);
static GstStaticPadTemplate gst_gtk_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")));
#define gst_gtk_gl_sink_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstGtkGLSink, gst_gtk_gl_sink,
GST_TYPE_GTK_BASE_SINK, GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_gl_sink,
GTKCONFIG_GLSINK, 0, GTKCONFIG_NAME " GL Video Sink"));
static void
gst_gtk_gl_sink_class_init (GstGtkGLSinkClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
GstBaseSinkClass *gstbasesink_class;
GstGtkBaseSinkClass *gstgtkbasesink_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gstbasesink_class = (GstBaseSinkClass *) klass;
gstgtkbasesink_class = (GstGtkBaseSinkClass *) klass;
gobject_class->finalize = gst_gtk_gl_sink_finalize;
gstbasesink_class->query = gst_gtk_gl_sink_query;
gstbasesink_class->propose_allocation = gst_gtk_gl_sink_propose_allocation;
gstbasesink_class->start = gst_gtk_gl_sink_start;
gstbasesink_class->stop = gst_gtk_gl_sink_stop;
gstbasesink_class->get_caps = gst_gtk_gl_sink_get_caps;
gstgtkbasesink_class->create_widget = gtk_gst_gl_widget_new;
gstgtkbasesink_class->window_title = GTKCONFIG_NAME " GL Renderer";
gst_element_class_set_metadata (gstelement_class,
GTKCONFIG_NAME " GL Video Sink",
"Sink/Video", "A video sink that renders to a GtkWidget using OpenGL",
"Matthew Waters <matthew@centricular.com>, "
"Rafał Dzięgiel <rafostar.github@gmail.com>");
gst_element_class_add_static_pad_template (gstelement_class,
&gst_gtk_gl_sink_template);
}
static void
gst_gtk_gl_sink_init (GstGtkGLSink * gtk_sink)
{
}
static gboolean
gst_gtk_gl_sink_query (GstBaseSink * bsink, GstQuery * query)
{
GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
gboolean res = FALSE;
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_CONTEXT:
{
if (gst_gl_handle_context_query ((GstElement *) gtk_sink, query,
gtk_sink->display, gtk_sink->context, gtk_sink->gtk_context))
return TRUE;
break;
}
default:
res = GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
break;
}
return res;
}
static void
destroy_cb (GtkWidget * widget, GstGtkGLSink * gtk_sink)
{
if (gtk_sink->widget_destroy_sig_handler) {
g_signal_handler_disconnect (widget, gtk_sink->widget_destroy_sig_handler);
gtk_sink->widget_destroy_sig_handler = 0;
}
}
static gboolean
gst_gtk_gl_sink_start (GstBaseSink * bsink)
{
GstGtkBaseSink *base_sink = GST_GTK_BASE_SINK (bsink);
GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
GtkGstGLWidget *gst_widget;
if (!GST_BASE_SINK_CLASS (parent_class)->start (bsink))
return FALSE;
/* After this point, gtk_sink->widget will always be set */
gst_widget = GTK_GST_GL_WIDGET (base_sink->widget);
if (!gtk_sink->widget_destroy_sig_handler) {
gtk_sink->widget_destroy_sig_handler =
g_signal_connect (gst_widget, "destroy", G_CALLBACK (destroy_cb),
gtk_sink);
}
if (!gtk_gst_gl_widget_init_winsys (gst_widget)) {
GST_ELEMENT_ERROR (bsink, RESOURCE, NOT_FOUND, ("%s",
"Failed to initialize OpenGL with GTK"), (NULL));
return FALSE;
}
if (!gtk_sink->display)
gtk_sink->display = gtk_gst_gl_widget_get_display (gst_widget);
if (!gtk_sink->context)
gtk_sink->context = gtk_gst_gl_widget_get_context (gst_widget);
if (!gtk_sink->gtk_context)
gtk_sink->gtk_context = gtk_gst_gl_widget_get_gtk_context (gst_widget);
if (!gtk_sink->display || !gtk_sink->context || !gtk_sink->gtk_context) {
GST_ELEMENT_ERROR (bsink, RESOURCE, NOT_FOUND, ("%s",
"Failed to retrieve OpenGL context from GTK"), (NULL));
return FALSE;
}
gst_gl_element_propagate_display_context (GST_ELEMENT (bsink),
gtk_sink->display);
return TRUE;
}
static gboolean
gst_gtk_gl_sink_stop (GstBaseSink * bsink)
{
GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
GstGtkBaseSink *base_sink = GST_GTK_BASE_SINK (bsink);
if (gtk_sink->display) {
gst_object_unref (gtk_sink->display);
gtk_sink->display = NULL;
}
if (gtk_sink->context) {
gst_object_unref (gtk_sink->context);
gtk_sink->context = NULL;
}
if (gtk_sink->gtk_context) {
gst_object_unref (gtk_sink->gtk_context);
gtk_sink->gtk_context = NULL;
}
return GST_BASE_SINK_CLASS (parent_class)->stop (bsink);
}
static gboolean
gst_gtk_gl_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
{
GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (bsink);
GstBufferPool *pool = NULL;
GstStructure *config;
GstCaps *caps;
GstVideoInfo info;
guint size;
gboolean need_pool;
GstStructure *allocation_meta = NULL;
gint display_width, display_height;
if (!gtk_sink->display || !gtk_sink->context)
return FALSE;
gst_query_parse_allocation (query, &caps, &need_pool);
if (caps == NULL)
goto no_caps;
if (!gst_video_info_from_caps (&info, caps))
goto invalid_caps;
/* the normal size of a frame */
size = info.size;
if (need_pool) {
GST_DEBUG_OBJECT (gtk_sink, "create new pool");
pool = gst_gl_buffer_pool_new (gtk_sink->context);
config = gst_buffer_pool_get_config (pool);
gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
gst_buffer_pool_config_add_option (config,
GST_BUFFER_POOL_OPTION_GL_SYNC_META);
if (!gst_buffer_pool_set_config (pool, config))
goto config_failed;
}
/* we need at least 2 buffer because we hold on to the last one */
gst_query_add_allocation_pool (query, pool, size, 2, 0);
if (pool)
gst_object_unref (pool);
GST_OBJECT_LOCK (gtk_sink);
display_width = gtk_sink->display_width;
display_height = gtk_sink->display_height;
GST_OBJECT_UNLOCK (gtk_sink);
if (display_width != 0 && display_height != 0) {
GST_DEBUG_OBJECT (gtk_sink, "sending alloc query with size %dx%d",
display_width, display_height);
allocation_meta = gst_structure_new ("GstVideoOverlayCompositionMeta",
"width", G_TYPE_UINT, display_width,
"height", G_TYPE_UINT, display_height, NULL);
}
gst_query_add_allocation_meta (query,
GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, allocation_meta);
if (allocation_meta)
gst_structure_free (allocation_meta);
/* we also support various metadata */
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, 0);
if (gtk_sink->context->gl_vtable->FenceSync)
gst_query_add_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, 0);
return TRUE;
/* ERRORS */
no_caps:
{
GST_DEBUG_OBJECT (bsink, "no caps specified");
return FALSE;
}
invalid_caps:
{
GST_DEBUG_OBJECT (bsink, "invalid caps specified");
return FALSE;
}
config_failed:
{
GST_DEBUG_OBJECT (bsink, "failed setting config");
return FALSE;
}
}
static GstCaps *
gst_gtk_gl_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
{
GstCaps *tmp = NULL;
GstCaps *result = NULL;
tmp = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (bsink));
if (filter) {
GST_DEBUG_OBJECT (bsink, "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;
}
result = gst_gl_overlay_compositor_add_caps (result);
GST_DEBUG_OBJECT (bsink, "returning caps: %" GST_PTR_FORMAT, result);
return result;
}
static void
gst_gtk_gl_sink_finalize (GObject * object)
{
GstGtkGLSink *gtk_sink = GST_GTK_GL_SINK (object);
GstGtkBaseSink *base_sink = GST_GTK_BASE_SINK (object);
if (gtk_sink->widget_destroy_sig_handler) {
g_signal_handler_disconnect (base_sink->widget,
gtk_sink->widget_destroy_sig_handler);
gtk_sink->widget_destroy_sig_handler = 0;
}
G_OBJECT_CLASS (parent_class)->finalize (object);
}

View File

@@ -1,64 +0,0 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.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_GTK_GL_SINK_H__
#define __GST_GTK_GL_SINK_H__
#include <gtk/gtk.h>
#include <gst/gst.h>
#include <gst/video/gstvideosink.h>
#include <gst/video/video.h>
#include <gst/gl/gl.h>
#include "gstgtkbasesink.h"
G_BEGIN_DECLS
#define GST_TYPE_GTK_GL_SINK (gst_gtk_gl_sink_get_type ())
G_DECLARE_FINAL_TYPE (GstGtkGLSink, gst_gtk_gl_sink, GST, GTK_GL_SINK,
GstGtkBaseSink);
/**
* GstGtkGLSink:
*
* Opaque #GstGtkGLSink object
*/
struct _GstGtkGLSink
{
/* <private> */
GstGtkBaseSink parent;
GstGLDisplay *display;
GstGLContext *context;
GstGLContext *gtk_context;
GstGLUpload *upload;
GstBuffer *uploaded_buffer;
/* read/write with object lock */
gint display_width;
gint display_height;
gulong widget_destroy_sig_handler;
};
G_END_DECLS
#endif /* __GST_GTK_GL_SINK_H__ */

View File

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

View File

@@ -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__ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,113 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
* Copyright (C) 2020 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 __GTK_CLAPPER_GL_WIDGET_H__
#define __GTK_CLAPPER_GL_WIDGET_H__
#include <gtk/gtk.h>
#include <gst/gst.h>
#include <gst/video/video.h>
#include <gst/gl/gl.h>
G_BEGIN_DECLS
GType gtk_clapper_gl_widget_get_type (void);
#define GTK_TYPE_CLAPPER_GL_WIDGET (gtk_clapper_gl_widget_get_type())
#define GTK_CLAPPER_GL_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GTK_TYPE_CLAPPER_GL_WIDGET,GtkClapperGLWidget))
#define GTK_CLAPPER_GL_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GTK_TYPE_CLAPPER_GL_WIDGET,GtkClapperGLWidgetClass))
#define GTK_IS_CLAPPER_GL_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GTK_TYPE_CLAPPER_GL_WIDGET))
#define GTK_IS_CLAPPER_GL_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GTK_TYPE_CLAPPER_GL_WIDGET))
#define GTK_CLAPPER_GL_WIDGET_CAST(obj) ((GtkClapperGLWidget*)(obj))
#define GTK_CLAPPER_GL_WIDGET_LOCK(w) g_mutex_lock(&((GtkClapperGLWidget*)(w))->lock)
#define GTK_CLAPPER_GL_WIDGET_UNLOCK(w) g_mutex_unlock(&((GtkClapperGLWidget*)(w))->lock)
typedef struct _GtkClapperGLWidget GtkClapperGLWidget;
typedef struct _GtkClapperGLWidgetClass GtkClapperGLWidgetClass;
typedef struct _GtkClapperGLWidgetPrivate GtkClapperGLWidgetPrivate;
struct _GtkClapperGLWidget
{
/* <private> */
GtkGLArea parent;
GtkClapperGLWidgetPrivate *priv;
/* properties */
gboolean force_aspect_ratio;
gint par_n, par_d;
gboolean keep_last_frame;
gint display_width;
gint display_height;
/* Widget dimensions */
gint scaled_width;
gint scaled_height;
/* Position coords */
gdouble last_pos_x;
gdouble last_pos_y;
gboolean negotiated;
gboolean ignore_buffers;
GstBuffer *pending_buffer;
GstBuffer *buffer;
GstVideoInfo v_info;
/* resize */
gboolean pending_resize;
GstVideoInfo pending_v_info;
guint display_ratio_num;
guint display_ratio_den;
/*< private >*/
GMutex lock;
GWeakRef element;
/* event controllers */
GtkEventController *key_controller;
GtkEventController *motion_controller;
GtkGesture *click_gesture;
/* Pending draw idles callback */
guint draw_id;
};
struct _GtkClapperGLWidgetClass
{
GtkGLAreaClass parent_class;
};
/* API */
gboolean gtk_clapper_gl_widget_set_format (GtkClapperGLWidget * widget, GstVideoInfo * v_info);
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);
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
#endif /* __GTK_CLAPPER_GL_WIDGET_H__ */

View File

@@ -1,619 +0,0 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
* Copyright (C) 2020 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 <stdio.h>
#include "gtkgstbasewidget.h"
GST_DEBUG_CATEGORY (gst_debug_gtk_base_widget);
#define GST_CAT_DEFAULT gst_debug_gtk_base_widget
#define DEFAULT_FORCE_ASPECT_RATIO TRUE
#define DEFAULT_PAR_N 0
#define DEFAULT_PAR_D 1
#define DEFAULT_IGNORE_ALPHA TRUE
#define DEFAULT_IGNORE_TEXTURES FALSE
enum
{
PROP_0,
PROP_FORCE_ASPECT_RATIO,
PROP_PIXEL_ASPECT_RATIO,
PROP_IGNORE_ALPHA,
PROP_IGNORE_TEXTURES,
};
static void
gtk_gst_base_widget_get_preferred_width (GtkWidget * widget, gint * min,
gint * natural)
{
GtkGstBaseWidget *gst_widget = (GtkGstBaseWidget *) widget;
gint video_width = gst_widget->display_width;
if (!gst_widget->negotiated)
video_width = 10;
if (min)
*min = 1;
if (natural)
*natural = video_width;
}
static void
gtk_gst_base_widget_get_preferred_height (GtkWidget * widget, gint * min,
gint * natural)
{
GtkGstBaseWidget *gst_widget = (GtkGstBaseWidget *) widget;
gint video_height = gst_widget->display_height;
if (!gst_widget->negotiated)
video_height = 10;
if (min)
*min = 1;
if (natural)
*natural = video_height;
}
#if defined(BUILD_FOR_GTK4)
static void
gtk_gst_base_widget_measure (GtkWidget * widget, GtkOrientation orientation,
gint for_size, gint * min, gint * natural,
gint * minimum_baseline, gint * natural_baseline)
{
if (orientation == GTK_ORIENTATION_HORIZONTAL)
gtk_gst_base_widget_get_preferred_width (widget, min, natural);
else
gtk_gst_base_widget_get_preferred_height (widget, min, natural);
*minimum_baseline = -1;
*natural_baseline = -1;
}
#endif
static void
gtk_gst_base_widget_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GtkGstBaseWidget *gtk_widget = GTK_GST_BASE_WIDGET (object);
switch (prop_id) {
case PROP_FORCE_ASPECT_RATIO:
gtk_widget->force_aspect_ratio = g_value_get_boolean (value);
break;
case PROP_PIXEL_ASPECT_RATIO:
gtk_widget->par_n = gst_value_get_fraction_numerator (value);
gtk_widget->par_d = gst_value_get_fraction_denominator (value);
break;
case PROP_IGNORE_ALPHA:
gtk_widget->ignore_alpha = g_value_get_boolean (value);
break;
case PROP_IGNORE_TEXTURES:
gtk_widget->ignore_textures = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_gst_base_widget_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GtkGstBaseWidget *gtk_widget = GTK_GST_BASE_WIDGET (object);
switch (prop_id) {
case PROP_FORCE_ASPECT_RATIO:
g_value_set_boolean (value, gtk_widget->force_aspect_ratio);
break;
case PROP_PIXEL_ASPECT_RATIO:
gst_value_set_fraction (value, gtk_widget->par_n, gtk_widget->par_d);
break;
case PROP_IGNORE_ALPHA:
g_value_set_boolean (value, gtk_widget->ignore_alpha);
break;
case PROP_IGNORE_TEXTURES:
g_value_set_boolean (value, gtk_widget->ignore_textures);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gboolean
_calculate_par (GtkGstBaseWidget * widget, GstVideoInfo * info)
{
gboolean ok;
gint width, height;
gint par_n, par_d;
gint display_par_n, display_par_d;
width = GST_VIDEO_INFO_WIDTH (info);
height = GST_VIDEO_INFO_HEIGHT (info);
par_n = GST_VIDEO_INFO_PAR_N (info);
par_d = GST_VIDEO_INFO_PAR_D (info);
if (!par_n)
par_n = 1;
/* get display's PAR */
if (widget->par_n != 0 && widget->par_d != 0) {
display_par_n = widget->par_n;
display_par_d = widget->par_d;
} else {
display_par_n = 1;
display_par_d = 1;
}
ok = gst_video_calculate_display_ratio (&widget->display_ratio_num,
&widget->display_ratio_den, width, height, par_n, par_d, display_par_n,
display_par_d);
if (ok) {
GST_LOG ("PAR: %u/%u DAR:%u/%u", par_n, par_d, display_par_n,
display_par_d);
return TRUE;
}
return FALSE;
}
static void
_apply_par (GtkGstBaseWidget * widget)
{
guint display_ratio_num, display_ratio_den;
gint width, height;
width = GST_VIDEO_INFO_WIDTH (&widget->v_info);
height = GST_VIDEO_INFO_HEIGHT (&widget->v_info);
display_ratio_num = widget->display_ratio_num;
display_ratio_den = widget->display_ratio_den;
if (height % display_ratio_den == 0) {
GST_DEBUG ("keeping video height");
widget->display_width = (guint)
gst_util_uint64_scale_int (height, display_ratio_num,
display_ratio_den);
widget->display_height = height;
} else if (width % display_ratio_num == 0) {
GST_DEBUG ("keeping video width");
widget->display_width = width;
widget->display_height = (guint)
gst_util_uint64_scale_int (width, display_ratio_den, display_ratio_num);
} else {
GST_DEBUG ("approximating while keeping video height");
widget->display_width = (guint)
gst_util_uint64_scale_int (height, display_ratio_num,
display_ratio_den);
widget->display_height = height;
}
GST_DEBUG ("scaling to %dx%d", widget->display_width, widget->display_height);
}
static gboolean
_queue_draw (GtkGstBaseWidget * widget)
{
GTK_GST_BASE_WIDGET_LOCK (widget);
widget->draw_id = 0;
if (widget->pending_resize) {
widget->pending_resize = FALSE;
widget->v_info = widget->pending_v_info;
widget->negotiated = TRUE;
_apply_par (widget);
gtk_widget_queue_resize (GTK_WIDGET (widget));
} else {
gtk_widget_queue_draw (GTK_WIDGET (widget));
}
GTK_GST_BASE_WIDGET_UNLOCK (widget);
return G_SOURCE_REMOVE;
}
static const gchar *
_gdk_key_to_navigation_string (guint keyval)
{
/* TODO: expand */
switch (keyval) {
#define KEY(key) case GDK_KEY_ ## key: return G_STRINGIFY(key)
KEY (Up);
KEY (Down);
KEY (Left);
KEY (Right);
KEY (Home);
KEY (End);
#undef KEY
default:
return NULL;
}
}
static GdkEvent *
_get_current_event (GtkEventController * controller)
{
#if defined(BUILD_FOR_GTK4)
return gtk_event_controller_get_current_event (controller);
#else
return gtk_get_current_event ();
#endif
}
static void
_gdk_event_free (GdkEvent * event)
{
#if !defined(BUILD_FOR_GTK4)
if (event)
gdk_event_free (event);
#endif
}
static gboolean
gtk_gst_base_widget_key_event (GtkEventControllerKey * key_controller,
guint keyval, guint keycode, GdkModifierType state)
{
GtkEventController *controller = GTK_EVENT_CONTROLLER (key_controller);
GtkWidget *widget = gtk_event_controller_get_widget (controller);
GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget);
GstElement *element;
if ((element = g_weak_ref_get (&base_widget->element))) {
if (GST_IS_NAVIGATION (element)) {
GdkEvent *event = _get_current_event (controller);
const gchar *str = _gdk_key_to_navigation_string (keyval);
if (str) {
const gchar *key_type =
gdk_event_get_event_type (event) ==
GDK_KEY_PRESS ? "key-press" : "key-release";
gst_navigation_send_key_event (GST_NAVIGATION (element), key_type, str);
}
_gdk_event_free (event);
}
g_object_unref (element);
}
return FALSE;
}
static void
_fit_stream_to_allocated_size (GtkGstBaseWidget * base_widget,
GtkAllocation * allocation, GstVideoRectangle * result)
{
if (base_widget->force_aspect_ratio) {
GstVideoRectangle src, dst;
src.x = 0;
src.y = 0;
src.w = base_widget->display_width;
src.h = base_widget->display_height;
dst.x = 0;
dst.y = 0;
dst.w = allocation->width;
dst.h = allocation->height;
gst_video_sink_center_rect (src, dst, result, TRUE);
} else {
result->x = 0;
result->y = 0;
result->w = allocation->width;
result->h = allocation->height;
}
}
static void
_display_size_to_stream_size (GtkGstBaseWidget * base_widget, gdouble x,
gdouble y, gdouble * stream_x, gdouble * stream_y)
{
gdouble stream_width, stream_height;
GtkAllocation allocation;
GstVideoRectangle result;
gtk_widget_get_allocation (GTK_WIDGET (base_widget), &allocation);
_fit_stream_to_allocated_size (base_widget, &allocation, &result);
stream_width = (gdouble) GST_VIDEO_INFO_WIDTH (&base_widget->v_info);
stream_height = (gdouble) GST_VIDEO_INFO_HEIGHT (&base_widget->v_info);
/* from display coordinates to stream coordinates */
if (result.w > 0)
*stream_x = (x - result.x) / result.w * stream_width;
else
*stream_x = 0.;
/* clip to stream size */
if (*stream_x < 0.)
*stream_x = 0.;
if (*stream_x > GST_VIDEO_INFO_WIDTH (&base_widget->v_info))
*stream_x = GST_VIDEO_INFO_WIDTH (&base_widget->v_info);
/* same for y-axis */
if (result.h > 0)
*stream_y = (y - result.y) / result.h * stream_height;
else
*stream_y = 0.;
if (*stream_y < 0.)
*stream_y = 0.;
if (*stream_y > GST_VIDEO_INFO_HEIGHT (&base_widget->v_info))
*stream_y = GST_VIDEO_INFO_HEIGHT (&base_widget->v_info);
GST_TRACE ("transform %fx%f into %fx%f", x, y, *stream_x, *stream_y);
}
static gboolean
gtk_gst_base_widget_button_event (
#if defined(BUILD_FOR_GTK4)
GtkGestureClick * gesture,
#else
GtkGestureMultiPress * gesture,
#endif
gint n_press, gdouble x, gdouble y)
{
GtkEventController *controller = GTK_EVENT_CONTROLLER (gesture);
GtkWidget *widget = gtk_event_controller_get_widget (controller);
GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget);
GstElement *element;
if ((element = g_weak_ref_get (&base_widget->element))) {
if (GST_IS_NAVIGATION (element)) {
GdkEvent *event = _get_current_event (controller);
const gchar *key_type =
gdk_event_get_event_type (event) == GDK_BUTTON_PRESS
? "mouse-button-press" : "mouse-button-release";
gdouble stream_x, stream_y;
#if !defined(BUILD_FOR_GTK4)
guint button;
gdk_event_get_button (event, &button);
#endif
_display_size_to_stream_size (base_widget, x, y, &stream_x, &stream_y);
gst_navigation_send_mouse_event (GST_NAVIGATION (element), key_type,
#if defined(BUILD_FOR_GTK4)
/* Gesture is set to ignore other buttons so we do not have to check */
GDK_BUTTON_PRIMARY,
#else
button,
#endif
stream_x, stream_y);
_gdk_event_free (event);
}
g_object_unref (element);
}
return FALSE;
}
static gboolean
gtk_gst_base_widget_motion_event (GtkEventControllerMotion * motion_controller,
gdouble x, gdouble y)
{
GtkEventController *controller = GTK_EVENT_CONTROLLER (motion_controller);
GtkWidget *widget = gtk_event_controller_get_widget (controller);
GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget);
GstElement *element;
if ((element = g_weak_ref_get (&base_widget->element))) {
if (GST_IS_NAVIGATION (element)) {
gdouble stream_x, stream_y;
_display_size_to_stream_size (base_widget, x, y, &stream_x, &stream_y);
gst_navigation_send_mouse_event (GST_NAVIGATION (element), "mouse-move",
0, stream_x, stream_y);
}
g_object_unref (element);
}
return FALSE;
}
void
gtk_gst_base_widget_class_init (GtkGstBaseWidgetClass * klass)
{
GObjectClass *gobject_klass = (GObjectClass *) klass;
GtkWidgetClass *widget_klass = (GtkWidgetClass *) klass;
gobject_klass->set_property = gtk_gst_base_widget_set_property;
gobject_klass->get_property = gtk_gst_base_widget_get_property;
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));
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));
g_object_class_install_property (gobject_klass, PROP_IGNORE_ALPHA,
g_param_spec_boolean ("ignore-alpha", "Ignore Alpha",
"When enabled, alpha will be ignored and converted to black",
DEFAULT_IGNORE_ALPHA, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_klass, PROP_IGNORE_TEXTURES,
g_param_spec_boolean ("ignore-textures", "Ignore Textures",
"When enabled, textures will be ignored and not drawn",
DEFAULT_IGNORE_TEXTURES, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
#if defined(BUILD_FOR_GTK4)
widget_klass->measure = gtk_gst_base_widget_measure;
#else
widget_klass->get_preferred_width = gtk_gst_base_widget_get_preferred_width;
widget_klass->get_preferred_height = gtk_gst_base_widget_get_preferred_height;
#endif
GST_DEBUG_CATEGORY_INIT (gst_debug_gtk_base_widget, "gtkbasewidget", 0,
"GTK Video Base Widget");
}
void
gtk_gst_base_widget_init (GtkGstBaseWidget * widget)
{
widget->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
widget->par_n = DEFAULT_PAR_N;
widget->par_d = DEFAULT_PAR_D;
widget->ignore_alpha = DEFAULT_IGNORE_ALPHA;
widget->ignore_textures = DEFAULT_IGNORE_TEXTURES;
gst_video_info_init (&widget->v_info);
gst_video_info_init (&widget->pending_v_info);
g_weak_ref_init (&widget->element, NULL);
g_mutex_init (&widget->lock);
widget->key_controller = gtk_event_controller_key_new (
#if !defined(BUILD_FOR_GTK4)
GTK_WIDGET (widget)
#endif
);
g_signal_connect (widget->key_controller, "key-pressed",
G_CALLBACK (gtk_gst_base_widget_key_event), NULL);
g_signal_connect (widget->key_controller, "key-released",
G_CALLBACK (gtk_gst_base_widget_key_event), NULL);
widget->motion_controller = gtk_event_controller_motion_new (
#if !defined(BUILD_FOR_GTK4)
GTK_WIDGET (widget)
#endif
);
g_signal_connect (widget->motion_controller, "motion",
G_CALLBACK (gtk_gst_base_widget_motion_event), NULL);
widget->click_gesture =
#if defined(BUILD_FOR_GTK4)
gtk_gesture_click_new ();
#else
gtk_gesture_multi_press_new (GTK_WIDGET (widget));
#endif
g_signal_connect (widget->click_gesture, "pressed",
G_CALLBACK (gtk_gst_base_widget_button_event), NULL);
g_signal_connect (widget->click_gesture, "released",
G_CALLBACK (gtk_gst_base_widget_button_event), NULL);
#if defined(BUILD_FOR_GTK4)
/* Otherwise widget in grid will appear as a 1x1px
* video which might be misleading for users */
gtk_widget_set_hexpand (GTK_WIDGET (widget), TRUE);
gtk_widget_set_vexpand (GTK_WIDGET (widget), TRUE);
gtk_widget_set_focusable (GTK_WIDGET (widget), TRUE);
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (widget->click_gesture),
GDK_BUTTON_PRIMARY);
gtk_widget_add_controller (GTK_WIDGET (widget), widget->key_controller);
gtk_widget_add_controller (GTK_WIDGET (widget), widget->motion_controller);
gtk_widget_add_controller (GTK_WIDGET (widget),
GTK_EVENT_CONTROLLER (widget->click_gesture));
#endif
gtk_widget_set_can_focus (GTK_WIDGET (widget), TRUE);
}
void
gtk_gst_base_widget_finalize (GObject * object)
{
GtkGstBaseWidget *widget = GTK_GST_BASE_WIDGET (object);
/* GTK4 takes ownership of EventControllers
* while GTK3 still needs manual unref */
#if !defined(BUILD_FOR_GTK4)
g_object_unref (widget->key_controller);
g_object_unref (widget->motion_controller);
g_object_unref (widget->click_gesture);
#endif
gst_buffer_replace (&widget->pending_buffer, NULL);
gst_buffer_replace (&widget->buffer, NULL);
g_mutex_clear (&widget->lock);
g_weak_ref_clear (&widget->element);
if (widget->draw_id)
g_source_remove (widget->draw_id);
}
void
gtk_gst_base_widget_set_element (GtkGstBaseWidget * widget,
GstElement * element)
{
g_weak_ref_set (&widget->element, element);
}
gboolean
gtk_gst_base_widget_set_format (GtkGstBaseWidget * widget,
GstVideoInfo * v_info)
{
GTK_GST_BASE_WIDGET_LOCK (widget);
if (gst_video_info_is_equal (&widget->pending_v_info, v_info)) {
GTK_GST_BASE_WIDGET_UNLOCK (widget);
return TRUE;
}
if (!_calculate_par (widget, v_info)) {
GTK_GST_BASE_WIDGET_UNLOCK (widget);
return FALSE;
}
widget->pending_resize = TRUE;
widget->pending_v_info = *v_info;
GTK_GST_BASE_WIDGET_UNLOCK (widget);
return TRUE;
}
void
gtk_gst_base_widget_set_buffer (GtkGstBaseWidget * widget, GstBuffer * buffer)
{
/* As we have no type, this is better then no check */
g_return_if_fail (GTK_IS_WIDGET (widget));
GTK_GST_BASE_WIDGET_LOCK (widget);
gst_buffer_replace (&widget->pending_buffer, buffer);
if (!widget->draw_id) {
widget->draw_id = g_idle_add_full (G_PRIORITY_DEFAULT,
(GSourceFunc) _queue_draw, widget, NULL);
}
GTK_GST_BASE_WIDGET_UNLOCK (widget);
}

View File

@@ -1,102 +0,0 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
* Copyright (C) 2020 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 __GTK_GST_BASE_WIDGET_H__
#define __GTK_GST_BASE_WIDGET_H__
#include <gtk/gtk.h>
#include <gst/gst.h>
#include <gst/video/video.h>
#if !defined(BUILD_FOR_GTK4)
#include <gdk/gdk.h>
#endif
#define GTK_GST_BASE_WIDGET(w) ((GtkGstBaseWidget *)(w))
#define GTK_GST_BASE_WIDGET_CLASS(k) ((GtkGstBaseWidgetClass *)(k))
#define GTK_GST_BASE_WIDGET_LOCK(w) g_mutex_lock(&((GtkGstBaseWidget*)(w))->lock)
#define GTK_GST_BASE_WIDGET_UNLOCK(w) g_mutex_unlock(&((GtkGstBaseWidget*)(w))->lock)
G_BEGIN_DECLS
typedef struct _GtkGstBaseWidget GtkGstBaseWidget;
typedef struct _GtkGstBaseWidgetClass GtkGstBaseWidgetClass;
struct _GtkGstBaseWidget
{
union {
GtkGLArea gl_area;
} parent;
/* properties */
gboolean force_aspect_ratio;
gint par_n, par_d;
gboolean ignore_alpha;
gboolean ignore_textures;
gint display_width;
gint display_height;
gboolean negotiated;
GstBuffer *pending_buffer;
GstBuffer *buffer;
GstVideoInfo v_info;
/* resize */
gboolean pending_resize;
GstVideoInfo pending_v_info;
guint display_ratio_num;
guint display_ratio_den;
/*< private >*/
GMutex lock;
GWeakRef element;
/* event controllers */
GtkEventController *key_controller;
GtkEventController *motion_controller;
GtkGesture *click_gesture;
/* Pending draw idles callback */
guint draw_id;
};
struct _GtkGstBaseWidgetClass
{
union {
GtkGLAreaClass gl_area_class;
} parent_class;
};
/* For implementer */
void gtk_gst_base_widget_class_init (GtkGstBaseWidgetClass * klass);
void gtk_gst_base_widget_init (GtkGstBaseWidget * widget);
void gtk_gst_base_widget_finalize (GObject * object);
/* API */
gboolean gtk_gst_base_widget_set_format (GtkGstBaseWidget * widget, GstVideoInfo * v_info);
void gtk_gst_base_widget_set_buffer (GtkGstBaseWidget * widget, GstBuffer * buffer);
void gtk_gst_base_widget_set_element (GtkGstBaseWidget * widget, GstElement * element);
G_END_DECLS
#endif /* __GTK_GST_BASE_WIDGET_H__ */

View File

@@ -1,604 +0,0 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
* Copyright (C) 2020 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 <stdio.h>
#include "gtkgstglwidget.h"
#include "gstgtkutils.h"
#include <gst/gl/gstglfuncs.h>
#include <gst/video/video.h>
#if GST_GL_HAVE_WINDOW_X11 && defined (GDK_WINDOWING_X11)
#if defined(BUILD_FOR_GTK4)
#include <gdk/x11/gdkx.h>
#else
#include <gdk/gdkx.h>
#endif
#include <gst/gl/x11/gstgldisplay_x11.h>
#endif
#if GST_GL_HAVE_WINDOW_WAYLAND && defined (GDK_WINDOWING_WAYLAND)
#if defined(BUILD_FOR_GTK4)
#include <gdk/wayland/gdkwayland.h>
#else
#include <gdk/gdkwayland.h>
#endif
#include <gst/gl/wayland/gstgldisplay_wayland.h>
#endif
/**
* SECTION:gtkgstglwidget
* @title: GtkGstGlWidget
* @short_description: a #GtkGLArea that renders GStreamer video #GstBuffers
* @see_also: #GtkGLArea, #GstBuffer
*
* #GtkGstGLWidget is an #GtkWidget that renders GStreamer video buffers.
*/
#define GST_CAT_DEFAULT gtk_gst_gl_widget_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _GtkGstGLWidgetPrivate
{
gboolean initiated;
GstGLDisplay *display;
GdkGLContext *gdk_context;
GstGLContext *other_context;
GstGLContext *context;
GstGLUpload *upload;
GstGLShader *shader;
GLuint vao;
GLuint vertex_buffer;
GLint attr_position;
GLint attr_texture;
GLuint current_tex;
GstGLOverlayCompositor *overlay_compositor;
};
static const GLfloat vertices[] = {
1.0f, 1.0f, 0.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 0.0f, 0.0f,
-1.0f, -1.0f, 0.0f, 0.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f, 1.0f
};
G_DEFINE_TYPE_WITH_CODE (GtkGstGLWidget, gtk_gst_gl_widget, GTK_TYPE_GL_AREA,
G_ADD_PRIVATE (GtkGstGLWidget)
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gtkgstglwidget", 0,
"GTK Gst GL Widget"));
static void
gtk_gst_gl_widget_bind_buffer (GtkGstGLWidget * gst_widget)
{
GtkGstGLWidgetPrivate *priv = gst_widget->priv;
const GstGLFuncs *gl = priv->context->gl_vtable;
gl->BindBuffer (GL_ARRAY_BUFFER, priv->vertex_buffer);
/* Load the vertex position */
gl->VertexAttribPointer (priv->attr_position, 3, GL_FLOAT, GL_FALSE,
5 * sizeof (GLfloat), (void *) 0);
/* Load the texture coordinate */
gl->VertexAttribPointer (priv->attr_texture, 2, GL_FLOAT, GL_FALSE,
5 * sizeof (GLfloat), (void *) (3 * sizeof (GLfloat)));
gl->EnableVertexAttribArray (priv->attr_position);
gl->EnableVertexAttribArray (priv->attr_texture);
}
static void
gtk_gst_gl_widget_unbind_buffer (GtkGstGLWidget * gst_widget)
{
GtkGstGLWidgetPrivate *priv = gst_widget->priv;
const GstGLFuncs *gl = priv->context->gl_vtable;
gl->BindBuffer (GL_ARRAY_BUFFER, 0);
gl->DisableVertexAttribArray (priv->attr_position);
gl->DisableVertexAttribArray (priv->attr_texture);
}
static void
gtk_gst_gl_widget_init_redisplay (GtkGstGLWidget * gst_widget)
{
GtkGstGLWidgetPrivate *priv = gst_widget->priv;
const GstGLFuncs *gl = priv->context->gl_vtable;
GError *error = NULL;
gst_gl_insert_debug_marker (priv->other_context, "initializing redisplay");
if (!(priv->shader = gst_gl_shader_new_default (priv->context, &error))) {
GST_ERROR ("Failed to initialize shader: %s", error->message);
return;
}
priv->attr_position =
gst_gl_shader_get_attribute_location (priv->shader, "a_position");
priv->attr_texture =
gst_gl_shader_get_attribute_location (priv->shader, "a_texcoord");
if (gl->GenVertexArrays) {
gl->GenVertexArrays (1, &priv->vao);
gl->BindVertexArray (priv->vao);
}
gl->GenBuffers (1, &priv->vertex_buffer);
gl->BindBuffer (GL_ARRAY_BUFFER, priv->vertex_buffer);
gl->BufferData (GL_ARRAY_BUFFER, 4 * 5 * sizeof (GLfloat), vertices,
GL_STATIC_DRAW);
if (gl->GenVertexArrays) {
gtk_gst_gl_widget_bind_buffer (gst_widget);
gl->BindVertexArray (0);
}
gl->BindBuffer (GL_ARRAY_BUFFER, 0);
priv->overlay_compositor =
gst_gl_overlay_compositor_new (priv->other_context);
priv->initiated = TRUE;
}
static void
_redraw_texture (GtkGstGLWidget * gst_widget, guint tex)
{
GtkGstGLWidgetPrivate *priv = gst_widget->priv;
const GstGLFuncs *gl = priv->context->gl_vtable;
const GLushort indices[] = { 0, 1, 2, 0, 2, 3 };
if (gst_widget->base.force_aspect_ratio) {
GstVideoRectangle src, dst, result;
gint widget_width, widget_height, widget_scale;
gl->ClearColor (0.0, 0.0, 0.0, 1.0);
gl->Clear (GL_COLOR_BUFFER_BIT);
widget_scale = gtk_widget_get_scale_factor ((GtkWidget *) gst_widget);
widget_width = gtk_widget_get_allocated_width ((GtkWidget *) gst_widget);
widget_height = gtk_widget_get_allocated_height ((GtkWidget *) gst_widget);
src.x = 0;
src.y = 0;
src.w = gst_widget->base.display_width;
src.h = gst_widget->base.display_height;
dst.x = 0;
dst.y = 0;
dst.w = widget_width * widget_scale;
dst.h = widget_height * widget_scale;
gst_video_sink_center_rect (src, dst, &result, TRUE);
gl->Viewport (result.x, result.y, result.w, result.h);
}
gst_gl_shader_use (priv->shader);
if (gl->BindVertexArray)
gl->BindVertexArray (priv->vao);
gtk_gst_gl_widget_bind_buffer (gst_widget);
gl->ActiveTexture (GL_TEXTURE0);
gl->BindTexture (GL_TEXTURE_2D, tex);
gst_gl_shader_set_uniform_1i (priv->shader, "tex", 0);
gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
if (gl->BindVertexArray)
gl->BindVertexArray (0);
else
gtk_gst_gl_widget_unbind_buffer (gst_widget);
gl->BindTexture (GL_TEXTURE_2D, 0);
}
static inline void
_draw_black (GstGLContext * context)
{
const GstGLFuncs *gl = context->gl_vtable;
gst_gl_insert_debug_marker (context, "rendering black");
gl->ClearColor (0.0, 0.0, 0.0, 1.0);
gl->Clear (GL_COLOR_BUFFER_BIT);
}
static inline void
_draw_black_with_gdk (GdkGLContext * gdk_context)
{
GST_DEBUG ("rendering empty frame with gdk context %p", gdk_context);
glClearColor (0.0, 0.0, 0.0, 1.0);
glClear (GL_COLOR_BUFFER_BIT);
}
static gboolean
gtk_gst_gl_widget_render (GtkGLArea * widget, GdkGLContext * context)
{
GtkGstGLWidgetPrivate *priv = GTK_GST_GL_WIDGET (widget)->priv;
GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (widget);
GTK_GST_BASE_WIDGET_LOCK (widget);
/* Draw black with GDK context when priv is not available yet.
GTK calls render with GDK context already active. */
if (!priv->context || !priv->other_context || base_widget->ignore_textures) {
_draw_black_with_gdk (context);
goto done;
}
gst_gl_context_activate (priv->other_context, TRUE);
if (!priv->initiated || !base_widget->negotiated) {
if (!priv->initiated)
gtk_gst_gl_widget_init_redisplay (GTK_GST_GL_WIDGET (widget));
_draw_black (priv->other_context);
goto done;
}
/* Upload latest buffer */
if (base_widget->pending_buffer) {
GstBuffer *buffer = base_widget->pending_buffer;
GstVideoFrame gl_frame;
GstGLSyncMeta *sync_meta;
if (!gst_video_frame_map (&gl_frame, &base_widget->v_info, buffer,
GST_MAP_READ | GST_MAP_GL)) {
_draw_black (priv->other_context);
goto done;
}
priv->current_tex = *(guint *) gl_frame.data[0];
gst_gl_insert_debug_marker (priv->other_context, "redrawing texture %u",
priv->current_tex);
gst_gl_overlay_compositor_upload_overlays (priv->overlay_compositor,
buffer);
sync_meta = gst_buffer_get_gl_sync_meta (buffer);
if (sync_meta) {
/* XXX: the set_sync() seems to be needed for resizing */
gst_gl_sync_meta_set_sync_point (sync_meta, priv->context);
gst_gl_sync_meta_wait (sync_meta, priv->other_context);
}
gst_video_frame_unmap (&gl_frame);
if (base_widget->buffer)
gst_buffer_unref (base_widget->buffer);
/* Keep the buffer to ensure current_tex stay valid */
base_widget->buffer = buffer;
base_widget->pending_buffer = NULL;
}
GST_DEBUG ("rendering buffer %p with gdk context %p",
base_widget->buffer, context);
_redraw_texture (GTK_GST_GL_WIDGET (widget), priv->current_tex);
gst_gl_overlay_compositor_draw_overlays (priv->overlay_compositor);
gst_gl_insert_debug_marker (priv->other_context, "texture %u redrawn",
priv->current_tex);
done:
if (priv->other_context)
gst_gl_context_activate (priv->other_context, FALSE);
GTK_GST_BASE_WIDGET_UNLOCK (widget);
return FALSE;
}
static void
_reset_gl (GtkGstGLWidget * gst_widget)
{
GtkGstGLWidgetPrivate *priv = gst_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 (gst_widget));
if (priv->gdk_context == NULL)
return;
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;
}
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_object_unref (priv->overlay_compositor);
gst_gl_context_activate (priv->other_context, FALSE);
gst_object_unref (priv->other_context);
priv->other_context = NULL;
gdk_gl_context_clear_current ();
g_object_unref (priv->gdk_context);
priv->gdk_context = NULL;
}
static void
gtk_gst_gl_widget_finalize (GObject * object)
{
GtkGstGLWidgetPrivate *priv = GTK_GST_GL_WIDGET (object)->priv;
GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (object);
if (priv->other_context)
gst_gtk_invoke_on_main ((GThreadFunc) (GCallback) _reset_gl, base_widget);
if (priv->context)
gst_object_unref (priv->context);
if (priv->display)
gst_object_unref (priv->display);
gtk_gst_base_widget_finalize (object);
G_OBJECT_CLASS (gtk_gst_gl_widget_parent_class)->finalize (object);
}
static void
gtk_gst_gl_widget_class_init (GtkGstGLWidgetClass * klass)
{
GObjectClass *gobject_klass = (GObjectClass *) klass;
GtkGLAreaClass *gl_widget_klass = (GtkGLAreaClass *) klass;
gtk_gst_base_widget_class_init (GTK_GST_BASE_WIDGET_CLASS (klass));
gobject_klass->finalize = gtk_gst_gl_widget_finalize;
gl_widget_klass->render = gtk_gst_gl_widget_render;
}
static void
gtk_gst_gl_widget_init (GtkGstGLWidget * gst_widget)
{
GtkGstBaseWidget *base_widget = GTK_GST_BASE_WIDGET (gst_widget);
GdkDisplay *display;
GtkGstGLWidgetPrivate *priv;
gtk_gst_base_widget_init (base_widget);
gst_widget->priv = priv = gtk_gst_gl_widget_get_instance_private (gst_widget);
display = gdk_display_get_default ();
#if GST_GL_HAVE_WINDOW_X11 && defined (GDK_WINDOWING_X11)
if (GDK_IS_X11_DISPLAY (display)) {
priv->display = (GstGLDisplay *)
gst_gl_display_x11_new_with_display (gdk_x11_display_get_xdisplay
(display));
}
#endif
#if GST_GL_HAVE_WINDOW_WAYLAND && defined (GDK_WINDOWING_WAYLAND)
if (GDK_IS_WAYLAND_DISPLAY (display)) {
struct wl_display *wayland_display =
gdk_wayland_display_get_wl_display (display);
priv->display = (GstGLDisplay *)
gst_gl_display_wayland_new_with_display (wayland_display);
}
#endif
(void) display;
if (!priv->display)
priv->display = gst_gl_display_new ();
GST_INFO ("Created %" GST_PTR_FORMAT, priv->display);
/* GTK4 always has alpha */
#if !defined(BUILD_FOR_GTK4)
gtk_gl_area_set_has_alpha (GTK_GL_AREA (gst_widget),
!base_widget->ignore_alpha);
#endif
}
static void
_get_gl_context (GtkGstGLWidget * gst_widget)
{
GtkGstGLWidgetPrivate *priv = gst_widget->priv;
GstGLPlatform platform = GST_GL_PLATFORM_NONE;
GstGLAPI gl_api = GST_GL_API_NONE;
guintptr gl_handle = 0;
gtk_widget_realize (GTK_WIDGET (gst_widget));
if (priv->other_context)
gst_object_unref (priv->other_context);
priv->other_context = NULL;
if (priv->gdk_context)
g_object_unref (priv->gdk_context);
priv->gdk_context = gtk_gl_area_get_context (GTK_GL_AREA (gst_widget));
if (priv->gdk_context == NULL) {
GError *error = gtk_gl_area_get_error (GTK_GL_AREA (gst_widget));
GST_ERROR_OBJECT (gst_widget, "Error creating GdkGLContext : %s",
error ? error->message : "No error set by Gdk");
g_clear_error (&error);
return;
}
g_object_ref (priv->gdk_context);
gdk_gl_context_make_current (priv->gdk_context);
#if GST_GL_HAVE_WINDOW_X11 && defined (GDK_WINDOWING_X11)
if (GST_IS_GL_DISPLAY_X11 (priv->display)) {
#if GST_GL_HAVE_PLATFORM_GLX
if (!gl_handle) {
platform = GST_GL_PLATFORM_GLX;
gl_handle = gst_gl_context_get_current_gl_context (platform);
}
#endif
#if GST_GL_HAVE_PLATFORM_EGL
if (!gl_handle) {
platform = GST_GL_PLATFORM_EGL;
gl_handle = gst_gl_context_get_current_gl_context (platform);
}
#endif
if (gl_handle) {
gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL);
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 = gst_gl_context_get_current_gl_api (platform, NULL, NULL);
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;
if (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);
g_clear_error (&error);
g_object_unref (priv->other_context);
priv->other_context = NULL;
} else {
gst_gl_context_activate (priv->other_context, FALSE);
}
} else {
GST_WARNING ("Could not retrieve Gdk OpenGL context");
}
}
GtkWidget *
gtk_gst_gl_widget_new (void)
{
return (GtkWidget *) g_object_new (GTK_TYPE_GST_GL_WIDGET, NULL);
}
gboolean
gtk_gst_gl_widget_init_winsys (GtkGstGLWidget * gst_widget)
{
GtkGstGLWidgetPrivate *priv = gst_widget->priv;
GError *error = NULL;
g_return_val_if_fail (GTK_IS_GST_GL_WIDGET (gst_widget), FALSE);
g_return_val_if_fail (priv->display != NULL, FALSE);
GTK_GST_BASE_WIDGET_LOCK (gst_widget);
if (priv->display && priv->gdk_context && priv->other_context) {
GST_TRACE ("have already initialized contexts");
GTK_GST_BASE_WIDGET_UNLOCK (gst_widget);
return TRUE;
}
if (!priv->other_context) {
GTK_GST_BASE_WIDGET_UNLOCK (gst_widget);
gst_gtk_invoke_on_main ((GThreadFunc) (GCallback) _get_gl_context, gst_widget);
GTK_GST_BASE_WIDGET_LOCK (gst_widget);
}
if (!GST_IS_GL_CONTEXT (priv->other_context)) {
GST_FIXME ("Could not retrieve Gdk OpenGL context");
GTK_GST_BASE_WIDGET_UNLOCK (gst_widget);
return FALSE;
}
GST_OBJECT_LOCK (priv->display);
if (!gst_gl_display_create_context (priv->display, priv->other_context,
&priv->context, &error)) {
GST_WARNING ("Could not create OpenGL context: %s",
error ? error->message : "Unknown");
g_clear_error (&error);
GST_OBJECT_UNLOCK (priv->display);
GTK_GST_BASE_WIDGET_UNLOCK (gst_widget);
return FALSE;
}
gst_gl_display_add_context (priv->display, priv->context);
GST_OBJECT_UNLOCK (priv->display);
GTK_GST_BASE_WIDGET_UNLOCK (gst_widget);
return TRUE;
}
GstGLContext *
gtk_gst_gl_widget_get_gtk_context (GtkGstGLWidget * gst_widget)
{
if (!gst_widget->priv->other_context)
return NULL;
return gst_object_ref (gst_widget->priv->other_context);
}
GstGLContext *
gtk_gst_gl_widget_get_context (GtkGstGLWidget * gst_widget)
{
if (!gst_widget->priv->context)
return NULL;
return gst_object_ref (gst_widget->priv->context);
}
GstGLDisplay *
gtk_gst_gl_widget_get_display (GtkGstGLWidget * gst_widget)
{
if (!gst_widget->priv->display)
return NULL;
return gst_object_ref (gst_widget->priv->display);
}

View File

@@ -1,77 +0,0 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.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 __GTK_GST_GL_WIDGET_H__
#define __GTK_GST_GL_WIDGET_H__
#include <gtk/gtk.h>
#include <gst/gst.h>
#include <gst/gl/gl.h>
#include "gtkgstbasewidget.h"
G_BEGIN_DECLS
GType gtk_gst_gl_widget_get_type (void);
#define GTK_TYPE_GST_GL_WIDGET (gtk_gst_gl_widget_get_type())
#define GTK_GST_GL_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GTK_TYPE_GST_GL_WIDGET,GtkGstGLWidget))
#define GTK_GST_GL_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GTK_TYPE_GST_GL_WIDGET,GtkGstGLWidgetClass))
#define GTK_IS_GST_GL_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GTK_TYPE_GST_GL_WIDGET))
#define GTK_IS_GST_GL_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GTK_TYPE_GST_GL_WIDGET))
#define GTK_GST_GL_WIDGET_CAST(obj) ((GtkGstGLWidget*)(obj))
typedef struct _GtkGstGLWidget GtkGstGLWidget;
typedef struct _GtkGstGLWidgetClass GtkGstGLWidgetClass;
typedef struct _GtkGstGLWidgetPrivate GtkGstGLWidgetPrivate;
/**
* GtkGstGLWidget:
*
* Opaque #GtkGstGLWidget object
*/
struct _GtkGstGLWidget
{
/* <private> */
GtkGstBaseWidget base;
GtkGstGLWidgetPrivate *priv;
};
/**
* GtkGstGLWidgetClass:
*
* The #GtkGstGLWidgetClass struct only contains private data
*/
struct _GtkGstGLWidgetClass
{
/* <private> */
GtkGstBaseWidgetClass base_class;
};
GtkWidget * gtk_gst_gl_widget_new (void);
gboolean gtk_gst_gl_widget_init_winsys (GtkGstGLWidget * widget);
GstGLDisplay * gtk_gst_gl_widget_get_display (GtkGstGLWidget * widget);
GstGLContext * gtk_gst_gl_widget_get_context (GtkGstGLWidget * widget);
GstGLContext * gtk_gst_gl_widget_get_gtk_context (GtkGstGLWidget * widget);
G_END_DECLS
#endif /* __GTK_GST_GL_WIDGET_H__ */

View File

@@ -6,13 +6,13 @@ gstclapper_sources = [
'gstclapper-g-main-context-signal-dispatcher.c',
'gstclapper-video-overlay-video-renderer.c',
'gstclapper-visualization.c',
'gstclapper-mpris.c',
'gstclapper-gtk4-plugin.c',
'gtk4/gstgtkbasesink.c',
'gtk4/gstclapperglsink.c',
'gtk4/gstgtkutils.c',
'gtk4/gtkgstbasewidget.c',
'gtk4/gstgtkglsink.c',
'gtk4/gtkgstglwidget.c',
'gtk4/gtkclapperglwidget.c',
'gtk4/gstclapperglutils.c',
]
gstclapper_headers = [
'clapper.h',
@@ -25,6 +25,7 @@ gstclapper_headers = [
'gstclapper-g-main-context-signal-dispatcher.h',
'gstclapper-video-overlay-video-renderer.h',
'gstclapper-visualization.h',
'gstclapper-mpris.h',
'gstclapper-gtk4-plugin.h',
]
gstclapper_defines = [
@@ -32,61 +33,51 @@ gstclapper_defines = [
'-DBUILDING_GST_CLAPPER',
'-DGST_USE_UNSTABLE_API',
'-DHAVE_GTK_GL',
'-DBUILD_FOR_GTK4',
]
gtk_deps = [gstgl_dep, gstglproto_dep]
have_gtk_gl_windowing = false
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')
if not get_option('lib')
subdir_done()
endif
if gst_gl_have_window_x11 and gst_gl_have_platform_glx
gtk_x11_dep = dependency('gtk4-x11', required : false)
if gtk_x11_dep.found()
gtk_deps += [gtk_x11_dep, gstglx11_dep]
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()
gtk_deps += [gtk_wayland_dep, gstglegl_dep, gstglwayland_dep]
have_gtk_gl_windowing = true
endif
if not gir.found()
error('Clapper lib requires GI bindings to be compiled')
endif
if not have_gtk_gl_windowing
error('GTK4 widget requires GL windowing')
endif
gstclapper_mpris_gdbus = gnome.gdbus_codegen('gstclapper-mpris-gdbus',
sources: '../../../data/gstclapper-mpris-gdbus.xml',
interface_prefix: 'org.mpris.',
namespace: 'GstClapperMpris'
)
gstclapper = library('gstclapper-' + api_version,
gstclapper_sources,
c_args : gstclapper_defines,
link_args : noseh_link_args,
include_directories : [configinc, libsinc],
version : libversion,
install : true,
install_dir : clapper_libdir,
dependencies : [gtk4_dep, gstbase_dep, gstvideo_dep, gstaudio_dep,
gstclapper_sources + gstclapper_mpris_gdbus,
c_args: gstclapper_defines,
link_args: noseh_link_args,
include_directories: [configinc, libsinc],
version: libversion,
install: true,
install_dir: pkglibdir,
dependencies: [gtk4_dep, glib_dep, gio_dep, giounix_dep,
gstbase_dep, gstvideo_dep, gstaudio_dep,
gsttag_dep, gstpbutils_dep, libm] + gtk_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
lib/gst/meson.build vendored
View File

@@ -1 +1,2 @@
subdir('clapper')
subdir('plugin')

681
lib/gst/plugin/gstclappersink.c vendored Normal file
View File

@@ -0,0 +1,681 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
* Copyright (C) 2020-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 0
#define DEFAULT_PAR_D 1
#define SINK_FORMATS \
"{ BGR, RGB, BGRA, RGBA, ABGR, ARGB, RGBx, BGRx, RGBA64_LE, RGBA64_BE, NV12 }"
#define GST_CLAPPER_GL_SINK_CAPS \
"video/x-raw(" GST_CAPS_FEATURE_MEMORY_GL_MEMORY "), " \
"format = (string)" SINK_FORMATS ", " \
"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)" SINK_FORMATS ", " \
"width = " GST_VIDEO_SIZE_RANGE ", " \
"height = " GST_VIDEO_SIZE_RANGE ", " \
"framerate = " GST_VIDEO_FPS_RANGE ", " \
"texture-target = (string) { 2D, external-oes } "
enum
{
PROP_0,
PROP_WIDGET,
PROP_FORCE_ASPECT_RATIO,
PROP_PIXEL_ASPECT_RATIO,
PROP_LAST
};
GST_DEBUG_CATEGORY (gst_debug_clapper_sink);
#define GST_CAT_DEFAULT gst_debug_clapper_sink
static GstStaticPadTemplate gst_clapper_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (
GST_VIDEO_CAPS_MAKE_WITH_FEATURES ("memory:DMABuf", SINK_FORMATS) ";"
GST_CLAPPER_GL_SINK_CAPS ";"
GST_VIDEO_CAPS_MAKE (SINK_FORMATS)));
static void gst_clapper_sink_navigation_interface_init (
GstNavigationInterface *iface);
#define gst_clapper_sink_parent_class 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_DEBUG_CATEGORY_INIT (gst_debug_clapper_sink,
"clappersink", 0, "Clapper Sink"));
GST_ELEMENT_REGISTER_DEFINE (clappersink, "clappersink", GST_RANK_NONE,
GST_TYPE_CLAPPER_SINK);
static void gst_clapper_sink_finalize (GObject *object);
static void gst_clapper_sink_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *param_spec);
static void gst_clapper_sink_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *param_spec);
static gboolean gst_clapper_sink_propose_allocation (GstBaseSink *bsink,
GstQuery *query);
static gboolean gst_clapper_sink_start (GstBaseSink *bsink);
static gboolean gst_clapper_sink_stop (GstBaseSink *bsink);
static GstStateChangeReturn
gst_clapper_sink_change_state (GstElement *element, GstStateChange transition);
static void gst_clapper_sink_get_times (GstBaseSink *bsink, GstBuffer *buffer,
GstClockTime *start, GstClockTime *end);
static GstCaps * gst_clapper_sink_get_caps (GstBaseSink *bsink,
GstCaps *filter);
static gboolean gst_clapper_sink_set_caps (GstBaseSink *bsink,
GstCaps *caps);
static GstFlowReturn gst_clapper_sink_show_frame (GstVideoSink *bsink,
GstBuffer *buffer);
static void
gst_clapper_sink_class_init (GstClapperSinkClass *klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
GstBaseSinkClass *gstbasesink_class;
GstVideoSinkClass *gstvideosink_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gstbasesink_class = (GstBaseSinkClass *) klass;
gstvideosink_class = (GstVideoSinkClass *) klass;
gobject_class->set_property = gst_clapper_sink_set_property;
gobject_class->get_property = gst_clapper_sink_get_property;
gobject_class->finalize = gst_clapper_sink_finalize;
//gst_gtk_install_shared_properties (gobject_class);
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->start = gst_clapper_sink_start;
gstbasesink_class->stop = gst_clapper_sink_stop;
gstvideosink_class->show_frame = gst_clapper_sink_show_frame;
gst_element_class_set_metadata (gstelement_class,
"Clapper Video Sink",
"Sink/Video", "A GTK4 video sink used by Clapper media player",
"Rafał Dzięgiel <rafostar.github@gmail.com>");
gst_element_class_add_static_pad_template (gstelement_class,
&gst_clapper_sink_template);
}
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 "
"(must only be get from the GTK main thread)",
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;
}
static void
gst_clapper_sink_finalize (GObject *object)
{
GstClapperSink *self = GST_CLAPPER_SINK (object);
GST_TRACE ("Finalize");
GST_OBJECT_LOCK (self);
if (self->window && self->window_destroy_id)
g_signal_handler_disconnect (self->window, self->window_destroy_id);
//if (self->widget && self->widget_destroy_id)
// g_signal_handler_disconnect (self->widget, self->widget_destroy_id);
g_clear_object (&self->obj);
GST_OBJECT_UNLOCK (self);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
widget_destroy_cb (GtkWidget *widget, GstClapperSink *self)
{
GST_OBJECT_LOCK (self);
g_clear_object (&self->obj);
GST_OBJECT_UNLOCK (self);
}
static void
window_destroy_cb (GtkWidget *window, GstClapperSink *self)
{
GST_OBJECT_LOCK (self);
if (self->obj) {
if (self->widget_destroy_id) {
GtkWidget *widget;
widget = gtk_clapper_object_get_widget (self->obj);
g_signal_handler_disconnect (widget, self->widget_destroy_id);
self->widget_destroy_id = 0;
}
g_clear_object (&self->obj);
}
self->window = NULL;
GST_OBJECT_UNLOCK (self);
}
static GtkWidget *
gst_clapper_sink_get_widget (GstClapperSink *self)
{
if (G_UNLIKELY (self->obj == NULL)) {
/* Ensure GTK is initialized */
if (!gtk_init_check ()) {
GST_ERROR_OBJECT (self, "Could not ensure GTK initialization");
return NULL;
}
self->obj = gtk_clapper_object_new ();
/* Take the floating ref, otherwise the destruction of the container will
* make this widget disappear possibly before we are done. */
//g_object_ref_sink (self->obj);
//self->widget_destroy_id = g_signal_connect (widget,
// "destroy", G_CALLBACK (widget_destroy_cb), self);
/* Back pointer */
gtk_clapper_object_set_element (
GTK_CLAPPER_OBJECT (self->obj), GST_ELEMENT (self));
}
return gtk_clapper_object_get_widget (self->obj);
}
static void
gst_clapper_sink_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
GstClapperSink *self = GST_CLAPPER_SINK (object);
switch (prop_id) {
case PROP_WIDGET:{
GObject *widget = NULL;
GST_OBJECT_LOCK (self);
if (G_LIKELY (self->obj != NULL))
widget = G_OBJECT (gtk_clapper_object_get_widget (self->obj));
GST_OBJECT_UNLOCK (self);
if (G_UNLIKELY (widget == NULL)) {
widget = gst_gtk_invoke_on_main (
(GThreadFunc) gst_clapper_sink_get_widget, self);
}
g_value_set_object (value, 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;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_clapper_sink_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
GstClapperSink *self = GST_CLAPPER_SINK (object);
switch (prop_id) {
case PROP_FORCE_ASPECT_RATIO:
self->force_aspect_ratio = g_value_get_boolean (value);
break;
case PROP_PIXEL_ASPECT_RATIO:
self->par_n = gst_value_get_fraction_numerator (value);
self->par_d = gst_value_get_fraction_denominator (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
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 (GST_IS_EVENT (event))) {
GstPad *pad;
pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (sink));
if (G_LIKELY (GST_IS_PAD (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 void
gst_clapper_sink_navigation_interface_init (GstNavigationInterface *iface)
{
iface->send_event = gst_clapper_sink_navigation_send_event;
}
static gboolean
gst_clapper_sink_propose_allocation (GstBaseSink *bsink, GstQuery *query)
{
GstClapperSink *self = GST_CLAPPER_SINK (bsink);
GstBufferPool *pool = NULL;
GstStructure *config;
GstCaps *caps;
GstVideoInfo info;
guint size;
gboolean need_pool;
GstStructure *allocation_meta = NULL;
gint display_width, display_height;
//if (!self->display || !self->context)
// return FALSE;
gst_query_parse_allocation (query, &caps, &need_pool);
if (!caps)
goto no_caps;
if (!gst_video_info_from_caps (&info, caps))
goto invalid_caps;
/* Normal size of a frame */
size = GST_VIDEO_INFO_SIZE (&info);
if (need_pool) {
GST_DEBUG_OBJECT (self, "Creating new pool");
pool = gst_buffer_pool_new ();
config = gst_buffer_pool_get_config (pool);
gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
if (!gst_buffer_pool_set_config (pool, config)) {
gst_object_unref (pool);
goto config_failed;
}
}
/* We need at least 3 buffers because we keep around the current one
* for memory to stay valid during resizing and hold on to the pending one */
gst_query_add_allocation_pool (query, pool, size, 3, 0);
if (pool)
gst_object_unref (pool);
/* FIXME: Read calculated display sizes from widget */
display_width = GST_VIDEO_INFO_WIDTH (&info);
display_height = GST_VIDEO_INFO_HEIGHT (&info);
if (display_width != 0 && display_height != 0) {
GST_DEBUG_OBJECT (self, "Sending alloc query with size %dx%d",
display_width, display_height);
allocation_meta = gst_structure_new ("GstVideoOverlayCompositionMeta",
"width", G_TYPE_UINT, display_width,
"height", G_TYPE_UINT, display_height, NULL);
}
gst_query_add_allocation_meta (query,
GST_VIDEO_OVERLAY_COMPOSITION_META_API_TYPE, allocation_meta);
if (allocation_meta)
gst_structure_free (allocation_meta);
/* We also support various metadata */
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
return TRUE;
/* ERRORS */
no_caps:
GST_DEBUG_OBJECT (bsink, "No caps specified");
return FALSE;
invalid_caps:
GST_DEBUG_OBJECT (bsink, "Invalid caps specified");
return FALSE;
config_failed:
GST_DEBUG_OBJECT (bsink, "Failed to set config");
return FALSE;
}
static gboolean
gst_clapper_sink_start_on_main (GstClapperSink *self)
{
GtkWidget *widget;
/* Make sure widget is created */
if (!(widget = gst_clapper_sink_get_widget (self)))
return FALSE;
/* After this point, self->obj will always be set */
if (!GTK_IS_ROOT (gtk_widget_get_root (widget))) {
GtkWidget *toplevel, *parent;
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;
/* User did not add widget its own UI, let's popup a new GtkWindow to
* make "gst-launch-1.0" work. */
self->window = (GtkWindow *) gtk_window_new ();
win_title = g_strdup_printf ("Clapper Sink - GTK %u.%u.%u Window",
gtk_get_major_version (),
gtk_get_minor_version (),
gtk_get_micro_version ());
gtk_window_set_default_size (self->window, 640, 480);
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);
}
return TRUE;
}
static gboolean
gst_clapper_sink_start (GstBaseSink *bsink)
{
GstClapperSink *self = GST_CLAPPER_SINK (bsink);
GtkClapperObject *obj = NULL;
if (!(! !gst_gtk_invoke_on_main ((GThreadFunc) (GCallback)
gst_clapper_sink_start_on_main, self)))
return FALSE;
//widget = GTK_CLAPPER_WIDGET (self->widget);
GST_OBJECT_LOCK (self);
if (self->obj)
obj = g_object_ref (self->obj);
GST_OBJECT_UNLOCK (self);
if (G_UNLIKELY (obj == NULL)) {
GST_ELEMENT_ERROR (bsink, RESOURCE, NOT_FOUND, ("%s",
"Clapper widget does not exist"), (NULL));
return FALSE;
}
if (!gtk_clapper_object_init_winsys (obj)) {
GST_ELEMENT_ERROR (bsink, RESOURCE, NOT_FOUND, ("%s",
"Failed to initialize OpenGL with GTK"), (NULL));
return FALSE;
}
/*
if (!clapper_sink->display)
clapper_sink->display = gtk_clapper_gl_widget_get_display (clapper_widget);
if (!clapper_sink->context)
clapper_sink->context = gtk_clapper_gl_widget_get_context (clapper_widget);
if (!clapper_sink->gtk_context)
clapper_sink->gtk_context = gtk_clapper_gl_widget_get_gtk_context (clapper_widget);
if (!clapper_sink->display || !clapper_sink->context || !clapper_sink->gtk_context) {
GST_ELEMENT_ERROR (bsink, RESOURCE, NOT_FOUND, ("%s",
"Failed to retrieve OpenGL context from GTK"), (NULL));
return FALSE;
}
gst_gl_element_propagate_display_context (GST_ELEMENT (bsink),
clapper_sink->display);
*/
return TRUE;
}
static gboolean
gst_clapper_sink_stop_on_main (GstClapperSink *self)
{
if (self->window) {
gtk_window_destroy (self->window);
self->window = NULL;
//self->widget = NULL;
}
return TRUE;
}
static gboolean
gst_clapper_sink_stop (GstBaseSink *bsink)
{
GstClapperSink *self = GST_CLAPPER_SINK_CAST (bsink);
if (G_UNLIKELY (self->window != NULL)) {
return ! !gst_gtk_invoke_on_main ((GThreadFunc) (GCallback)
gst_clapper_sink_stop_on_main, self);
}
return TRUE;
}
static void
gst_gtk_window_show_all_and_unref (GtkWindow *window)
{
gtk_window_present (window);
g_object_unref (window);
}
static GstStateChangeReturn
gst_clapper_sink_change_state (GstElement *element, GstStateChange transition)
{
GstClapperSink *self = GST_CLAPPER_SINK_CAST (element);
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
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)));
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
if (G_UNLIKELY (ret == GST_STATE_CHANGE_FAILURE))
return ret;
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:{
GtkWindow *window = NULL;
GST_OBJECT_LOCK (self);
if (self->window)
window = g_object_ref (self->window);
GST_OBJECT_UNLOCK (self);
if (window) {
gst_gtk_invoke_on_main ((GThreadFunc) (GCallback)
gst_gtk_window_show_all_and_unref, window);
}
break;
}
case GST_STATE_CHANGE_PAUSED_TO_READY:
GST_OBJECT_LOCK (self);
if (G_LIKELY (self->obj != NULL))
gtk_clapper_object_set_buffer (self->obj, NULL);
GST_OBJECT_UNLOCK (self);
break;
default:
break;
}
return ret;
}
static void
gst_clapper_sink_get_times (GstBaseSink *bsink, GstBuffer *buffer,
GstClockTime *start, GstClockTime *end)
{
if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
*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);
if (GST_VIDEO_INFO_FPS_N (&self->v_info) > 0) {
*end = *start + gst_util_uint64_scale_int (GST_SECOND,
GST_VIDEO_INFO_FPS_D (&self->v_info),
GST_VIDEO_INFO_FPS_N (&self->v_info));
}
}
}
}
static GstCaps *
gst_clapper_sink_get_caps (GstBaseSink *bsink, GstCaps *filter)
{
GstCaps *tmp = NULL;
GstCaps *result = NULL;
tmp = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (bsink));
if (filter) {
GST_DEBUG_OBJECT (bsink, "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;
}
//result = gst_gl_overlay_compositor_add_caps (result);
GST_DEBUG_OBJECT (bsink, "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_DEBUG ("Set caps: %" GST_PTR_FORMAT, caps);
GST_OBJECT_LOCK (self);
if (!gst_video_info_from_caps (&self->v_info, caps)) {
GST_OBJECT_UNLOCK (self);
return FALSE;
}
if (G_UNLIKELY (self->obj == NULL)) {
GST_OBJECT_UNLOCK (self);
GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
("%s", "Output widget was destroyed"), (NULL));
return FALSE;
}
if (!gtk_clapper_object_set_format (self->obj, &self->v_info)) {
GST_OBJECT_UNLOCK (self);
return FALSE;
}
GST_OBJECT_UNLOCK (self);
return TRUE;
}
static GstFlowReturn
gst_clapper_sink_show_frame (GstVideoSink *vsink, GstBuffer *buffer)
{
GstClapperSink *self = GST_CLAPPER_SINK_CAST (vsink);
GST_TRACE ("Rendering buffer: %p", buffer);
GST_OBJECT_LOCK (self);
if (G_UNLIKELY (self->obj == NULL)) {
GST_OBJECT_UNLOCK (self);
GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
("%s", "Output widget was destroyed"), (NULL));
return GST_FLOW_ERROR;
}
gtk_clapper_object_set_buffer (self->obj, buffer);
GST_OBJECT_UNLOCK (self);
return GST_FLOW_OK;
}

74
lib/gst/plugin/gstclappersink.h vendored Normal file
View File

@@ -0,0 +1,74 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
* Copyright (C) 2020-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 "gtkclapperobject.h"
G_BEGIN_DECLS
#define GST_TYPE_CLAPPER_SINK (gst_clapper_sink_get_type ())
#define GST_IS_CLAPPER_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CLAPPER_SINK))
#define GST_IS_CLAPPER_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_CLAPPER_SINK))
#define GST_CLAPPER_SINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CLAPPER_SINK, GstClapperSinkClass))
#define GST_CLAPPER_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_CLAPPER_SINK, GstClapperSinkClass))
#define GST_CLAPPER_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_CLAPPER_SINK, GstClapperSink))
#define GST_CLAPPER_SINK_CAST(obj) ((GstClapperSink*)(obj))
typedef struct _GstClapperSink GstClapperSink;
typedef struct _GstClapperSinkClass GstClapperSinkClass;
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GstClapperSink, gst_object_unref)
#endif
struct _GstClapperSink
{
GstVideoSink parent;
GstVideoInfo v_info;
GtkClapperObject *obj;
GtkWindow *window;
/* properties */
gboolean force_aspect_ratio;
gint par_n, par_d;
gint display_width, display_height;
gulong widget_destroy_id, window_destroy_id;
};
struct _GstClapperSinkClass
{
GstVideoSinkClass parent_class;
};
GST_ELEMENT_REGISTER_DECLARE (clappersink);
GType gst_clapper_sink_get_type (void);
G_END_DECLS

71
lib/gst/plugin/gstgtkutils.c vendored Normal file
View File

@@ -0,0 +1,71 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
* Copyright (C) 2015 Thibault Saunier <tsaunier@gnome.org>
*
* 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"
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;
}

View File

@@ -1,6 +1,7 @@
/*
* GStreamer
* Copyright (C) 2020 Rafał Dzięgiel <rafostar.github@gmail.com>
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
* Copyright (C) 2015 Thibault Saunier <tsaunier@gnome.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -18,14 +19,8 @@
* Boston, MA 02110-1301, USA.
*/
#if defined(BUILD_FOR_GTK4)
#define GTKCONFIG_PLUGIN gtk4
#define GTKCONFIG_NAME "GTK4"
#define GTKCONFIG_SINK "gtk4sink"
#define GTKCONFIG_GLSINK "gtk4glsink"
#else
#define GTKCONFIG_PLUGIN gtk
#define GTKCONFIG_NAME "GTK"
#define GTKCONFIG_SINK "gtksink"
#define GTKCONFIG_GLSINK "gtkglsink"
#endif
#pragma once
#include <glib.h>
gpointer gst_gtk_invoke_on_main (GThreadFunc func, gpointer data);

38
lib/gst/plugin/gstplugin.c vendored Normal file
View File

@@ -0,0 +1,38 @@
/*
* 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"
static gboolean
plugin_init (GstPlugin *plugin)
{
gboolean res = FALSE;
res |= GST_ELEMENT_REGISTER (clappersink, plugin);
return res;
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
clapper, "Clapper elements", plugin_init, VERSION, "LGPL",
GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)

960
lib/gst/plugin/gtkclapperobject.c vendored Normal file
View File

@@ -0,0 +1,960 @@
/*
* GStreamer
* 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 <gst/gl/gstglfuncs.h>
#include <gst/allocators/gstdmabuf.h>
#include "gtkclapperobject.h"
#include "gstgtkutils.h"
#if GST_GL_HAVE_WINDOW_X11 && defined (GDK_WINDOWING_X11)
#include <gdk/x11/gdkx.h>
#if GST_GL_HAVE_PLATFORM_EGL
#include <gst/gl/egl/gstgldisplay_egl.h>
#endif
#endif
#if GST_GL_HAVE_WINDOW_WAYLAND && defined (GDK_WINDOWING_WAYLAND)
#include <gdk/wayland/gdkwayland.h>
#include <gst/gl/wayland/gstgldisplay_wayland.h>
#endif
#if GST_GL_HAVE_PLATFORM_EGL
#include <gst/gl/egl/gsteglimage.h>
#endif
GST_DEBUG_CATEGORY (gst_debug_clapper_object);
#define GST_CAT_DEFAULT gst_debug_clapper_object
static void gtk_clapper_object_paintable_iface_init (GdkPaintableInterface *iface);
static void gtk_clapper_object_finalize (GObject *object);
static const GLfloat vertices[] = {
1.0f, 1.0f, 0.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 0.0f, 0.0f,
-1.0f, -1.0f, 0.0f, 0.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f, 1.0f
};
static const GLushort indices[] = {
0, 1, 2, 0, 2, 3
};
/* GTK4 renders things upside down ¯\_(ツ)_/¯ */
static const gfloat vertical_flip_matrix[] = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f,
};
#define gtk_clapper_object_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GtkClapperObject, gtk_clapper_object, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
gtk_clapper_object_paintable_iface_init)
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gtkclapperobject", 0,
"GTK Clapper Object"));
static void
gtk_clapper_object_class_init (GtkClapperObjectClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
gobject_class->finalize = gtk_clapper_object_finalize;
}
static void
gtk_clapper_object_init (GtkClapperObject *self)
{
self->last_pos_x = 0;
self->last_pos_y = 0;
self->picture = (GtkPicture *) gtk_picture_new ();
/* We cannot do textures of 0x0px size */
gtk_widget_set_size_request (GTK_WIDGET (self->picture), 1, 1);
/* Center instead of fill to not draw empty space into framebuffer */
gtk_widget_set_halign (GTK_WIDGET (self->picture), GTK_ALIGN_CENTER);
gtk_widget_set_valign (GTK_WIDGET (self->picture), GTK_ALIGN_CENTER);
gtk_picture_set_paintable (self->picture, GDK_PAINTABLE (self));
gst_video_info_init (&self->v_info);
gst_video_info_init (&self->pending_v_info);
g_weak_ref_init (&self->element, NULL);
g_mutex_init (&self->lock);
self->gst_tex_target = GST_GL_TEXTURE_TARGET_EXTERNAL_OES;
self->gl_tex_target = gst_gl_texture_target_to_gl (self->gst_tex_target);
}
static void
gtk_clapper_object_finalize (GObject *object)
{
GtkClapperObject *self = GTK_CLAPPER_OBJECT (object);
if (self->draw_id)
g_source_remove (self->draw_id);
gst_buffer_replace (&self->pending_buffer, NULL);
gst_buffer_replace (&self->buffer, NULL);
g_mutex_clear (&self->lock);
g_weak_ref_clear (&self->element);
GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
}
static void
_gdk_gl_context_set_active (GtkClapperObject *self, gboolean activate)
{
/* We wrap around a GDK context, so we need to make
* both GTK and GStreamer aware of its active state */
if (activate) {
gdk_gl_context_make_current (self->gdk_context);
gst_gl_context_activate (self->wrapped_context, TRUE);
} else {
gst_gl_context_activate (self->wrapped_context, FALSE);
gdk_gl_context_clear_current ();
}
}
static GdkMemoryFormat
video_format_to_gdk_memory_format (GstVideoFormat format)
{
switch (format) {
case GST_VIDEO_FORMAT_BGR:
return GDK_MEMORY_B8G8R8;
case GST_VIDEO_FORMAT_RGB:
return GDK_MEMORY_R8G8B8;
case GST_VIDEO_FORMAT_BGRA:
return GDK_MEMORY_B8G8R8A8;
case GST_VIDEO_FORMAT_RGBA:
return GDK_MEMORY_R8G8B8A8;
case GST_VIDEO_FORMAT_ABGR:
return GDK_MEMORY_A8B8G8R8;
case GST_VIDEO_FORMAT_ARGB:
return GDK_MEMORY_A8R8G8B8;
case GST_VIDEO_FORMAT_BGRx:
return GDK_MEMORY_B8G8R8A8_PREMULTIPLIED;
case GST_VIDEO_FORMAT_RGBx:
return GDK_MEMORY_R8G8B8A8_PREMULTIPLIED;
case GST_VIDEO_FORMAT_RGBA64_LE:
case GST_VIDEO_FORMAT_RGBA64_BE:
return GDK_MEMORY_R16G16B16A16_PREMULTIPLIED;
default:
g_assert_not_reached ();
}
/* Number not belonging to any format */
return GDK_MEMORY_N_FORMATS;
}
static void
gtk_clapper_object_bind_buffer (GtkClapperObject *self)
{
const GstGLFuncs *gl = self->wrapped_context->gl_vtable;
gl->BindBuffer (GL_ARRAY_BUFFER, self->vertex_buffer);
/* Load the vertex position */
gl->VertexAttribPointer (self->attr_position, 3, GL_FLOAT, GL_FALSE,
5 * sizeof (GLfloat), (void *) 0);
/* Load the texture coordinate */
gl->VertexAttribPointer (self->attr_texture, 2, GL_FLOAT, GL_FALSE,
5 * sizeof (GLfloat), (void *) (3 * sizeof (GLfloat)));
gl->EnableVertexAttribArray (self->attr_position);
gl->EnableVertexAttribArray (self->attr_texture);
}
static void
gtk_clapper_object_unbind_buffer (GtkClapperObject *self)
{
const GstGLFuncs *gl = self->wrapped_context->gl_vtable;
gl->BindBuffer (GL_ARRAY_BUFFER, 0);
gl->DisableVertexAttribArray (self->attr_position);
gl->DisableVertexAttribArray (self->attr_texture);
}
static void
gtk_clapper_object_init_redisplay (GtkClapperObject *self)
{
GstGLSLStage *frag_stage, *vert_stage;
GError *error = NULL;
gchar *frag_str;
const GstGLFuncs *gl;
if (self->gst_tex_target != GST_GL_TEXTURE_TARGET_EXTERNAL_OES)
return;
if (!((vert_stage = gst_glsl_stage_new_with_string (self->wrapped_context,
GL_VERTEX_SHADER, GST_GLSL_VERSION_NONE,
GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY,
gst_gl_shader_string_vertex_mat4_vertex_transform)))) {
GST_ERROR ("Failed to retrieve vertex shader for texture target");
return;
}
frag_str = gst_gl_shader_string_fragment_external_oes_get_default (
self->wrapped_context, GST_GLSL_VERSION_NONE,
GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY);
frag_stage = gst_glsl_stage_new_with_string (self->wrapped_context,
GL_FRAGMENT_SHADER, GST_GLSL_VERSION_NONE,
GST_GLSL_PROFILE_ES | GST_GLSL_PROFILE_COMPATIBILITY, frag_str);
g_free (frag_str);
if (!frag_stage) {
GST_ERROR ("Failed to retrieve fragment shader for texture target");
return;
}
if (!((self->shader = gst_gl_shader_new_link_with_stages (self->wrapped_context,
&error, vert_stage, frag_stage, NULL)))) {
GST_ERROR ("Failed to initialize shader: %s", error->message);
g_clear_error (&error);
gst_object_unref (vert_stage);
gst_object_unref (frag_stage);
return;
}
self->attr_position =
gst_gl_shader_get_attribute_location (self->shader, "a_position");
self->attr_texture =
gst_gl_shader_get_attribute_location (self->shader, "a_texcoord");
gl = self->wrapped_context->gl_vtable;
if (gl->GenVertexArrays) {
gl->GenVertexArrays (1, &self->vao);
gl->BindVertexArray (self->vao);
}
gl->GenBuffers (1, &self->vertex_buffer);
gl->BindBuffer (GL_ARRAY_BUFFER, self->vertex_buffer);
gl->BufferData (GL_ARRAY_BUFFER, 4 * 5 * sizeof (GLfloat), vertices, GL_STATIC_DRAW);
if (gl->GenVertexArrays) {
gtk_clapper_object_bind_buffer (self);
gl->BindVertexArray (0);
}
gl->BindBuffer (GL_ARRAY_BUFFER, 0);
self->initiated = TRUE;
}
static gboolean
_dmabuf_into_texture (GtkClapperObject *self, gint *fds, gsize *offsets)
{
GstEGLImage *image;
const GstGLFuncs *gl;
image = gst_egl_image_from_dmabuf_direct_target (self->wrapped_context,
fds, offsets, &self->v_info, self->gst_tex_target);
/* If HW colorspace conversion failed and there is only one
* plane, we can just make it into single EGLImage as is */
if (!image && GST_VIDEO_INFO_N_PLANES (&self->v_info) == 1)
image = gst_egl_image_from_dmabuf (self->wrapped_context,
fds[0], &self->v_info, 0, offsets[0]);
/* Still no image? Give up then */
if (!image)
return FALSE;
gl = self->wrapped_context->gl_vtable;
if (!self->texture_id)
gl->GenTextures (1, &self->texture_id);
gl->BindTexture (self->gl_tex_target, self->texture_id);
gl->TexParameteri (self->gl_tex_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gl->TexParameteri (self->gl_tex_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl->TexParameteri (self->gl_tex_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
gl->TexParameteri (self->gl_tex_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
gl->EGLImageTargetTexture2D (self->gl_tex_target, gst_egl_image_get_image (image));
gl->BindTexture (GL_TEXTURE_2D, 0);
gst_egl_image_unref (image);
return TRUE;
}
static gboolean
_ext_texture_into_2d (GtkClapperObject *self, guint tex_width, guint tex_height)
{
GLuint framebuffer, new_texture_id;
GLenum status;
const GstGLFuncs *gl;
if (!self->initiated)
gtk_clapper_object_init_redisplay (self);
gl = self->wrapped_context->gl_vtable;
gl->GenFramebuffers (1, &framebuffer);
gl->BindFramebuffer (GL_FRAMEBUFFER, framebuffer);
gl->GenTextures (1, &new_texture_id);
gl->BindTexture (GL_TEXTURE_2D, new_texture_id);
gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
gl->TexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
gl->TexImage2D (GL_TEXTURE_2D, 0, GL_RGBA8, tex_width, tex_height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, NULL);
gl->FramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, new_texture_id, 0);
status = gl->CheckFramebufferStatus (GL_FRAMEBUFFER);
if (G_UNLIKELY (status != GL_FRAMEBUFFER_COMPLETE)) {
GST_ERROR ("Invalid framebuffer status: %u", status);
gl->BindTexture (GL_TEXTURE_2D, 0);
gl->DeleteTextures (1, &new_texture_id);
gl->BindFramebuffer (GL_FRAMEBUFFER, 0);
gl->DeleteFramebuffers (1, &framebuffer);
return FALSE;
}
gl->Viewport (0, 0, tex_width, tex_height);
gst_gl_shader_use (self->shader);
if (gl->BindVertexArray)
gl->BindVertexArray (self->vao);
gtk_clapper_object_bind_buffer (self);
gl->ActiveTexture (GL_TEXTURE0);
gl->BindTexture (self->gl_tex_target, self->texture_id);
gst_gl_shader_set_uniform_1i (self->shader, "tex", 0);
gst_gl_shader_set_uniform_matrix_4fv (self->shader,
"u_transformation", 1, FALSE, vertical_flip_matrix);
gl->DrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
if (gl->BindVertexArray)
gl->BindVertexArray (0);
else
gtk_clapper_object_unbind_buffer (self);
gl->BindTexture (self->gl_tex_target, 0);
/* Replace external OES texture with new 2D one */
gl->DeleteTextures (1, &self->texture_id);
self->texture_id = new_texture_id;
gl->BindFramebuffer (GL_FRAMEBUFFER, 0);
gl->DeleteFramebuffers (1, &framebuffer);
return TRUE;
}
static GdkTexture *
gtk_clapper_object_import_dmabuf (GtkClapperObject *self, gint *fds, gsize *offsets)
{
GdkTexture *texture;
guint tex_width, tex_height;
_gdk_gl_context_set_active (self, TRUE);
if (!_dmabuf_into_texture (self, fds, offsets)) {
_gdk_gl_context_set_active (self, FALSE);
return NULL;
}
switch (self->gst_tex_target) {
case GST_GL_TEXTURE_TARGET_2D:
tex_width = GST_VIDEO_INFO_WIDTH (&self->v_info);
tex_height = GST_VIDEO_INFO_HEIGHT (&self->v_info);
break;
case GST_GL_TEXTURE_TARGET_EXTERNAL_OES:{
GtkWidget *widget = (GtkWidget *) self->picture;
gint scale;
scale = gtk_widget_get_scale_factor (widget);
tex_width = gtk_widget_get_width (widget) * scale;
tex_height = gtk_widget_get_height (widget) * scale;
if (G_LIKELY (_ext_texture_into_2d (self, tex_width, tex_height)))
break;
return NULL;
}
default:
g_assert_not_reached ();
return NULL;
}
texture = gdk_gl_texture_new (self->gdk_context,
self->texture_id, tex_width, tex_height, NULL, NULL);
_gdk_gl_context_set_active (self, FALSE);
return texture;
}
typedef gboolean (*MemTypeCheckFunc) (gpointer data);
static gboolean
buffer_memory_type_check (GstBuffer *buffer, MemTypeCheckFunc func)
{
guint i, n_mems;
n_mems = gst_buffer_n_memory (buffer);
for (i = 0; i < n_mems; i++) {
if (!func (gst_buffer_peek_memory (buffer, i)))
return FALSE;
}
return n_mems > 0;
}
static gboolean
verify_dmabuf_memory (GtkClapperObject *self, guint n_planes,
gint *fds, gsize *offsets)
{
guint i;
for (i = 0; i < n_planes; i++) {
GstMemory *memory;
gsize plane_size, mem_skip;
guint mem_idx, length;
plane_size = gst_gl_get_plane_data_size (&self->v_info, NULL, i);
if (!gst_buffer_find_memory (self->buffer,
GST_VIDEO_INFO_PLANE_OFFSET (&self->v_info, i),
plane_size, &mem_idx, &length, &mem_skip)) {
GST_DEBUG_OBJECT (self, "Could not find memory %u", i);
return FALSE;
}
/* We can't have more then one DMABuf per plane */
if (length != 1) {
GST_DEBUG_OBJECT (self, "Data for plane %u spans %u memories",
i, length);
return FALSE;
}
memory = gst_buffer_peek_memory (self->buffer, mem_idx);
offsets[i] = memory->offset + mem_skip;
fds[i] = gst_dmabuf_memory_get_fd (memory);
}
return TRUE;
}
static GdkTexture *
obtain_texture_from_current_buffer (GtkClapperObject *self)
{
GdkTexture *texture = NULL;
GstVideoFrame frame;
/* DMABuf */
if (buffer_memory_type_check (self->buffer, (MemTypeCheckFunc) gst_is_dmabuf_memory)) {
gsize offsets[GST_VIDEO_MAX_PLANES];
gint fds[GST_VIDEO_MAX_PLANES];
guint n_planes;
n_planes = GST_VIDEO_INFO_N_PLANES (&self->v_info);
if (!verify_dmabuf_memory (self, n_planes, fds, offsets)) {
GST_ERROR ("DMABuf memory is invalid");
return NULL;
}
if (!((texture = gtk_clapper_object_import_dmabuf (self, fds, offsets))))
GST_ERROR ("Could not create texture from DMABuf");
return texture;
}
/* GL Memory */
if (buffer_memory_type_check (self->buffer, (MemTypeCheckFunc) gst_is_gl_memory)) {
if (gst_video_frame_map (&frame, &self->v_info, self->buffer, GST_MAP_READ | GST_MAP_GL)) {
GST_FIXME_OBJECT (self, "Handle GstGLMemory");
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),
NULL, NULL);
gst_video_frame_unmap (&frame);
}
return texture;
}
/* RAW */
if (gst_video_frame_map (&frame, &self->v_info, self->buffer, GST_MAP_READ)) {
GBytes *bytes;
/* Our ref on a buffer together with 2 buffers pool ensures that
* current buffer will not be freed while another one is prepared */
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),
NULL, NULL);
texture = gdk_memory_texture_new (
GST_VIDEO_FRAME_WIDTH (&frame),
GST_VIDEO_FRAME_HEIGHT (&frame),
video_format_to_gdk_memory_format (GST_VIDEO_FRAME_FORMAT (&frame)),
bytes,
GST_VIDEO_FRAME_PLANE_STRIDE (&frame, 0));
g_bytes_unref (bytes);
gst_video_frame_unmap (&frame);
}
return texture;
}
static gboolean
calculate_display_par (GtkClapperObject *self, GstVideoInfo *info)
{
gboolean success;
gint width, height;
gint par_n, par_d;
gint display_par_n, display_par_d;
width = GST_VIDEO_INFO_WIDTH (info);
height = GST_VIDEO_INFO_HEIGHT (info);
par_n = GST_VIDEO_INFO_PAR_N (info);
par_d = GST_VIDEO_INFO_PAR_D (info);
if (!par_n)
par_n = 1;
/* User set props */
if (self->par_n != 0 && self->par_d != 0) {
display_par_n = self->par_n;
display_par_d = self->par_d;
} else {
display_par_n = 1;
display_par_d = 1;
}
if ((success = gst_video_calculate_display_ratio (&self->display_ratio_num,
&self->display_ratio_den, width, height, par_n, par_d, display_par_n,
display_par_d))) {
GST_LOG ("PAR: %u/%u, DAR: %u/%u", par_n, par_d, display_par_n, display_par_d);
}
return success;
}
static void
update_display_size (GtkClapperObject *self)
{
guint display_ratio_num, display_ratio_den;
gint width, height;
display_ratio_num = self->display_ratio_num;
display_ratio_den = self->display_ratio_den;
width = GST_VIDEO_INFO_WIDTH (&self->v_info);
height = GST_VIDEO_INFO_HEIGHT (&self->v_info);
if (height % display_ratio_den == 0) {
GST_DEBUG ("Keeping video height");
self->display_width = (guint)
gst_util_uint64_scale_int (height, display_ratio_num, display_ratio_den);
self->display_height = height;
} else if (width % display_ratio_num == 0) {
GST_DEBUG ("Keeping video width");
self->display_width = width;
self->display_height = (guint)
gst_util_uint64_scale_int (width, display_ratio_den, display_ratio_num);
} else {
GST_DEBUG ("Approximating while keeping video height");
self->display_width = (guint)
gst_util_uint64_scale_int (height, display_ratio_num, display_ratio_den);
self->display_height = height;
}
self->display_aspect_ratio = ((gdouble) self->display_width
/ (gdouble) self->display_height);
GST_DEBUG ("Scaling to %dx%d", self->display_width, self->display_height);
}
static void
update_paintable (GtkClapperObject *self, GdkPaintable *paintable)
{
/* No change, so discard the new one */
if (self->paintable == paintable) {
if (paintable)
g_object_unref (paintable);
return;
}
if (self->paintable)
g_object_unref (self->paintable);
self->paintable = paintable;
if (self->pending_resize) {
update_display_size (self);
gdk_paintable_invalidate_size ((GdkPaintable *) self);
self->pending_resize = FALSE;
}
gdk_paintable_invalidate_contents ((GdkPaintable *) self);
}
static gboolean
draw_on_main_cb (GtkClapperObject *self)
{
GdkTexture *texture;
GTK_CLAPPER_OBJECT_LOCK (self);
/* Replace used buffer and set matching v_info */
gst_buffer_replace (&self->buffer, self->pending_buffer);
self->v_info = self->pending_v_info;
texture = obtain_texture_from_current_buffer (self);
if (texture)
update_paintable (self, (GdkPaintable *) texture);
self->draw_id = 0;
GTK_CLAPPER_OBJECT_UNLOCK (self);
return G_SOURCE_REMOVE;
}
void
gtk_clapper_object_set_element (GtkClapperObject *self, GstElement *element)
{
g_weak_ref_set (&self->element, element);
}
gboolean
gtk_clapper_object_set_format (GtkClapperObject *self, GstVideoInfo *v_info)
{
GTK_CLAPPER_OBJECT_LOCK (self);
if (gst_video_info_is_equal (&self->pending_v_info, v_info)) {
GTK_CLAPPER_OBJECT_UNLOCK (self);
return TRUE;
}
if (!calculate_display_par (self, v_info)) {
GTK_CLAPPER_OBJECT_UNLOCK (self);
return FALSE;
}
self->pending_resize = TRUE;
self->pending_v_info = *v_info;
GTK_CLAPPER_OBJECT_UNLOCK (self);
return TRUE;
}
void
gtk_clapper_object_set_buffer (GtkClapperObject *self, GstBuffer *buffer)
{
GstVideoMeta *meta = NULL;
GTK_CLAPPER_OBJECT_LOCK (self);
gst_buffer_replace (&self->pending_buffer, buffer);
if (self->draw_id) {
GTK_CLAPPER_OBJECT_UNLOCK (self);
return;
}
if (self->pending_buffer)
meta = gst_buffer_get_video_meta (self->pending_buffer);
/* Update pending info from video meta */
if (meta) {
guint i;
GST_VIDEO_INFO_WIDTH (&self->pending_v_info) = meta->width;
GST_VIDEO_INFO_HEIGHT (&self->pending_v_info) = meta->height;
for (i = 0; i < meta->n_planes; i++) {
GST_VIDEO_INFO_PLANE_OFFSET (&self->pending_v_info, i) = meta->offset[i];
GST_VIDEO_INFO_PLANE_STRIDE (&self->pending_v_info, i) = meta->stride[i];
}
}
self->draw_id = g_idle_add_full (G_PRIORITY_DEFAULT,
(GSourceFunc) draw_on_main_cb, self, NULL);
GTK_CLAPPER_OBJECT_UNLOCK (self);
}
GtkClapperObject *
gtk_clapper_object_new (void)
{
return g_object_new (GTK_TYPE_CLAPPER_OBJECT, NULL);
}
GtkWidget *
gtk_clapper_object_get_widget (GtkClapperObject *self)
{
return (GtkWidget *) self->picture;
}
static gboolean
wrap_current_gl (GstGLDisplay *display, GstGLPlatform platform, GstGLContext **context)
{
GstGLAPI gl_api = GST_GL_API_NONE;
guint gl_major = 0, gl_minor = 0;
gl_api = gst_gl_context_get_current_gl_api (platform, &gl_major, &gl_minor);
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 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 ((*context = gst_gl_context_new_wrapped (display,
gl_handle, platform, gl_api)))
return TRUE;
}
}
return FALSE;
}
static void
retrieve_gl_context_on_main (GtkClapperObject *self)
{
GdkDisplay *gdk_display;
GstGLPlatform platform = GST_GL_PLATFORM_NONE;
GError *error = NULL;
gst_clear_object (&self->wrapped_context);
g_clear_object (&self->gdk_context);
gtk_widget_realize (GTK_WIDGET (self->picture));
if (!((self->gdk_context = gdk_surface_create_gl_context (gtk_native_get_surface (
gtk_widget_get_native (GTK_WIDGET (self->picture))), &error)))) {
GST_ERROR_OBJECT (self, "Error creating Gdk GL context: %s",
error ? error->message : "No error set by Gdk");
g_clear_error (&error);
return;
}
//gdk_gl_context_set_use_es (self->gdk_context, TRUE);
//gdk_gl_context_realize (self->gdk_context, &error);
gdk_display = gdk_gl_context_get_display (self->gdk_context);
#if GST_GL_HAVE_WINDOW_X11 && defined (GDK_WINDOWING_X11)
if (GDK_IS_X11_DISPLAY (gdk_display)) {
gpointer display_ptr;
#if GST_GL_HAVE_PLATFORM_EGL && GTK_CHECK_VERSION(4,3,1)
display_ptr = gdk_x11_display_get_egl_display (gdk_display);
if (display_ptr)
self->display = (GstGLDisplay *)
gst_gl_display_egl_new_with_egl_display (display_ptr);
#endif
}
#endif
#if GST_GL_HAVE_WINDOW_WAYLAND && defined (GDK_WINDOWING_WAYLAND)
if (GDK_IS_WAYLAND_DISPLAY (gdk_display)) {
struct wl_display *wayland_display =
gdk_wayland_display_get_wl_display (gdk_display);
self->display = (GstGLDisplay *)
gst_gl_display_wayland_new_with_display (wayland_display);
}
#endif
if (G_UNLIKELY (!self->display)) {
GST_WARNING_OBJECT (self, "Unknown Gdk display!");
self->display = gst_gl_display_new ();
}
#if GST_GL_HAVE_PLATFORM_EGL
#if GST_GL_HAVE_WINDOW_WAYLAND && defined (GDK_WINDOWING_WAYLAND)
if (GST_IS_GL_DISPLAY_WAYLAND (self->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_IS_GL_DISPLAY_EGL (self->display)) {
platform = GST_GL_PLATFORM_EGL;
GST_DEBUG ("Using EGL on x11");
goto have_platform;
}
#endif
#endif /* GST_GL_HAVE_PLATFORM_EGL */
GST_ERROR ("Unsupported GL platform");
return;
have_platform:
g_object_ref (self->gdk_context);
gdk_gl_context_make_current (self->gdk_context);
if (!wrap_current_gl (self->display, platform, &self->wrapped_context)) {
GST_WARNING ("Could not retrieve Gdk OpenGL context");
return;
}
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 retrieve Gdk context info: %s", error->message);
g_clear_error (&error);
g_clear_object (&self->wrapped_context);
}
/* Deactivate in both places */
_gdk_gl_context_set_active (self, FALSE);
}
gboolean
gtk_clapper_object_init_winsys (GtkClapperObject *self)
{
GError *error = NULL;
GTK_CLAPPER_OBJECT_LOCK (self);
if (self->display && self->gdk_context && self->wrapped_context) {
GST_TRACE ("Have already initialized contexts");
GTK_CLAPPER_OBJECT_UNLOCK (self);
return TRUE;
}
if (!self->wrapped_context) {
GTK_CLAPPER_OBJECT_UNLOCK (self);
gst_gtk_invoke_on_main ((GThreadFunc) (GCallback) retrieve_gl_context_on_main, self);
GTK_CLAPPER_OBJECT_LOCK (self);
}
if (!GST_IS_GL_CONTEXT (self->wrapped_context)) {
GST_FIXME ("Could not retrieve Gdk GL context");
GTK_CLAPPER_OBJECT_UNLOCK (self);
return FALSE;
}
GTK_CLAPPER_OBJECT_UNLOCK (self);
return TRUE;
}
/*
* GdkPaintableInterface
*/
static void
gtk_clapper_object_paintable_snapshot (GdkPaintable *paintable,
GdkSnapshot *snapshot, gdouble width, gdouble height)
{
GtkClapperObject *self = GTK_CLAPPER_OBJECT_CAST (paintable);
if (self->paintable)
gdk_paintable_snapshot (self->paintable, snapshot, width, height);
}
static GdkPaintable *
gtk_clapper_object_paintable_get_current_image (GdkPaintable *paintable)
{
GtkClapperObject *self = GTK_CLAPPER_OBJECT_CAST (paintable);
return (self->paintable)
? g_object_ref (self->paintable)
: gdk_paintable_new_empty (0, 0);
}
static gint
gtk_clapper_object_paintable_get_intrinsic_width (GdkPaintable *paintable)
{
GtkClapperObject *self = GTK_CLAPPER_OBJECT_CAST (paintable);
return self->display_width;
}
static gint
gtk_clapper_object_paintable_get_intrinsic_height (GdkPaintable *paintable)
{
GtkClapperObject *self = GTK_CLAPPER_OBJECT_CAST (paintable);
return self->display_height;
}
static gdouble
gtk_clapper_object_paintable_get_intrinsic_aspect_ratio (GdkPaintable *paintable)
{
GtkClapperObject *self = GTK_CLAPPER_OBJECT_CAST (paintable);
return self->display_aspect_ratio;
}
static void
gtk_clapper_object_paintable_iface_init (GdkPaintableInterface *iface)
{
iface->snapshot = gtk_clapper_object_paintable_snapshot;
iface->get_current_image = gtk_clapper_object_paintable_get_current_image;
iface->get_intrinsic_width = gtk_clapper_object_paintable_get_intrinsic_width;
iface->get_intrinsic_height = gtk_clapper_object_paintable_get_intrinsic_height;
iface->get_intrinsic_aspect_ratio = gtk_clapper_object_paintable_get_intrinsic_aspect_ratio;
}

139
lib/gst/plugin/gtkclapperobject.h vendored Normal file
View File

@@ -0,0 +1,139 @@
/*
* GStreamer
* Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
* Copyright (C) 2020-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 <gst/gl/gl.h>
#include <gst/gl/gstglfuncs.h>
G_BEGIN_DECLS
#define GTK_TYPE_CLAPPER_OBJECT (gtk_clapper_object_get_type ())
#define GTK_IS_CLAPPER_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CLAPPER_OBJECT))
#define GTK_IS_CLAPPER_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CLAPPER_OBJECT))
#define GTK_CLAPPER_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CLAPPER_OBJECT, GtkClapperObjectClass))
#define GTK_CLAPPER_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CLAPPER_OBJECT, GtkClapperObjectClass))
#define GTK_CLAPPER_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CLAPPER_OBJECT, GtkClapperObject))
#define GTK_CLAPPER_OBJECT_CAST(obj) ((GtkClapperObject*)(obj))
#define GTK_CLAPPER_OBJECT_LOCK(w) g_mutex_lock(&((GtkClapperObject*)(w))->lock)
#define GTK_CLAPPER_OBJECT_UNLOCK(w) g_mutex_unlock(&((GtkClapperObject*)(w))->lock)
typedef struct _GtkClapperObject GtkClapperObject;
typedef struct _GtkClapperObjectClass GtkClapperObjectClass;
struct _GtkClapperObject
{
GObject parent;
GtkPicture *picture;
GdkPaintable *paintable;
GstGLDisplay *display;
GdkGLContext *gdk_context;
GstGLContext *wrapped_context;
GstGLContext *gst_context;
/* properties */
gboolean force_aspect_ratio;
gint par_n, par_d;
gboolean keep_last_frame;
gint display_width;
gint display_height;
gdouble display_aspect_ratio;
/* Object dimensions */
gint scaled_width;
gint scaled_height;
/* Position coords */
gdouble last_pos_x;
gdouble last_pos_y;
gboolean negotiated;
gboolean ignore_buffers;
GstBuffer *pending_buffer;
GstBuffer *buffer;
GstVideoInfo pending_v_info;
GstVideoInfo v_info;
guint texture_id, oes_texture_id, next_texture_id;
/* resize */
gboolean pending_resize;
guint display_ratio_num;
guint display_ratio_den;
/*< private >*/
GMutex lock;
GWeakRef element;
/* event controllers */
GtkEventController *key_controller;
GtkEventController *motion_controller;
GtkGesture *click_gesture;
/* Pending draw idles callback */
guint draw_id;
GstGLTextureTarget gst_tex_target;
guint gl_tex_target;
GstGLShader *shader;
GLuint vao;
GLuint vertex_buffer;
GLint attr_position;
GLint attr_texture;
gboolean initiated;
guint frame_buffer;
};
struct _GtkClapperObjectClass
{
GObjectClass parent_class;
};
GType gtk_clapper_object_get_type (void);
GtkClapperObject * gtk_clapper_object_new (void);
GtkWidget * gtk_clapper_object_get_widget (GtkClapperObject *object);
gboolean gtk_clapper_object_init_winsys (GtkClapperObject *object);
gboolean gtk_clapper_object_set_format (GtkClapperObject *object, GstVideoInfo *v_info);
void gtk_clapper_object_set_buffer (GtkClapperObject *object, GstBuffer *buffer);
void gtk_clapper_object_set_element (GtkClapperObject *object, GstElement *element);
G_END_DECLS

63
lib/gst/plugin/meson.build vendored Normal file
View File

@@ -0,0 +1,63 @@
gst_plugins_libdir = join_paths(prefix, libdir, 'gstreamer-1.0')
gst_clapper_plugin_args = [
'-DHAVE_CONFIG_H',
'-DGST_USE_UNSTABLE_API',
]
gst_clapper_plugin_deps = [
gtk4_dep,
gst_dep,
gstbase_dep,
gstvideo_dep,
gstallocators_dep,
]
if get_option('default_library') == 'static'
gst_clapper_plugin_args += ['-DGST_STATIC_COMPILATION']
endif
gst_clapper_plugin_option = get_option('gst-plugin')
if gst_clapper_plugin_option.disabled()
subdir_done()
endif
foreach dep : gst_clapper_plugin_deps
if not dep.found()
if gst_clapper_plugin_option.enabled()
error('GStreamer plugin was enabled, but required dependencies were not found')
endif
subdir_done()
endif
endforeach
if not have_gtk_gl_windowing
if gst_clapper_plugin_option.enabled()
error('GTK4 widget requires GL windowing')
else
subdir_done()
endif
endif
if not gtk4_dep.version().version_compare('>=4.6.0')
if gst_clapper_plugin_option.enabled()
error('GTK4 version on this system is too old, plugin needs 4.6.0+')
else
subdir_done()
endif
endif
gst_clapper_plugin_sources = [
'gstclappersink.c',
'gtkclapperobject.c',
'gstgtkutils.c',
'gstplugin.c',
]
library('gstclapper',
gst_clapper_plugin_sources,
c_args: gst_clapper_plugin_args,
include_directories: configinc,
dependencies: gst_clapper_plugin_deps + gtk_deps,
install: true,
install_dir: gst_plugins_libdir,
)

119
lib/meson.build vendored
View File

@@ -1,5 +1,5 @@
glib_req = '>= 2.56.0'
gst_req = '>= 1.18.0'
gst_req = '>= 1.19.1'
api_version = '1.0'
libversion = meson.project_version()
@@ -7,10 +7,6 @@ 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()
if cc.get_id() == 'msvc'
@@ -40,7 +36,7 @@ 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
@@ -142,7 +138,7 @@ cdata.set_quoted('PACKAGE_BUGREPORT', 'https://github.com/Rafostar/clapper/issue
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 = [
@@ -192,25 +188,27 @@ cdata.set_quoted('GST_PACKAGE_NAME', 'GStreamer Plugins 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'])
gstallocators_dep = dependency('gstreamer-allocators-1.0', version: gst_req,
fallback : ['gst-plugins-base', 'allocators_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 +230,72 @@ 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'])
giounix_dep = dependency('gio-unix-2.0', version: glib_req, fallback: ['glib', 'libgiounix_dep'])
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')
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'
]
gtk_deps = [gstgl_dep, gstglproto_dep]
have_gtk_gl_windowing = false
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 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()
gtk_deps += gtk_x11_dep
if gst_gl_have_platform_glx
gtk_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()
gtk_deps += [gtk_wayland_dep, gstglwayland_dep]
have_gtk_gl_windowing = true
endif
endif
if gst_gl_have_platform_egl
gtk_deps += gstglegl_dep
endif
subdir('gst')
configure_file(output : 'config.h', configuration : cdata)
configure_file(output: 'config.h', configuration: cdata)

View File

@@ -1,33 +1,41 @@
project('com.github.rafostar.Clapper', 'c', 'cpp',
version: '0.1.0',
version: '0.4.1',
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')
pkglibdir = join_paths(libdir, meson.project_name())
pkgdatadir = join_paths(datadir, meson.project_name())
if get_option('lib') or not get_option('gst-plugin').disabled()
subdir('lib')
endif
if get_option('clapper-player')
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

View File

@@ -1,20 +1,25 @@
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: 'enabled',
description: 'Build GStreamer plugin (includes GTK video sink element)'
)
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'
)

View File

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

View File

@@ -1,4 +0,0 @@
clapper-*/
clapper-*.pkg.tar.*
pkg/
src/

View File

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

View File

@@ -1,28 +0,0 @@
Format: 3.0 (quilt)
Source: clapper
Binary: clapper
Architecture: any
Version: 0.1.0
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.

View File

@@ -1,5 +0,0 @@
clapper (0.1.0) unstable; urgency=low
* New version
-- Rafostar <rafostar.github@gmail.com> Fri, 26 Feb 2021 09:39:00 +0100

View File

@@ -1 +0,0 @@
10

View File

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

View File

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

View File

@@ -1,4 +0,0 @@
#!/usr/bin/make -f
%:
dh $@

View File

@@ -1 +0,0 @@
3.0 (quilt)

View File

@@ -1,4 +1,6 @@
build/
builddir/
repo/
.flatpak-builder/
com.github.rafostar.Clapper.flatpak
flathub/com.github.rafostar.Clapper.json

View File

@@ -0,0 +1,52 @@
{
"app-id": "com.github.rafostar.Clapper",
"runtime": "org.gnome.Platform",
"runtime-version": "master",
"sdk": "org.gnome.Sdk",
"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",
"--own-name=org.mpris.MediaPlayer2.Clapper",
"--talk-name=org.gnome.Shell",
"--env=GST_PLUGIN_SYSTEM_PATH=/app/lib/gstreamer-1.0",
"--env=GST_VAAPI_ALL_DRIVERS=1"
],
"modules": [
"flathub/shared-modules/gudev/gudev.json",
"flathub/lib/libsass.json",
"flathub/lib/sassc.json",
"flathub/lib/libadwaita.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/libsoup3.json",
"testing/gstreamer.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/"
]
}

View File

@@ -1,7 +1,7 @@
{
"app-id": "com.github.rafostar.Clapper",
"runtime": "org.gnome.Platform",
"runtime-version": "3.38",
"runtime-version": "41",
"sdk": "org.gnome.Sdk",
"command": "com.github.rafostar.Clapper",
"finish-args": [
@@ -12,42 +12,44 @@
"--socket=pulseaudio",
"--share=network",
"--device=all",
"--filesystem=xdg-run/pipewire-0:ro",
"--filesystem=xdg-videos",
"--own-name=org.mpris.MediaPlayer2.Clapper",
"--talk-name=org.gnome.Shell",
"--env=GST_PLUGIN_SYSTEM_PATH=/app/lib/gstreamer-1.0",
"--env=GST_VAAPI_ALL_DRIVERS=1"
],
"modules": [
"lib/glib-networking.json",
"shared-modules/gudev/gudev.json",
"lib/pango.json",
"lib/libsass.json",
"lib/sassc.json",
"lib/gtk4.json",
"lib/liba52.json",
"lib/libmpeg2.json",
"lib/libdvdcss.json",
"lib/libdvdread.json",
"lib/libdvdnav.json",
"lib/libass.json",
"lib/ffmpeg.json",
"lib/uchardet.json",
"gstreamer-1.0/gstreamer.json",
"gstreamer-1.0/gst-plugins-base.json",
"gstreamer-1.0/gst-plugins-good.json",
"gstreamer-1.0/gst-plugins-bad.json",
"gstreamer-1.0/gst-plugins-ugly.json",
"gstreamer-1.0/gst-libav.json",
"gstreamer-1.0/gstreamer-vaapi.json",
"flathub/shared-modules/gudev/gudev.json",
"flathub/lib/pango.json",
"flathub/lib/libsass.json",
"flathub/lib/sassc.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",
"flathub/lib/uchardet.json",
"testing/libsoup3.json",
"flathub/gstreamer-1.0/gstreamer.json",
"flathub/lib/gtk4.json",
"flathub/lib/libadwaita.json",
"testing/gtuber.json",
{
"name": "clapper",
"buildsystem": "meson",
"sources": [
{
"type": "git",
"url": "https://github.com/Rafostar/clapper.git"
"type": "dir",
"path": "../../."
}
]
}
],
"cleanup-commands": [
"ln -s /lib/$FLATPAK_ARCH-linux-*/gstreamer-1.0/libgstpipewire.so /app/lib/gstreamer-1.0/"
]
}

1
pkgs/flatpak/flathub Submodule

Submodule pkgs/flatpak/flathub added at 4b3bdfab8c

View File

@@ -1,30 +0,0 @@
From 2c371f17af1695bd42f572d5ccdb837152b8b67a Mon Sep 17 00:00:00 2001
From: Thomas Coldrick <othko97@gmail.com>
Date: Thu, 8 Nov 2018 17:46:53 +0000
Subject: [PATCH] gst-libav-stop-caching-codecs
---
ext/libav/gstav.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/ext/libav/gstav.c b/ext/libav/gstav.c
index 2a88230..bfd19a1 100644
--- a/ext/libav/gstav.c
+++ b/ext/libav/gstav.c
@@ -155,6 +155,13 @@ plugin_init (GstPlugin * plugin)
/* build global ffmpeg param/property info */
gst_ffmpeg_cfg_init ();
+ gst_plugin_add_dependency_simple (plugin, NULL,
+ "/app/lib",
+ "libavcodec.so.58,"
+ "libavformat.so.58,"
+ "libswscale.so.5",
+ GST_PLUGIN_DEPENDENCY_FLAG_NONE);
+
gst_ffmpegaudenc_register (plugin);
gst_ffmpegvidenc_register (plugin);
gst_ffmpegauddec_register (plugin);
--
2.19.1

View File

@@ -1,20 +0,0 @@
{
"name": "gst-libav",
"buildsystem": "meson",
"config-opts": [
"-Ddoc=disabled",
"-Dtests=disabled"
],
"sources": [
{
"type": "git",
"url": "https://gitlab.freedesktop.org/gstreamer/gst-libav.git",
"tag": "1.18.1",
"commit": "097313530cae4a49437a779a9ded0ade8113c26b"
},
{
"type": "patch",
"path": "gst-libav-stop-caching-codecs.patch"
}
]
}

View File

@@ -1,89 +0,0 @@
From ab9ceccc8b7f0591f580abfa6901d27c49812a94 Mon Sep 17 00:00:00 2001
From: Rafostar <40623528+Rafostar@users.noreply.github.com>
Date: Sun, 10 Jan 2021 20:22:43 +0100
Subject: [PATCH 1/2] assrender: fix mimetype detection
Previously gst_structure_has_name was used to get a string to compare with supported mimetypes.
This is incorrect as above function returns a user defined structure name which is
not the structure mimetype value.
---
ext/assrender/gstassrender.c | 21 ++++++++++++---------
1 file changed, 12 insertions(+), 9 deletions(-)
diff --git a/ext/assrender/gstassrender.c b/ext/assrender/gstassrender.c
index e6d31985b..a69d3fe78 100644
--- a/ext/assrender/gstassrender.c
+++ b/ext/assrender/gstassrender.c
@@ -1557,7 +1557,7 @@ gst_ass_render_handle_tag_sample (GstAssRender * render, GstSample * sample)
const GstStructure *structure;
gboolean valid_mimetype, valid_extension;
guint i;
- const gchar *filename;
+ const gchar *mimetype, *filename;
buf = gst_sample_get_buffer (sample);
structure = gst_sample_get_info (sample);
@@ -1565,20 +1565,23 @@ gst_ass_render_handle_tag_sample (GstAssRender * render, GstSample * sample)
if (!buf || !structure)
return;
+ filename = gst_structure_get_string (structure, "filename");
+ if (!filename)
+ return;
+
valid_mimetype = FALSE;
valid_extension = FALSE;
- for (i = 0; i < G_N_ELEMENTS (mimetypes); i++) {
- if (gst_structure_has_name (structure, mimetypes[i])) {
- valid_mimetype = TRUE;
- break;
+ mimetype = gst_structure_get_string (structure, "mimetype");
+ if (mimetype) {
+ for (i = 0; i < G_N_ELEMENTS (mimetypes); i++) {
+ if (strcmp (mimetype, mimetypes[i]) == 0) {
+ valid_mimetype = TRUE;
+ break;
+ }
}
}
- filename = gst_structure_get_string (structure, "filename");
- if (!filename)
- return;
-
if (!valid_mimetype) {
guint len = strlen (filename);
const gchar *extension = filename + len - 4;
--
2.28.0
From fd7d46171b2abcd3ac247491f01a91444e7b95b2 Mon Sep 17 00:00:00 2001
From: Rafostar <40623528+Rafostar@users.noreply.github.com>
Date: Sun, 10 Jan 2021 20:26:58 +0100
Subject: [PATCH 2/2] assrender: add "vnd.ms-opentype" to supported mimetypes
The "application/vnd.ms-opentype" mimetype is commonly used mimetype
for fonts with .otf extension, handle it without checking the file extension.
---
ext/assrender/gstassrender.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/ext/assrender/gstassrender.c b/ext/assrender/gstassrender.c
index a69d3fe78..96b062c50 100644
--- a/ext/assrender/gstassrender.c
+++ b/ext/assrender/gstassrender.c
@@ -1546,7 +1546,8 @@ gst_ass_render_handle_tag_sample (GstAssRender * render, GstSample * sample)
static const gchar *mimetypes[] = {
"application/x-font-ttf",
"application/x-font-otf",
- "application/x-truetype-font"
+ "application/x-truetype-font",
+ "application/vnd.ms-opentype"
};
static const gchar *extensions[] = {
".otf",
--
2.28.0

View File

@@ -1,30 +0,0 @@
From 1c8538d8f8c2181106d626d67784af6db094036e Mon Sep 17 00:00:00 2001
From: Rafostar <rafostar.github@gmail.com>
Date: Thu, 19 Nov 2020 18:03:11 +0100
Subject: [PATCH] assrender: fix smooth scaling by disabling hinting
When ass hinting value is set to anything other than NONE,
subtitles cannot use smooth scaling, thus all animations will jitter.
The libass author warns about possibility of breaking some scripts when it is enabled,
so lets do what is recommended and disable it to get the smooth scaling working.
---
ext/assrender/gstassrender.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ext/assrender/gstassrender.c b/ext/assrender/gstassrender.c
index e99458bf29..111987b9d8 100644
--- a/ext/assrender/gstassrender.c
+++ b/ext/assrender/gstassrender.c
@@ -916,7 +916,7 @@ gst_ass_render_negotiate (GstAssRender * render, GstCaps * caps)
ass_set_pixel_aspect (render->ass_renderer,
(gdouble) render->info.par_n / (gdouble) render->info.par_d);
ass_set_font_scale (render->ass_renderer, 1.0);
- ass_set_hinting (render->ass_renderer, ASS_HINTING_LIGHT);
+ ass_set_hinting (render->ass_renderer, ASS_HINTING_NONE);
ass_set_fonts (render->ass_renderer, "Arial", "sans-serif", 1, NULL, 1);
ass_set_fonts (render->ass_renderer, NULL, "Sans", 1, NULL, 1);
--
GitLab

View File

@@ -1,75 +0,0 @@
From f9af93d841546ca7898350ae14ed57448b24a644 Mon Sep 17 00:00:00 2001
From: Seungha Yang <seungha@centricular.com>
Date: Sat, 14 Nov 2020 03:16:07 +0900
Subject: [PATCH 1/2] codecs: h264decoder: Don't give up to decode due to
missing reference picture
Missing reference picture is very common thing for broken/malformed stream.
Decoder should be able to keep decoding if it's not a very critical error.
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/1809>
---
gst-libs/gst/codecs/gsth264decoder.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/gst-libs/gst/codecs/gsth264decoder.c b/gst-libs/gst/codecs/gsth264decoder.c
index e6d20af208..40446d92df 100644
--- a/gst-libs/gst/codecs/gsth264decoder.c
+++ b/gst-libs/gst/codecs/gsth264decoder.c
@@ -2354,7 +2354,7 @@ modify_ref_pic_list (GstH264Decoder * self, int list)
if (!pic) {
GST_WARNING_OBJECT (self, "Malformed stream, no pic num %d",
pic_num_lx);
- return FALSE;
+ break;
}
shift_right_and_insert (ref_pic_listx, ref_idx_lx,
num_ref_idx_lX_active_minus1, pic);
@@ -2380,7 +2380,7 @@ modify_ref_pic_list (GstH264Decoder * self, int list)
if (!pic) {
GST_WARNING_OBJECT (self, "Malformed stream, no pic num %d",
list_mod->value.long_term_pic_num);
- return FALSE;
+ break;
}
shift_right_and_insert (ref_pic_listx, ref_idx_lx,
num_ref_idx_lX_active_minus1, pic);
--
GitLab
From 9011a58491b089461762a8f550892de434af5c29 Mon Sep 17 00:00:00 2001
From: Seungha Yang <seungha@centricular.com>
Date: Sat, 14 Nov 2020 03:20:19 +0900
Subject: [PATCH 2/2] vah264dec: Allow missing reference picture
baseclass might provide reference picture list with null picture.
Ensure picture before filling picture information.
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/1809>
---
sys/va/gstvah264dec.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/sys/va/gstvah264dec.c b/sys/va/gstvah264dec.c
index e90f84bb44..184af430fa 100644
--- a/sys/va/gstvah264dec.c
+++ b/sys/va/gstvah264dec.c
@@ -198,7 +198,13 @@ _fill_ref_pic_list (VAPictureH264 va_reflist[32], GArray * reflist)
for (i = 0; i < reflist->len; i++) {
GstH264Picture *picture = g_array_index (reflist, GstH264Picture *, i);
- _fill_vaapi_pic (&va_reflist[i], picture);
+
+ if (picture) {
+ _fill_vaapi_pic (&va_reflist[i], picture);
+ } else {
+ /* list might include null picture if reference picture was missing */
+ _init_vaapi_pic (&va_reflist[i]);
+ }
}
for (; i < 32; i++)
--
GitLab

View File

@@ -1,41 +0,0 @@
{
"name": "gst-plugins-bad",
"buildsystem": "meson",
"config-opts": [
"-Ddoc=disabled",
"-Dexamples=disabled",
"-Dtests=disabled",
"-Dnls=disabled",
"-Dgobject-cast-checks=disabled",
"-Dglib-asserts=disabled",
"-Dglib-checks=disabled",
"-Dextra-checks=disabled",
"-Dvulkan=disabled",
"-Dwebrtc=disabled",
"-Dwasapi=disabled",
"-Dwasapi2=disabled",
"-Dwinks=disabled",
"-Dwinscreencap=disabled"
],
"sources": [
{
"type": "git",
"url": "https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad.git",
"tag": "1.18.1",
"commit": "e5c3c106a2da607953fea36e3a253b382c939684"
},
{
"type": "patch",
"path": "gst-plugins-bad-vah264dec-fix-seeking-errors.patch"
},
{
"type": "patch",
"path": "gst-plugins-bad-assrender-smooth-scaling.patch"
},
{
"type": "patch",
"path": "gst-plugins-bad-assrender-fix-mimetype-detection.patch"
}
]
}

View File

@@ -1,142 +0,0 @@
From 61a66babede5a587783a1d4eb28e950a755ff362 Mon Sep 17 00:00:00 2001
From: Rafostar <rafostar.github@gmail.com>
Date: Wed, 25 Nov 2020 14:44:21 +0100
Subject: [PATCH] subparse: Autodetect subtitle text encoding
Use "uchardet" to guess the subtitle text encoding if it is not in UTF-8
or manually specified instead of blindly guessing its "ISO-8859-15".
The "uchardet" dependency is optional and when is not available at
compile time, then old behaviour will be used.
---
gst/subparse/gstsubparse.c | 58 +++++++++++++++++++++++++++++++++-----
gst/subparse/meson.build | 12 ++++++--
2 files changed, 61 insertions(+), 9 deletions(-)
diff --git a/gst/subparse/gstsubparse.c b/gst/subparse/gstsubparse.c
index 382e430f2..42283d2d1 100644
--- a/gst/subparse/gstsubparse.c
+++ b/gst/subparse/gstsubparse.c
@@ -31,6 +31,10 @@
#include <sys/types.h>
#include <glib.h>
+#if defined(HAVE_UCHARDET)
+#include <uchardet.h>
+#endif
+
#include "gstsubparse.h"
#include "gstssaparse.h"
#include "samiparse.h"
@@ -148,8 +152,9 @@ gst_sub_parse_class_init (GstSubParseClass * klass)
"Encoding to assume if input subtitles are not in UTF-8 or any other "
"Unicode encoding. If not set, the GST_SUBTITLE_ENCODING environment "
"variable will be checked for an encoding to use. If that is not set "
- "either, ISO-8859-15 will be assumed.", DEFAULT_ENCODING,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ "either, then if plugin was build with uchardet support it will be "
+ "used to guess the encoding, otherwise ISO-8859-15 will be assumed.",
+ DEFAULT_ENCODING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_VIDEOFPS,
gst_param_spec_fraction ("video-fps", "Video framerate",
@@ -439,6 +444,35 @@ detect_encoding (const gchar * str, gsize len)
return NULL;
}
+static gchar *
+uchardet_detect_encoding (const gchar * str, gsize len)
+{
+ gchar *charset = NULL;
+ gint retval;
+
+#if defined(HAVE_UCHARDET)
+ uchardet_t handle = uchardet_new ();
+ retval = uchardet_handle_data (handle, str, len);
+
+ GST_DEBUG ("detecting encoding with uchardet using %li characters", len);
+
+ if (retval != 0) {
+ GST_WARNING ("could not handle data with uchardet");
+ } else {
+ uchardet_data_end (handle);
+ charset = g_strdup (uchardet_get_charset (handle));
+
+ if (charset == NULL || *charset == '\0')
+ GST_WARNING ("uchardet could not detect encoding");
+ else
+ GST_INFO ("uchardet detected encoding: %s", charset);
+ }
+ uchardet_delete (handle);
+#endif
+
+ return charset;
+}
+
static gchar *
convert_encoding (GstSubParse * self, const gchar * str, gsize len,
gsize * consumed)
@@ -481,11 +515,18 @@ convert_encoding (GstSubParse * self, const gchar * str, gsize len,
encoding = g_getenv ("GST_SUBTITLE_ENCODING");
}
if (encoding == NULL || *encoding == '\0') {
- /* if local encoding is UTF-8 and no encoding specified
- * via the environment variable, assume ISO-8859-15 */
- if (g_get_charset (&encoding)) {
+ /* no encoding specified via the environment variable either,
+ * so try to autodetect with uchardet */
+ encoding = uchardet_detect_encoding (str, len);
+ }
+
+ /* if uchardet failed and local encoding is UTF-8, assume ISO-8859-15 */
+ if (encoding == NULL || *encoding == '\0') {
+ if (g_get_charset (&encoding))
encoding = "ISO-8859-15";
- }
+ } else {
+ /* reuse the detected encoding from now on */
+ self->detected_encoding = g_strdup (encoding);
}
ret = gst_convert_to_utf8 (str, len, encoding, consumed, &err);
@@ -2159,7 +2200,10 @@ gst_subparse_type_find (GstTypeFind * tf, gpointer private)
enc = g_getenv ("GST_SUBTITLE_ENCODING");
if (enc == NULL || *enc == '\0') {
/* if local encoding is UTF-8 and no encoding specified
- * via the environment variable, assume ISO-8859-15 */
+ * via the environment variable, assume ISO-8859-15
+ *
+ * Encoding here is only used for type find, so no need
+ * to run through uchardet at this point */
if (g_get_charset (&enc)) {
enc = "ISO-8859-15";
}
diff --git a/gst/subparse/meson.build b/gst/subparse/meson.build
index 9a76601f0..2dcf8830f 100644
--- a/gst/subparse/meson.build
+++ b/gst/subparse/meson.build
@@ -6,12 +6,20 @@ subparse_sources = [
'mpl2parse.c',
'qttextparse.c',
]
+subparse_defines = []
+subparse_optional_deps = []
+
+subparse_uchardet_dep = dependency('uchardet', required : false)
+if subparse_uchardet_dep.found()
+ subparse_defines += '-DHAVE_UCHARDET'
+ subparse_optional_deps += subparse_uchardet_dep
+endif
gstsubparse = library('gstsubparse',
subparse_sources,
- c_args : gst_plugins_base_args,
+ c_args : gst_plugins_base_args + subparse_defines,
include_directories: [configinc, libsinc],
- dependencies : [gst_base_dep],
+ dependencies : [gst_base_dep] + subparse_optional_deps,
install : true,
install_dir : plugins_install_dir,
)
--
2.26.2

View File

@@ -1,30 +0,0 @@
{
"name": "gst-plugins-base",
"buildsystem": "meson",
"config-opts": [
"--wrap-mode=nofallback",
"-Ddoc=disabled",
"-Dexamples=disabled",
"-Dtests=disabled",
"-Dnls=disabled",
"-Dgobject-cast-checks=disabled",
"-Dglib-asserts=disabled",
"-Dglib-checks=disabled",
"-Dgl_api=opengl,gles2",
"-Dgl_platform=egl,glx"
],
"sources": [
{
"type": "git",
"url": "https://gitlab.freedesktop.org/gstreamer/gst-plugins-base.git",
"tag": "1.18.1",
"commit": "4013b8003e78971dd01b055066c12f8aaadb8897"
},
{
"type": "patch",
"path": "gst-plugins-base-autodetect-subtitle-text-encoding.patch"
}
]
}

View File

@@ -1,67 +0,0 @@
From b2ad7c68c3478c433a0ede4aed6afb2f0b32702c Mon Sep 17 00:00:00 2001
From: Rafostar <40623528+Rafostar@users.noreply.github.com>
Date: Sun, 10 Jan 2021 15:44:45 +0100
Subject: [PATCH] matroska: fix attachments detection in large data blocks
Due to max block size limit being set to 15MB, large
attachments (fonts of few MB in size) were undetected
as attachments consist of single data block. Raise max
data block limit to 30MB to fix that.
---
gst/matroska/matroska-demux.c | 34 ++++++++++++++++------------------
1 file changed, 16 insertions(+), 18 deletions(-)
diff --git a/gst/matroska/matroska-demux.c b/gst/matroska/matroska-demux.c
index 4d0234743..ce906e5a3 100644
--- a/gst/matroska/matroska-demux.c
+++ b/gst/matroska/matroska-demux.c
@@ -5115,30 +5115,28 @@ gst_matroska_demux_parse_contents (GstMatroskaDemux * demux, GstEbmlRead * ebml)
}
#define GST_FLOW_OVERFLOW GST_FLOW_CUSTOM_ERROR
-
-#define MAX_BLOCK_SIZE (15 * 1024 * 1024)
+#define MAX_BLOCK_SIZE (60 * 1024 * 1024)
static inline GstFlowReturn
gst_matroska_demux_check_read_size (GstMatroskaDemux * demux, guint64 bytes)
{
- if (G_UNLIKELY (bytes > MAX_BLOCK_SIZE)) {
- /* only a few blocks are expected/allowed to be large,
- * and will be recursed into, whereas others will be read and must fit */
- if (demux->streaming) {
- /* fatal in streaming case, as we can't step over easily */
- GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL),
- ("reading large block of size %" G_GUINT64_FORMAT " not supported; "
- "file might be corrupt.", bytes));
- return GST_FLOW_ERROR;
- } else {
- /* indicate higher level to quietly give up */
- GST_DEBUG_OBJECT (demux,
- "too large block of size %" G_GUINT64_FORMAT, bytes);
- return GST_FLOW_ERROR;
- }
- } else {
+ if (G_LIKELY (bytes <= MAX_BLOCK_SIZE))
return GST_FLOW_OK;
+
+ /* only a few blocks are expected/allowed to be large,
+ * and will be recursed into, whereas others will be read and must fit */
+ if (demux->streaming) {
+ /* fatal in streaming case, as we can't step over easily */
+ GST_ELEMENT_ERROR (demux, STREAM, DEMUX, (NULL),
+ ("reading large block of size %" G_GUINT64_FORMAT " not supported; "
+ "file might be corrupt.", bytes));
+ } else {
+ /* indicate higher level to quietly give up */
+ GST_DEBUG_OBJECT (demux, "too large block of size %" G_GUINT64_FORMAT,
+ bytes);
}
+
+ return GST_FLOW_ERROR;
}
/* returns TRUE if we truly are in error state, and should give up */
--
2.29.2

View File

@@ -1,36 +0,0 @@
From 4e5b2b0c3aeefffdd9613e33678cade25fac3fe4 Mon Sep 17 00:00:00 2001
From: Rafostar <rafostar.github@gmail.com>
Date: Sun, 10 Jan 2021 19:55:31 +0100
Subject: [PATCH] matroska: treat non-image structure as attachment and set
mimetype
Otherwise each structure is named as GstTagImageInfo even if it does not contain any images
which is misleading. Also set the structure mimetype to fix assrender fonts detection.
---
gst/matroska/matroska-read-common.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/gst/matroska/matroska-read-common.c b/gst/matroska/matroska-read-common.c
index 90d6e38e1..628e19669 100644
--- a/gst/matroska/matroska-read-common.c
+++ b/gst/matroska/matroska-read-common.c
@@ -851,10 +851,13 @@ gst_matroska_read_common_parse_attached_file (GstMatroskaReadCommon * common,
}
/* Set filename and description in the info */
- if (info == NULL)
- info = gst_structure_new_empty ("GstTagImageInfo");
-
+ if (info == NULL) {
+ const gchar *structure_name = (image_type != GST_TAG_IMAGE_TYPE_NONE) ?
+ "GstTagImageInfo" : "GstTagAttachmentInfo";
+ info = gst_structure_new_empty (structure_name);
+ }
gst_structure_set (info, "filename", G_TYPE_STRING, filename, NULL);
+ gst_structure_set (info, "mimetype", G_TYPE_STRING, mimetype, NULL);
if (description)
gst_structure_set (info, "description", G_TYPE_STRING, description, NULL);
--
2.28.0

View File

@@ -1,31 +0,0 @@
{
"name": "gst-plugins-good",
"buildsystem": "meson",
"config-opts": [
"-Ddoc=disabled",
"-Dexamples=disabled",
"-Dtests=disabled",
"-Dnls=disabled",
"-Dgobject-cast-checks=disabled",
"-Dglib-asserts=disabled",
"-Dglib-checks=disabled",
"-Dgtk3=disabled"
],
"sources": [
{
"type": "git",
"url": "https://gitlab.freedesktop.org/gstreamer/gst-plugins-good.git",
"tag": "1.18.1",
"commit": "7c44cdb0e00dd1c9932d8e5194b09fcf4e1e6fc1"
},
{
"type": "patch",
"path": "gst-plugins-good-matroska-fix-attachments-detection.patch"
},
{
"type": "patch",
"path": "gst-plugins-good-matroska-set-attachment-mimetype.patch"
}
]
}

View File

@@ -1,22 +0,0 @@
{
"name": "gst-plugins-ugly",
"buildsystem": "meson",
"config-opts": [
"-Ddoc=disabled",
"-Dnls=disabled",
"-Dtests=disabled",
"-Dgobject-cast-checks=disabled",
"-Dglib-asserts=disabled",
"-Dglib-checks=disabled",
"-Dmpeg2dec=enabled"
],
"sources": [
{
"type": "git",
"url": "https://gitlab.freedesktop.org/gstreamer/gst-plugins-ugly.git",
"tag": "1.18.1",
"commit": "720672eed30b3be47b2f26d67554786c0d3693ad"
}
]
}

View File

@@ -1,83 +0,0 @@
From 65fc08032a41ae8779d1845dce2c00b1efa2955c Mon Sep 17 00:00:00 2001
From: Rafostar <rafostar.github@gmail.com>
Date: Tue, 22 Dec 2020 15:08:21 +0100
Subject: [PATCH] glx: Iterate over FBConfig and select 8 bit color size
---
gst-libs/gst/vaapi/gstvaapiutils_glx.c | 40 ++++++++++++++++++++++++--
1 file changed, 38 insertions(+), 2 deletions(-)
diff --git a/gst-libs/gst/vaapi/gstvaapiutils_glx.c b/gst-libs/gst/vaapi/gstvaapiutils_glx.c
index ccd7832b..f73106c2 100644
--- a/gst-libs/gst/vaapi/gstvaapiutils_glx.c
+++ b/gst-libs/gst/vaapi/gstvaapiutils_glx.c
@@ -301,9 +301,17 @@ gl_create_context (Display * dpy, int screen, GLContextState * parent)
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
+ GLX_ALPHA_SIZE, 8,
None
};
+ const GLint rgba_colors[4] = {
+ GLX_RED_SIZE,
+ GLX_GREEN_SIZE,
+ GLX_BLUE_SIZE,
+ GLX_ALPHA_SIZE
+ };
+
cs = malloc (sizeof (*cs));
if (!cs)
goto error;
@@ -333,11 +341,38 @@ gl_create_context (Display * dpy, int screen, GLContextState * parent)
if (!fbconfigs)
goto error;
- /* Find out a GLXFBConfig compatible with the parent context */
+ /* Find out a 8 bit GLXFBConfig compatible with the parent context */
for (n = 0; n < n_fbconfigs; n++) {
+ gboolean sizes_correct = FALSE;
+ int cn;
+
status = glXGetFBConfigAttrib (parent->display,
fbconfigs[n], GLX_FBCONFIG_ID, &val);
- if (status == Success && val == fbconfig_id)
+ if (status != Success)
+ goto error;
+ if (val != fbconfig_id)
+ continue;
+
+ /* Iterate over RGBA sizes in fbconfig */
+ for (cn = 0; cn < 4; cn++) {
+ int size = 0;
+
+ status = glXGetFBConfigAttrib (parent->display,
+ fbconfigs[n], rgba_colors[cn], &size);
+ if (status != Success)
+ goto error;
+
+ /* Last check is for alpha
+ * and alpha is optional */
+ if (cn == 3) {
+ if (size == 0 || size == 8) {
+ sizes_correct = TRUE;
+ break;
+ }
+ } else if (size != 8)
+ break;
+ }
+ if (sizes_correct)
break;
}
if (n == n_fbconfigs)
@@ -809,6 +844,7 @@ gl_create_pixmap_object (Display * dpy, guint width, guint height)
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
+ GLX_ALPHA_SIZE, 8,
GL_NONE,
};
--
2.28.0

View File

@@ -1,21 +0,0 @@
{
"name": "gstreamer-vaapi",
"buildsystem": "meson",
"config-opts": [
"-Ddoc=disabled",
"-Dexamples=disabled",
"-Dtests=disabled"
],
"sources": [
{
"type": "git",
"url": "https://gitlab.freedesktop.org/gstreamer/gstreamer-vaapi.git",
"tag": "1.18.1",
"commit": "f9e925af3645439f7b7a4580700fcd6ce17fc1c9"
},
{
"type": "patch",
"path": "gstreamer-vaapi-glx-select-8-bit-color-size.patch"
}
]
}

View File

@@ -1,24 +0,0 @@
{
"name": "gstreamer",
"buildsystem": "meson",
"config-opts": [
"-Ddoc=disabled",
"-Dgtk_doc=disabled",
"-Dexamples=disabled",
"-Dtests=disabled",
"-Dbenchmarks=disabled",
"-Dnls=disabled",
"-Dgobject-cast-checks=disabled",
"-Dglib-asserts=disabled",
"-Dglib-checks=disabled",
"-Dextra-checks=disabled"
],
"sources": [
{
"type": "git",
"url": "https://gitlab.freedesktop.org/gstreamer/gstreamer.git",
"tag": "1.18.1",
"commit": "29a8099d1d4bd8717c13923e710e92e67e335353"
}
]
}

View File

@@ -1,22 +0,0 @@
--- a52dec-0.7.4/configure~ 2002-07-28 06:50:42.000000000 +0300
+++ a52dec-0.7.4/configure 2006-02-16 23:03:07.000000000 +0200
@@ -5839,7 +5839,7 @@
shlibpath_overrides_runpath=unknown
version_type=none
dynamic_linker="$host_os ld.so"
-sys_lib_dlsearch_path_spec="/lib /usr/lib"
+sys_lib_dlsearch_path_spec="/lib64 /usr/lib64 /lib /usr/lib"
sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
case $host_os in
--- a52dec-0.7.4/aclocal.m4~ 2002-07-28 06:50:38.000000000 +0300
+++ a52dec-0.7.4/aclocal.m4 2006-02-16 23:02:38.000000000 +0200
@@ -2141,7 +2141,7 @@
shlibpath_overrides_runpath=unknown
version_type=none
dynamic_linker="$host_os ld.so"
-sys_lib_dlsearch_path_spec="/lib /usr/lib"
+sys_lib_dlsearch_path_spec="/lib64 /usr/lib64 /lib /usr/lib"
sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
case $host_os in

View File

@@ -1,23 +0,0 @@
--- ./configure~ 2002-07-28 06:50:42.000000000 +0300
+++ ./configure 2003-04-13 17:20:53.000000000 +0300
@@ -2857,13 +2857,13 @@
case "$host" in
i?86-* | k?-*)
- case "$host" in
- i386-*) OPT_CFLAGS="$CFLAGS -mcpu=i386";;
- i486-*) OPT_CFLAGS="$CFLAGS -mcpu=i486";;
- i586-*) OPT_CFLAGS="$CFLAGS -mcpu=pentium";;
- i686-*) OPT_CFLAGS="$CFLAGS -mcpu=pentiumpro";;
- k6-*) OPT_CFLAGS="$CFLAGS -mcpu=k6";;
- esac
+# case "$host" in
+# i386-*) OPT_CFLAGS="$CFLAGS -mcpu=i386";;
+# i486-*) OPT_CFLAGS="$CFLAGS -mcpu=i486";;
+# i586-*) OPT_CFLAGS="$CFLAGS -mcpu=pentium";;
+# i686-*) OPT_CFLAGS="$CFLAGS -mcpu=pentiumpro";;
+# k6-*) OPT_CFLAGS="$CFLAGS -mcpu=k6";;
+# esac
echo "$as_me:$LINENO: checking if $CC supports $OPT_CFLAGS flags" >&5
echo $ECHO_N "checking if $CC supports $OPT_CFLAGS flags... $ECHO_C" >&6
SAVE_CFLAGS="$CFLAGS"

View File

@@ -1,29 +0,0 @@
{
"name": "ffmpeg",
"cleanup": [
"/lib/ffmpeg/examples"
],
"config-opts": [
"--disable-debug",
"--disable-doc",
"--disable-static",
"--disable-everything",
"--enable-gpl",
"--enable-version3",
"--enable-optimizations",
"--enable-runtime-cpudetect",
"--enable-shared",
"--enable-protocol=file",
"--enable-decoder=flv,h263,h264,hevc,mjpeg,mpeg2video,mpeg4,mpegvideo,msmpeg4v1,msmpeg4v2,png,tiff,vc1,vp8,vp9,webp,wmv1,wmv2,wmv3,zerocodec",
"--enable-decoder=aac,aac_fixed,aac_latm,ac3,ac3_fixed,eac3,flac,mp3,opus,tak,truehd,tta,wmalossless",
"--enable-demuxer=gif,yuv4mpegpipe"
],
"sources": [
{
"type": "git",
"url": "https://git.ffmpeg.org/ffmpeg.git",
"tag": "n4.3.1",
"commit": "6b6b9e593dd4d3aaf75f48d40a13ef03bdef9fdb"
}
]
}

View File

@@ -1,12 +0,0 @@
{
"name": "glib-networking",
"buildsystem": "meson",
"sources": [
{
"type": "git",
"url": "https://gitlab.gnome.org/GNOME/glib-networking.git",
"tag": "2.66.0",
"commit": "61d7e024ca354e6d2e39930d66a2067f3de5842c"
}
]
}

View File

@@ -1,26 +0,0 @@
From c6320cfd75c65bfb1736b7ca5afc9c0f5ffc09d7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Dzi=C4=99giel?= <rafostar.github@gmail.com>
Date: Thu, 25 Feb 2021 09:45:38 +0100
Subject: [PATCH] Broadway: fix unsafe variable type
Only guint32 guarantees to be always 32bit on all platforms. Mixing 32bit and 64bit memory sizes leads to a crash.
---
gdk/broadway/gdkbroadway-server.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/gdk/broadway/gdkbroadway-server.c b/gdk/broadway/gdkbroadway-server.c
index 02b6f93183..e6b96ff0b9 100644
--- a/gdk/broadway/gdkbroadway-server.c
+++ b/gdk/broadway/gdkbroadway-server.c
@@ -235,7 +235,7 @@ static void
parse_all_input (GdkBroadwayServer *server)
{
guint8 *p, *end;
- size_t size;
+ guint32 size;
BroadwayReply *reply;
p = server->recv_buffer;
--
2.26.2

View File

@@ -1,31 +0,0 @@
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

View File

@@ -1,33 +0,0 @@
{
"name": "gtk",
"buildsystem": "meson",
"config-opts": [
"--wrap-mode=nofallback",
"-Dbroadway-backend=true",
"-Dwin32-backend=false",
"-Dmacos-backend=false",
"-Dmedia-ffmpeg=disabled",
"-Dprint-cups=disabled",
"-Dprint-cloudprint=disabled",
"-Dintrospection=enabled",
"-Ddemos=false",
"-Dbuild-examples=false",
"-Dbuild-tests=false"
],
"sources": [
{
"type": "git",
"url": "https://gitlab.gnome.org/GNOME/gtk.git",
"tag": "4.1.1",
"commit": "1f284fcd706de5b0b8c54fee3ff61880caf1d167"
},
{
"type": "patch",
"path": "gtk4-popover-unrealize.patch"
},
{
"type": "patch",
"path": "gtk4-broadway-fix-unsafe-variable-type.patch"
}
]
}

View File

@@ -1,39 +0,0 @@
From 4c18c43b4d4ccb1d05ae73b813f26ba193fbeee3 Mon Sep 17 00:00:00 2001
From: Bastien Nocera <hadess@hadess.net>
Date: Fri, 18 Jan 2019 17:37:13 +0100
Subject: [PATCH] Prefer PIC
---
configure | 2 +-
liba52/configure.incl | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/configure b/configure
index b81fdff..bc0267c 100755
--- a/configure
+++ b/configure
@@ -9640,7 +9640,7 @@ _ACEOF
-LIBA52_CFLAGS="$LIBA52_CFLAGS -prefer-non-pic"
+LIBA52_CFLAGS="$LIBA52_CFLAGS -prefer-pic"
# Check whether --enable-double or --disable-double was given.
if test "${enable_double+set}" = set; then
diff --git a/liba52/configure.incl b/liba52/configure.incl
index 4dbbcea..5eb69ee 100644
--- a/liba52/configure.incl
+++ b/liba52/configure.incl
@@ -2,7 +2,7 @@ AC_SUBST([LIBA52_CFLAGS])
AC_SUBST([LIBA52_LIBS])
dnl avoid -fPIC when possible
-LIBA52_CFLAGS="$LIBA52_CFLAGS -prefer-non-pic"
+LIBA52_CFLAGS="$LIBA52_CFLAGS -prefer-pic"
AC_ARG_ENABLE([double],
[ --enable-double use double-precision samples])
--
2.20.1

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