Merge pull request #374 from Rafostar/libclapper

Introduce Clapper libraries
This commit is contained in:
Rafał Dzięgiel
2024-04-05 22:30:55 +02:00
committed by GitHub
376 changed files with 48498 additions and 19838 deletions

View File

@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 600
container:
image: bilelmoussaoui/flatpak-github-actions:gnome-43
image: bilelmoussaoui/flatpak-github-actions:gnome-45
options: --privileged
strategy:
matrix:

View File

@@ -55,7 +55,7 @@ modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
@@ -111,7 +111,7 @@ modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
@@ -158,7 +158,7 @@ Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
@@ -216,7 +216,7 @@ instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
@@ -267,7 +267,7 @@ Library will still fall under Section 6.)
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
@@ -329,7 +329,7 @@ restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
@@ -370,7 +370,7 @@ subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
@@ -422,7 +422,7 @@ conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
@@ -456,7 +456,7 @@ SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
@@ -485,7 +485,8 @@ convey the exclusion of warranty; and each file should have at least the
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
USA
Also add information on how to contact you by electronic and paper mail.
@@ -494,7 +495,8 @@ school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
library `Frob' (a library for tweaking knobs) written by James Random
Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice

View File

@@ -1,9 +0,0 @@
#!@GJS@
imports.package.init({
name: '@PACKAGE_NAME@',
version: '@PACKAGE_VERSION@',
prefix: '@prefix@',
libdir: '@libdir@',
});
imports.package.run(imports.src.main);

View File

@@ -1,22 +0,0 @@
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('prefix', get_option('prefix'))
bin_conf.set('libdir', libdir)
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'
)
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)

View File

@@ -1,22 +0,0 @@
#!/usr/bin/env python3
from os import environ, path
from subprocess import call
prefix = environ.get('MESON_INSTALL_PREFIX', '/usr/local')
sharedir = path.join(prefix, 'share')
destdir = environ.get('DESTDIR', '')
# Package managers set this so we don't need to run
if not destdir:
print('Updating icon cache...')
call(['gtk4-update-icon-cache', '-qtf', path.join(sharedir, 'icons', 'hicolor')])
print('Updating mime database...')
call(['update-mime-database', path.join(sharedir, 'mime')])
print('Updating desktop database...')
call(['update-desktop-database', '-q', path.join(sharedir, 'applications')])
print('Compiling GSettings schemas...')
call(['glib-compile-schemas', path.join(sharedir, 'glib-2.0', 'schemas')])

View File

@@ -1,426 +0,0 @@
/* Defaults */
scale marks {
color: currentColor;
}
radio {
margin-left: -2px;
}
scrolledwindow scrollbar.vertical slider {
min-height: 16px;
}
/* Consistent scales color */
scale trough highlight {
color: @accent_fg_color;
background-color: @accent_bg_color;
}
/* Consistent radio buttons color */
.osd radio {
color: @accent_fg_color;
background-color: transparent;
background-image: none;
}
.osd radio:hover,
.osd radio:checked {
background-image: image(rgba(255,255,255,0.1));
}
.osd radio:active {
background-image: image(rgba(255,255,255,0.3));
}
/* Adwaita OSD background color is unacceptable:
* https://gitlab.gnome.org/GNOME/libadwaita/-/issues/454 */
box.osd,
.osd popover contents,
.osd popover arrow,
.osdheaderbar button {
background-color: rgba(38,38,38,0.78);
}
.osdheaderbar button:hover,
.osdheaderbar button:checked {
background-color: rgba(63,63,63,0.78);
}
.osdheaderbar button:active {
background-color: rgba(82,82,82,0.78);
}
/* Adwaita is missing osd ListBox */
.clapperplaylist {
background: none;
}
.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;
}
.osdheaderbar {
background: transparent;
}
.osdheaderbar button {
border: transparent;
}
.linkseparator {
background: rgba(24,24,24,0.72);
min-width: 1px;
}
.linkedleft image {
margin-left: 2px;
}
.linkedright image {
margin-right: 2px;
}
/* Flat popovers */
popover arrow,
popover contents {
border-color: transparent;
box-shadow: none;
}
.popoverseparator separator {
background-color: @insensitive_fg_color;
margin-left: 3px;
margin-right: 3px;
}
/* Rounded corners */
.adwrounded.csd {
border-radius: 8px;
}
.adwrounded.fullscreen,
.adwrounded.maximized,
.adwrounded.tiled,
.adwrounded.tiled-top,
.adwrounded.tiled-left,
.adwrounded.tiled-right,
.adwrounded.tiled-bottom {
border-radius: 0px;
}
.roundedcorners {
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;
}
.fullscreen.tvmode popover box {
text-shadow: none;
font-size: 21px;
font-weight: 500;
}
.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;
}
.fullscreen.tvmode radio {
margin-left: 0px;
margin-right: 4px;
border: 2px solid;
min-width: 16px;
min-height: 16px;
box-shadow: none;
}
/* Also affects popover buttons */
.fullscreen.tvmode .clappercontrols button image {
-gtk-icon-size: 26px;
}
.clappercontrolsbutton.text-button label {
font-family: 'Cantarell', sans-serif;
font-variant-numeric: tabular-nums;
font-weight: 600;
}
.fullscreen.tvmode .clappercontrolsbutton.text-button label {
font-size: 22px;
text-shadow: none;
}
/* Top Revealer */
.fullscreen.tvmode .revealertopgrid {
font-family: 'Cantarell', sans-serif;
}
.fullscreen.tvmode .tvtitle {
font-size: 28px;
font-weight: 500;
text-shadow: none;
}
.fullscreen.tvmode .tvtime {
margin-top: -2px;
margin-bottom: -2px;
min-height: 4px;
font-size: 38px;
font-weight: 700;
font-variant-numeric: tabular-nums;
}
.fullscreen.tvmode .tvendtime {
margin-top: -4px;
margin-bottom: 2px;
min-height: 6px;
font-size: 24px;
font-weight: 600;
font-variant-numeric: tabular-nums;
}
/* Position Scale */
.positionscale {
margin: -2px;
}
.positionscale trough highlight {
min-height: 6px;
}
.fullscreen.tvmode .positionscale {
padding-left: 12px;
padding-right: 12px;
}
.fullscreen.tvmode .positionscale.fine-tune {
padding-left: 12px;
padding-right: 12px;
}
.fullscreen.tvmode .positionscale trough slider {
color: transparent;
background: transparent;
border-color: transparent;
box-shadow: none;
outline: none;
}
.positionscale mark indicator {
min-height: 6px;
}
.positionscale.fine-tune mark indicator {
min-height: 6px;
}
.fullscreen.tvmode .positionscale mark indicator {
min-height: 7px;
min-width: 2px;
}
.fullscreen.tvmode .positionscale.fine-tune mark indicator {
min-height: 7px;
min-width: 2px;
}
.positionscale marks.top {
margin-top: -6px;
margin-bottom: 4px;
}
.positionscale marks.bottom {
margin-top: 4px;
margin-bottom: -6px;
}
.fullscreen.tvmode .positionscale marks.top {
margin-bottom: 2px;
}
.fullscreen.tvmode .positionscale marks.bottom {
margin-top: 2px;
}
.fullscreen.tvmode .positionscale trough {
border-radius: 3px;
}
.fullscreen.tvmode .positionscale trough highlight {
border-radius: 3px;
min-height: 20px;
}
.fullscreen.tvmode .positionscale.fine-tune trough highlight {
border-radius: 3px;
min-height: 20px;
}
/* Volume Scale */
.volumescale {
margin: -2px;
margin-left: -8px;
margin-right: -6px;
min-height: 180px;
}
.fullscreen.tvmode .volumescale {
margin: 2px;
margin-left: -6px;
margin-right: -4px;
min-height: 260px;
}
.volumescale marks label {
margin-right: 4px;
margin-top: -4px;
margin-bottom: -6px;
}
.volumescale trough highlight {
min-width: 4px;
}
.fullscreen.tvmode .volumescale trough highlight {
min-width: 6px;
}
.overamp trough highlight {
color: @error_fg_color;
background-color: @error_bg_color;
}
/* Elapsed Popover */
.elapsedpopover {
min-width: 326px;
}
.fullscreen.tvmode .elapsedpopover {
min-width: 448px;
}
.elapsedpopover contents {
padding-bottom: 0px;
}
.speedscale {
margin-left: 4px;
margin-right: 4px;
}
.speedscale trough highlight {
min-height: 4px;
}
.fullscreen.tvmode .speedscale trough highlight {
min-height: 6px;
}
.narrowbutton {
min-width: 8px;
}
@keyframes halfrotation {
to { transform: rotate(0.5turn); }
}
.halfrotate {
animation-name: halfrotation;
animation-duration: 200ms;
animation-delay: 280ms;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-iteration-count: 1;
}
/* Chapters */
.chapterlabel {
min-width: 32px;
}
.fullscreen.tvmode .chapterlabel {
min-width: 40px;
text-shadow: none;
font-size: 22px;
font-weight: 600;
}
/* Open URI Dialog */
.uridialogbox {
margin: 10px;
}
/* Tweaks */
.nobackground {
background: none;
}
.noborder {
border: none;
}
.controlsbox {
background: @popover_bg_color;
}
.gpufriendly {
box-shadow: -8px -8px transparent, 8px 8px transparent;
}
.fullscreen.gpufriendlyfs {
box-shadow: none;
}
/* 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

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

@@ -1,108 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<schemalist gettext-domain="com.github.rafostar.Clapper">
<schema id="com.github.rafostar.Clapper" path="/com/github/rafostar/Clapper/">
<!-- General -->
<key name="fullscreen-auto" type="b">
<default>false</default>
<summary>Automatically enter fullscreen when first file is loaded</summary>
</key>
<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="after-playback" type="i">
<default>0</default>
<summary>What to do after playback finishes</summary>
</key>
<!-- 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="i">
<default>0</default>
<summary>Unit ID to use with seeking value</summary>
</key>
<key name="resume-enabled" type="b">
<default>false</default>
<summary>Ask to resume unfinished video</summary>
</key>
<key name="resume-database" type="s">
<default>'[]'</default>
<summary>Data storing unfinished videos resume info</summary>
</key>
<key name="floating-stick" type="b">
<default>false</default>
<summary>Auto stick floating window to all workspaces</summary>
</key>
<!-- Audio -->
<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-font" type="s">
<default>"Sans 12"</default>
<summary>The subtitles font description</summary>
</key>
<!-- Network -->
<key name="webserver-enabled" type="b">
<default>false</default>
<summary>Enable WebSocket server for remote playback control</summary>
</key>
<key name="webserver-port" type="i">
<default>6446</default>
<summary>Listening port to use for incoming WebSocket connections</summary>
</key>
<!-- Tweaks -->
<key name="dark-theme" type="b">
<default>true</default>
<summary>Enable to force the app to use dark theme variant</summary>
</key>
<key name="render-shadows" type="b">
<default>true</default>
<summary>Enable rendering window shadows (only if theme has them)</summary>
</key>
<!-- GStreamer -->
<key name="plugin-ranking" type="s">
<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>1559</default>
<summary>Set PlayFlags for playbin</summary>
</key>
<!-- Other -->
<key name="window-size" type="s">
<default>'[800, 490]'</default>
<summary>Stores window size to restore on next launch</summary>
</key>
<key name="volume-last" type="d">
<default>1</default>
<summary>Stores last linear volume value to apply on startup</summary>
</key>
</schema>
</schemalist>

View File

@@ -1,3 +0,0 @@
[D-BUS Service]
Name=@app_id@
Exec=@bindir@/@app_id@ --gapplication-service

View File

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

Before

Width:  |  Height:  |  Size: 262 B

View File

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

Before

Width:  |  Height:  |  Size: 4.6 KiB

View File

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

Before

Width:  |  Height:  |  Size: 4.7 KiB

View File

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

Before

Width:  |  Height:  |  Size: 480 B

View File

@@ -1,46 +0,0 @@
iconsdir = join_paths(datadir, 'icons', 'hicolor')
appstream_util = find_program('appstream-util', required: false)
if appstream_util.found()
test('Validate appstream file', appstream_util, args: [
'validate-relax', '--nonet',
join_paths(meson.current_source_dir(), 'com.github.rafostar.Clapper.metainfo.xml')
])
endif
install_data('com.github.rafostar.Clapper.svg',
install_dir: join_paths(iconsdir, 'scalable', 'apps')
)
install_data('com.github.rafostar.Clapper-symbolic.svg',
install_dir: join_paths(iconsdir, 'symbolic', 'apps')
)
install_data('com.github.rafostar.Clapper.gschema.xml',
install_dir: join_paths(datadir, 'glib-2.0', 'schemas')
)
install_data('com.github.rafostar.Clapper.xml',
install_dir: join_paths(datadir, 'mime', 'packages')
)
install_data('com.github.rafostar.Clapper.desktop',
install_dir: join_paths(datadir, 'applications')
)
install_data('com.github.rafostar.Clapper.metainfo.xml',
install_dir: join_paths(datadir, 'metainfo')
)
gnome.compile_resources('com.github.rafostar.Clapper.data',
'com.github.rafostar.Clapper.data.gresource.xml',
gresource_bundle: true,
install: true,
install_dir: pkgdatadir,
)
dbus_conf = configuration_data()
dbus_conf.set('app_id', meson.project_name())
dbus_conf.set('bindir', bindir)
configure_file(
input: 'com.github.rafostar.Clapper.service.in',
output: 'com.github.rafostar.Clapper.service',
configuration: dbus_conf,
install: true,
install_dir: join_paths(datadir, 'dbus-1', 'services'),
)

10
doc/meson.build Normal file
View File

@@ -0,0 +1,10 @@
gi_docgen = find_program('gi-docgen', required: get_option('doc'))
dot = find_program('dot', required: get_option('doc')) # Class hierarchy generation
build_doc = (gi_docgen.found() and dot.found() and get_option('doc'))
if build_doc
if not build_gir
error('Building documentation requires introspection to be compiled')
endif
subdir('reference')
endif

View File

@@ -0,0 +1,63 @@
[library]
version = "@CLAPPER_VERSION@"
browse_url = "https://github.com/Rafostar/clapper/"
repository_url = "https://github.com/Rafostar/clapper.git"
website_url = "https://rafostar.github.io/clapper/"
docs_url = "https://rafostar.github.io/clapper/doc/clapper-gtk/"
authors = "Rafał Dzięgiel"
logo_url = "clapper-logo.svg"
license = "LGPL-2.1-or-later"
description = "Clapper GTK integration library"
devhelp = true
search_index = true
dependencies = ["Clapper@CLAPPER_VERSION_SUFFIX@", "Gtk-4.0"]
[dependencies."Clapper@CLAPPER_VERSION_SUFFIX@"]
name = "Clapper"
description = "Clapper playback library"
docs_url = "https://rafostar.github.io/clapper/doc/clapper/"
[dependencies."Gtk-4.0"]
name = "Gtk"
description = "The GTK toolkit"
docs_url = "https://docs.gtk.org/gtk4/"
related = ["GLib-2.0", "GObject-2.0", "Gio-2.0", "Gst-1.0"]
[related."GLib-2.0"]
name = "GLib"
description = "A general-purpose, portable utility library"
docs_url = "https://docs.gtk.org/glib/"
[related."GObject-2.0"]
name = "GObject"
description = "The base type system library"
docs_url = "https://docs.gtk.org/gobject/"
[related."Gio-2.0"]
name = "Gio"
description = "GObject Interfaces and Objects, Networking, IPC, and I/O"
docs_url = "https://docs.gtk.org/gio/"
[related."Gst-1.0"]
name = "Gst"
description = "GStreamer core library"
docs_url = "https://gstreamer.freedesktop.org/documentation/gstreamer/gi-index.html"
[theme]
name = "basic"
show_index_summary = true
show_class_hierarchy = true
[source-location]
base_url = "https://github.com/Rafostar/clapper/tree/master/"
[extra]
# The same order will be used when generating the index
content_files = [
]
content_images = [
"images/clapper-logo.svg",
]
urlmap_file = "urlmap.js"

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -0,0 +1,29 @@
clappergtk_toml = configure_file(
input: 'clapper-gtk.toml.in',
output: 'clapper-gtk.toml',
configuration: doc_version_conf,
install: true,
install_dir: join_paths(datadir, 'doc', 'clapper-gtk'),
)
custom_target('clapper-gtk-doc',
input: [
clappergtk_toml,
clappergtk_gir[0],
],
output: 'clapper-gtk',
command: [
gi_docgen,
'generate',
gi_docgen_common_args,
'--add-include-path=@0@'.format(join_paths(meson.project_build_root(), 'src', 'lib', 'clapper-gtk')),
'--config=@INPUT0@',
'--output-dir=@OUTPUT@',
'--content-dir=@0@'.format(meson.current_build_dir()),
'--content-dir=@0@'.format(meson.current_source_dir()),
'@INPUT1@',
],
build_by_default: true,
install: true,
install_dir: join_paths(datadir, 'doc'),
)

View File

@@ -0,0 +1,8 @@
baseURLs = [
['GLib', 'https://docs.gtk.org/glib/'],
['GObject', 'https://docs.gtk.org/gobject/'],
['Gio', 'https://docs.gtk.org/gio/'],
['Gtk', 'https://docs.gtk.org/gtk4/'],
['Gst', 'https://gstreamer.freedesktop.org/documentation/gstreamer/gi-index.html?'],
['Clapper', 'https://rafostar.github.io/clapper/doc/clapper/'],
]

View File

@@ -0,0 +1,73 @@
[library]
version = "@CLAPPER_VERSION@"
browse_url = "https://github.com/Rafostar/clapper/"
repository_url = "https://github.com/Rafostar/clapper.git"
website_url = "https://rafostar.github.io/clapper/"
docs_url = "https://rafostar.github.io/clapper/doc/clapper/"
authors = "Rafał Dzięgiel"
logo_url = "clapper-logo.svg"
license = "LGPL-2.1-or-later"
description = "Clapper playback library"
devhelp = true
search_index = true
dependencies = ["GLib-2.0", "GObject-2.0", "Gio-2.0", "Gst-1.0", "GstBase-1.0", "GstAudio-1.0", "GstTag-1.0", "GstPbutils-1.0"]
[dependencies."GLib-2.0"]
name = "GLib"
description = "A general-purpose, portable utility library"
docs_url = "https://docs.gtk.org/glib/"
[dependencies."GObject-2.0"]
name = "GObject"
description = "The base type system library"
docs_url = "https://docs.gtk.org/gobject/"
[dependencies."Gio-2.0"]
name = "Gio"
description = "GObject Interfaces and Objects, Networking, IPC, and I/O"
docs_url = "https://docs.gtk.org/gio/"
[dependencies."Gst-1.0"]
name = "Gst"
description = "GStreamer core library"
docs_url = "https://gstreamer.freedesktop.org/documentation/gstreamer/gi-index.html"
[dependencies."GstBase-1.0"]
name = "GstBase"
description = "GStreamer base and utility classes"
docs_url = "https://gstreamer.freedesktop.org/documentation/base/"
[dependencies."GstAudio-1.0"]
name = "GstAudio"
description = "GStreamer audio library"
docs_url = "https://gstreamer.freedesktop.org/documentation/audio/"
[dependencies."GstTag-1.0"]
name = "GstTag"
description = "GStreamer tag support library"
docs_url = "https://gstreamer.freedesktop.org/documentation/tag/"
[dependencies."GstPbutils-1.0"]
name = "GstPbutils"
description = "GStreamer base utils library"
docs_url = "https://gstreamer.freedesktop.org/documentation/pbutils/"
related = []
[theme]
name = "basic"
show_index_summary = true
show_class_hierarchy = true
[source-location]
base_url = "https://github.com/Rafostar/clapper/tree/master/"
[extra]
# The same order will be used when generating the index
content_files = [
]
content_images = [
"images/clapper-logo.svg",
]
urlmap_file = "urlmap.js"

View File

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

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -0,0 +1,29 @@
clapper_toml = configure_file(
input: 'clapper.toml.in',
output: 'clapper.toml',
configuration: doc_version_conf,
install: true,
install_dir: join_paths(datadir, 'doc', 'clapper'),
)
custom_target('clapper-doc',
input: [
clapper_toml,
clapper_gir[0],
],
output: 'clapper',
command: [
gi_docgen,
'generate',
gi_docgen_common_args,
'--add-include-path=@0@'.format(join_paths(meson.project_build_root(), 'src', 'lib', 'clapper')),
'--config=@INPUT0@',
'--output-dir=@OUTPUT@',
'--content-dir=@0@'.format(meson.current_build_dir()),
'--content-dir=@0@'.format(meson.current_source_dir()),
'@INPUT1@',
],
build_by_default: true,
install: true,
install_dir: join_paths(datadir, 'doc'),
)

View File

@@ -0,0 +1,10 @@
baseURLs = [
['GLib', 'https://docs.gtk.org/glib/'],
['GObject', 'https://docs.gtk.org/gobject/'],
['Gio', 'https://docs.gtk.org/gio/'],
['Gst', 'https://gstreamer.freedesktop.org/documentation/gstreamer/gi-index.html?'],
['GstBase', 'https://gstreamer.freedesktop.org/documentation/base/?'],
['GstAudio', 'https://gstreamer.freedesktop.org/documentation/audio/?'],
['GstTag', 'https://gstreamer.freedesktop.org/documentation/tag/?'],
['GstPbutils', 'https://gstreamer.freedesktop.org/documentation/pbutils/?'],
]

18
doc/reference/meson.build Normal file
View File

@@ -0,0 +1,18 @@
doc_version_conf = configuration_data()
doc_version_conf.set('CLAPPER_VERSION', meson.project_version())
doc_version_conf.set('CLAPPER_VERSION_SUFFIX', clapper_version_suffix)
gi_docgen_common_args = [
'--quiet',
'--no-namespace-dir',
]
if get_option('werror')
gi_docgen_common_args += ['--fatal-warnings']
endif
if build_clapper
subdir('clapper')
endif
if build_clappergtk
subdir('clapper-gtk')
endif

View File

@@ -1,183 +0,0 @@
const { GLib } = imports.gi;
let ink = { Ink: null };
try {
ink = imports.ink;
} catch(e) {}
const { Ink } = ink;
const DEBUG_ENV = GLib.getenv('DEBUG');
var Debugger = class
{
constructor(name, opts)
{
opts = (opts && typeof opts === 'object')
? opts : {};
this.name = (name && typeof name === 'string')
? name : 'GJS';
this.print_state = (opts.print_state)
? true : false;
this.json_space = (typeof opts.json_space === 'number')
? opts.json_space : 2;
this.name_printer = opts.name_printer || this._getInkPrinter(true);
this.message_printer = opts.message_printer || this._getDefaultPrinter();
this.time_printer = opts.time_printer || this._getInkPrinter();
this.high_precision = opts.high_precision || false;
if(typeof opts.color !== 'undefined')
this.color = opts.color;
this._isEnabled = false;
this._lastDebug = GLib.get_monotonic_time();
this.enabled = (typeof opts.enabled !== 'undefined')
? opts.enabled : this._enabledAtStart;
}
get enabled()
{
return this._isEnabled;
}
set enabled(value)
{
if(this._isEnabled === value)
return;
this._isEnabled = (value) ? true : false;
if(!this.print_state)
return;
let state = (this.enabled) ? 'en' : 'dis';
this._runDebug(`debug ${state}abled`);
}
get color()
{
return this.name_printer.color;
}
set color(value)
{
this.name_printer.color = value;
this.time_printer.color = this.name_printer.color;
}
get debug()
{
return message => this._debug(message);
}
get _enabledAtStart()
{
if(!DEBUG_ENV)
return false;
let envArr = DEBUG_ENV.split(',');
return envArr.some(el => {
if(el === this.name || el === '*')
return true;
let searchType;
let offset = 0;
if(el.startsWith('*')) {
searchType = 'ends';
} else if(el.endsWith('*')) {
searchType = 'starts';
offset = 1;
}
if(!searchType)
return false;
return this.name[searchType + 'With'](
el.substring(1 - offset, el.length - offset)
);
});
}
_getInkPrinter(isBold)
{
if(!Ink)
return this._getDefaultPrinter();
let printer = new Ink.Printer({
color: Ink.colorFromText(this.name)
});
if(isBold)
printer.font = Ink.Font.BOLD;
return printer;
}
_getDefaultPrinter()
{
return {
getPainted: function() {
return Object.values(arguments);
}
};
}
_debug(message)
{
if(!this.enabled)
return;
this._runDebug(message);
}
_runDebug(message)
{
switch(typeof message) {
case 'string':
break;
case 'object':
if(
message !== null
&& (message.constructor === Object
|| message.constructor === Array)
) {
message = JSON.stringify(message, null, this.json_space);
break;
}
default:
message = String(message);
break;
}
let time = GLib.get_monotonic_time() - this._lastDebug;
if(!this.high_precision) {
time = (time < 1000)
? '+0ms'
: (time < 1000000)
? '+' + Math.floor(time / 1000) + 'ms'
: '+' + Math.floor(time / 1000000) + 's';
}
else {
time = (time < 1000)
? '+' + time + 'µs'
: (time < 1000000)
? '+' + (time / 1000).toFixed(3) + 'ms'
: '+' + (time / 1000000).toFixed(3) + 's';
}
printerr(
this.name_printer.getPainted(this.name),
this.message_printer.getPainted(message),
this.time_printer.getPainted(time)
);
this._lastDebug = GLib.get_monotonic_time();
}
}

View File

@@ -1,322 +0,0 @@
const TERM_ESC = '\x1B[';
const TERM_RESET = '0m';
var maxTransparency = 128;
var Font = {
VARIOUS: null,
REGULAR: 0,
BOLD: 1,
DIM: 2,
ITALIC: 3,
UNDERLINE: 4,
BLINK: 5,
REVERSE: 7,
HIDDEN: 8,
STRIKEOUT: 9,
};
var Color = {
VARIOUS: null,
DEFAULT: 39,
BLACK: 30,
RED: 31,
GREEN: 32,
YELLOW: 33,
BLUE: 34,
MAGENTA: 35,
CYAN: 36,
LIGHT_GRAY: 37,
DARK_GRAY: 90,
LIGHT_RED: 91,
LIGHT_GREEN: 92,
LIGHT_YELLOW: 93,
LIGHT_BLUE: 94,
LIGHT_MAGENTA: 95,
LIGHT_CYAN: 96,
WHITE: 97,
BROWN: colorFrom256(52),
LIGHT_BROWN: colorFrom256(130),
PINK: colorFrom256(205),
LIGHT_PINK: colorFrom256(211),
ORANGE: colorFrom256(208),
LIGHT_ORANGE: colorFrom256(214),
SALMON: colorFrom256(209),
LIGHT_SALMON: colorFrom256(216),
};
function colorFrom256(number)
{
if(typeof number === 'undefined')
number = Math.floor(Math.random() * 256) + 1;
return `38;5;${number || 0}`;
}
function colorFromRGB(R, G, B, A)
{
if(typeof R === 'undefined') {
R = Math.floor(Math.random() * 256);
G = Math.floor(Math.random() * 256);
B = Math.floor(Math.random() * 256);
}
else if(typeof G === 'undefined' && Array.isArray(R)) {
A = (R.length > 3) ? R[3] : 255;
B = (R.length > 2) ? R[2] : 0;
G = (R.length > 1) ? R[1] : 0;
R = (R.length > 0) ? R[0] : 0;
}
if(_getIsTransparent(A))
return Color.DEFAULT;
R = R || 0;
G = G || 0;
B = B || 0;
return `38;2;${R};${G};${B}`;
}
function colorFromHex(R, G, B, A)
{
if((Array.isArray(R)))
R = R.join('');
let str = (typeof G === 'undefined')
? String(R)
: (typeof A !== 'undefined')
? String(R) + String(G) + String(B) + String(A)
: (typeof B !== 'undefined')
? String(R) + String(G) + String(B)
: String(R) + String(G);
let offset = (str[0] === '#') ? 1 : 0;
let alphaIndex = 6 + offset;
while(str.length < alphaIndex)
str += '0';
A = (str.length > alphaIndex)
? parseInt(str.substring(alphaIndex, alphaIndex + 2), 16)
: 255;
str = str.substring(offset, alphaIndex);
let colorInt = parseInt(str, 16);
let u8arr = new Uint8Array(3);
u8arr[2] = colorInt;
u8arr[1] = colorInt >> 8;
u8arr[0] = colorInt >> 16;
return colorFromRGB(u8arr[0], u8arr[1], u8arr[2], A);
}
function colorFromText(text)
{
let value = _stringToDec(text);
/* Returns color from 1 to 221 every 10 */
return colorFrom256((value % 23) * 10 + 1);
}
function fontFromText(text)
{
let arr = Object.keys(Font);
let value = _stringToDec(text);
/* Return a font excluding first (null) */
return Font[arr[value % (arr.length - 1) + 1]];
}
function _getIsImage(args)
{
if(args.length !== 1)
return false;
let arg = args[0];
let argType = (typeof arg);
if(argType === 'string' || argType === 'number')
return false;
if(!Array.isArray(arg))
return false;
let depth = 2;
while(depth--) {
arg = arg[0];
if(!Array.isArray(arg))
return false;
}
return arg.some(val => val !== 'number');
}
function _getIsTransparent(A)
{
return (typeof A !== 'undefined' && A <= maxTransparency);
}
function _stringToDec(str)
{
str = str || '';
let len = str.length;
let total = 0;
while(len--)
total += Number(str.charCodeAt(len).toString(10));
return total;
}
var Printer = class
{
constructor(opts)
{
opts = opts || {};
const defaults = {
font: Font.REGULAR,
color: Color.DEFAULT,
background: Color.DEFAULT
};
for(let def in defaults) {
this[def] = (typeof opts[def] !== 'undefined')
? opts[def] : defaults[def];
}
}
print()
{
(_getIsImage(arguments))
? this._printImage(arguments[0], 'stdout')
: print(this._getPaintedArgs(arguments));
}
printerr()
{
(_getIsImage(arguments))
? this._printImage(arguments[0], 'stderr')
: printerr(this._getPaintedArgs(arguments));
}
getPainted()
{
return (_getIsImage(arguments))
? this._printImage(arguments[0], 'return')
: this._getPaintedArgs(arguments);
}
get background()
{
return this._background;
}
set background(value)
{
let valueType = (typeof value);
if(valueType === 'string') {
value = (value[2] === ';')
? '4' + value.substring(1)
: Number(value);
}
this._background = (valueType === 'object')
? null
: (value < 40 || value >= 90 && value < 100)
? value + 10
: value;
}
_getPaintedArgs(args)
{
let str = '';
for(let arg of args) {
if(Array.isArray(arg))
arg = arg.join(',');
let painted = this._getPaintedString(arg);
str += (str.length) ? ' ' + painted : painted;
}
return str;
}
_getPaintedString(text, noReset)
{
let str = TERM_ESC;
for(let option of ['font', 'color', '_background']) {
let optionType = (typeof this[option]);
str += (optionType === 'number' || optionType === 'string')
? this[option]
: (option === 'font' && Array.isArray(this[option]))
? this[option].join(';')
: (option === 'font')
? fontFromText(text)
: colorFromText(text);
str += (option !== '_background') ? ';' : 'm';
}
str += text;
return (noReset)
? str
: (str + TERM_ESC + TERM_RESET);
}
_printImage(pixelsArr, output)
{
let total = '';
let prevColor = this.color;
let prevBackground = this._background;
for(let row of pixelsArr) {
let paintedLine = '';
let block = ' ';
for(let i = 0; i < row.length; i++) {
let pixel = row[i];
let nextPixel = (i < row.length - 1) ? row[i + 1] : null;
if(nextPixel && pixel.every((value, index) =>
value === nextPixel[index]
)) {
block += ' ';
continue;
}
/* Do not use predefined functions here (it would impact performance) */
let isTransparent = (pixel.length >= 3) ? _getIsTransparent(pixel[3]) : false;
this.color = (isTransparent)
? Color.DEFAULT
: `38;2;${pixel[0]};${pixel[1]};${pixel[2]}`;
this._background = (isTransparent)
? Color.DEFAULT
: `48;2;${pixel[0]};${pixel[1]};${pixel[2]}`;
paintedLine += `${TERM_ESC}0;${this.color};${this._background}m${block}`;
block = ' ';
}
paintedLine += TERM_ESC + TERM_RESET;
switch(output) {
case 'stderr':
printerr(paintedLine);
break;
case 'return':
total += paintedLine + '\n';
break;
default:
print(paintedLine);
break;
}
}
this.color = prevColor;
this._background = prevBackground;
return total;
}
}

View File

@@ -1,42 +0,0 @@
/* GStreamer
* Copyright (C) 2018 GStreamer developers
* 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_PRELUDE_H__
#define __GST_CLAPPER_PRELUDE_H__
#include <gst/gst.h>
#ifndef GST_CLAPPER_API
# ifdef BUILDING_GST_CLAPPER
# define GST_CLAPPER_API GST_API_EXPORT /* from config.h */
# else
# define GST_CLAPPER_API GST_API_IMPORT
# endif
#endif
#ifndef GST_DISABLE_DEPRECATED
#define GST_CLAPPER_DEPRECATED GST_CLAPPER_API
#define GST_CLAPPER_DEPRECATED_FOR(f) GST_CLAPPER_API
#else
#define GST_CLAPPER_DEPRECATED G_DEPRECATED GST_CLAPPER_API
#define GST_CLAPPER_DEPRECATED_FOR(f) G_DEPRECATED_FOR(f) GST_CLAPPER_API
#endif
#endif /* __GST_CLAPPER_PRELUDE_H__ */

View File

@@ -1,34 +0,0 @@
/* GStreamer
*
* Copyright (C) 2014 Sebastian Dröge <sebastian@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 __CLAPPER_H__
#define __CLAPPER_H__
#include <gst/clapper/clapper-prelude.h>
#include <gst/clapper/gstclapper.h>
#include <gst/clapper/gstclapper-media-info.h>
#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

@@ -1,214 +0,0 @@
/* GStreamer
*
* Copyright (C) 2014-2015 Sebastian Dröge <sebastian@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.
*/
/**
* SECTION:gstclapper-gmaincontextsignaldispatcher
* @title: GstClapperGMainContextSignalDispatcher
* @short_description: Clapper GLib MainContext dispatcher
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstclapper-g-main-context-signal-dispatcher.h"
struct _GstClapperGMainContextSignalDispatcher
{
GObject parent;
GMainContext *application_context;
};
struct _GstClapperGMainContextSignalDispatcherClass
{
GObjectClass parent_class;
};
static void
gst_clapper_g_main_context_signal_dispatcher_interface_init
(GstClapperSignalDispatcherInterface * iface);
enum
{
G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_0,
G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_APPLICATION_CONTEXT,
G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_LAST
};
G_DEFINE_TYPE_WITH_CODE (GstClapperGMainContextSignalDispatcher,
gst_clapper_g_main_context_signal_dispatcher, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GST_TYPE_CLAPPER_SIGNAL_DISPATCHER,
gst_clapper_g_main_context_signal_dispatcher_interface_init));
static GParamSpec
* g_main_context_signal_dispatcher_param_specs
[G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_LAST] = { NULL, };
static void
gst_clapper_g_main_context_signal_dispatcher_finalize (GObject * object)
{
GstClapperGMainContextSignalDispatcher *self =
GST_CLAPPER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER (object);
if (self->application_context)
g_main_context_unref (self->application_context);
G_OBJECT_CLASS
(gst_clapper_g_main_context_signal_dispatcher_parent_class)->finalize
(object);
}
static void
gst_clapper_g_main_context_signal_dispatcher_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)
{
GstClapperGMainContextSignalDispatcher *self =
GST_CLAPPER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER (object);
switch (prop_id) {
case G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_APPLICATION_CONTEXT:
self->application_context = g_value_dup_boxed (value);
if (!self->application_context)
self->application_context = g_main_context_ref_thread_default ();
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_clapper_g_main_context_signal_dispatcher_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec)
{
GstClapperGMainContextSignalDispatcher *self =
GST_CLAPPER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER (object);
switch (prop_id) {
case G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_APPLICATION_CONTEXT:
g_value_set_boxed (value, self->application_context);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_clapper_g_main_context_signal_dispatcher_class_init
(GstClapperGMainContextSignalDispatcherClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize =
gst_clapper_g_main_context_signal_dispatcher_finalize;
gobject_class->set_property =
gst_clapper_g_main_context_signal_dispatcher_set_property;
gobject_class->get_property =
gst_clapper_g_main_context_signal_dispatcher_get_property;
g_main_context_signal_dispatcher_param_specs
[G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_APPLICATION_CONTEXT] =
g_param_spec_boxed ("application-context", "Application Context",
"Application GMainContext to dispatch signals to", G_TYPE_MAIN_CONTEXT,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class,
G_MAIN_CONTEXT_SIGNAL_DISPATCHER_PROP_LAST,
g_main_context_signal_dispatcher_param_specs);
}
static void
gst_clapper_g_main_context_signal_dispatcher_init
(G_GNUC_UNUSED GstClapperGMainContextSignalDispatcher * self)
{
}
typedef struct
{
void (*emitter) (gpointer data);
gpointer data;
GDestroyNotify destroy;
} GMainContextSignalDispatcherData;
static gboolean
g_main_context_signal_dispatcher_dispatch_gsourcefunc (gpointer user_data)
{
GMainContextSignalDispatcherData *data = user_data;
data->emitter (data->data);
return G_SOURCE_REMOVE;
}
static void
g_main_context_signal_dispatcher_dispatch_destroy (gpointer user_data)
{
GMainContextSignalDispatcherData *data = user_data;
if (data->destroy)
data->destroy (data->data);
g_free (data);
}
static void
gst_clapper_g_main_context_signal_dispatcher_dispatch (GstClapperSignalDispatcher
* iface, G_GNUC_UNUSED GstClapper * clapper, void (*emitter) (gpointer data),
gpointer data, GDestroyNotify destroy)
{
GstClapperGMainContextSignalDispatcher *self =
GST_CLAPPER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER (iface);
GMainContextSignalDispatcherData *gsourcefunc_data =
g_new (GMainContextSignalDispatcherData, 1);
gsourcefunc_data->emitter = emitter;
gsourcefunc_data->data = data;
gsourcefunc_data->destroy = destroy;
g_main_context_invoke_full (self->application_context,
G_PRIORITY_DEFAULT, g_main_context_signal_dispatcher_dispatch_gsourcefunc,
gsourcefunc_data, g_main_context_signal_dispatcher_dispatch_destroy);
}
static void
gst_clapper_g_main_context_signal_dispatcher_interface_init
(GstClapperSignalDispatcherInterface * iface)
{
iface->dispatch = gst_clapper_g_main_context_signal_dispatcher_dispatch;
}
/**
* gst_clapper_g_main_context_signal_dispatcher_new:
* @application_context: (allow-none): GMainContext to use or %NULL
*
* Creates a new GstClapperSignalDispatcher that uses @application_context,
* or the thread default one if %NULL is used. See gst_clapper_new().
*
* Returns: (transfer full): the new GstClapperSignalDispatcher
*/
GstClapperSignalDispatcher *
gst_clapper_g_main_context_signal_dispatcher_new (GMainContext *
application_context)
{
return g_object_new (GST_TYPE_CLAPPER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER,
"application-context", application_context, NULL);
}

View File

@@ -1,51 +0,0 @@
/* GStreamer
*
* Copyright (C) 2014-2015 Sebastian Dröge <sebastian@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_G_MAIN_CONTEXT_SIGNAL_DISPATCHER_H__
#define __GST_CLAPPER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER_H__
#include <gst/clapper/gstclapper-types.h>
#include <gst/clapper/gstclapper-signal-dispatcher.h>
G_BEGIN_DECLS
typedef struct _GstClapperGMainContextSignalDispatcher
GstClapperGMainContextSignalDispatcher;
typedef struct _GstClapperGMainContextSignalDispatcherClass
GstClapperGMainContextSignalDispatcherClass;
#define GST_TYPE_CLAPPER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER (gst_clapper_g_main_context_signal_dispatcher_get_type ())
#define GST_IS_CLAPPER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CLAPPER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER))
#define GST_IS_CLAPPER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_CLAPPER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER))
#define GST_CLAPPER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CLAPPER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER, GstClapperGMainContextSignalDispatcherClass))
#define GST_CLAPPER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_CLAPPER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER, GstClapperGMainContextSignalDispatcher))
#define GST_CLAPPER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_CLAPPER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER, GstClapperGMainContextSignalDispatcherClass))
#define GST_CLAPPER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER_CAST(obj) ((GstClapperGMainContextSignalDispatcher*)(obj))
GST_CLAPPER_API
GType gst_clapper_g_main_context_signal_dispatcher_get_type (void);
GST_CLAPPER_API
GstClapperSignalDispatcher * gst_clapper_g_main_context_signal_dispatcher_new (GMainContext * application_context);
G_END_DECLS
#endif /* __GST_CLAPPER_G_MAIN_CONTEXT_SIGNAL_DISPATCHER_H__ */

View File

@@ -1,123 +0,0 @@
/* GStreamer
*
* 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.
*/
/**
* SECTION:gstclapper-gtk4plugin
* @title: GstClapperGtk4Plugin
* @short_description: Clapper GTK4 plugin
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstclapper-gtk4-plugin.h"
#include "gtk4/gstclapperglsink.h"
enum
{
PROP_0,
PROP_VIDEO_SINK,
PROP_LAST
};
#define parent_class gst_clapper_gtk4_plugin_parent_class
G_DEFINE_TYPE_WITH_CODE (GstClapperGtk4Plugin, gst_clapper_gtk4_plugin,
G_TYPE_OBJECT, NULL);
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
static void gst_clapper_gtk4_plugin_constructed (GObject * object);
static void gst_clapper_gtk4_plugin_finalize (GObject * object);
static void gst_clapper_gtk4_plugin_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void gst_clapper_gtk4_plugin_init
(G_GNUC_UNUSED GstClapperGtk4Plugin * self)
{
}
static void gst_clapper_gtk4_plugin_class_init
(G_GNUC_UNUSED GstClapperGtk4PluginClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
gobject_class->constructed = gst_clapper_gtk4_plugin_constructed;
gobject_class->get_property = gst_clapper_gtk4_plugin_get_property;
gobject_class->finalize = gst_clapper_gtk4_plugin_finalize;
param_specs[PROP_VIDEO_SINK] =
g_param_spec_object ("video-sink",
"Video Sink", "Video sink to use with video renderer",
GST_TYPE_ELEMENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
}
static void
gst_clapper_gtk4_plugin_constructed (GObject * object)
{
GstClapperGtk4Plugin *self = GST_CLAPPER_GTK4_PLUGIN (object);
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);
}
static void
gst_clapper_gtk4_plugin_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstClapperGtk4Plugin *self = GST_CLAPPER_GTK4_PLUGIN (object);
switch (prop_id) {
case PROP_VIDEO_SINK:
g_value_set_object (value, self->video_sink);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_clapper_gtk4_plugin_finalize (GObject * object)
{
GstClapperGtk4Plugin *self = GST_CLAPPER_GTK4_PLUGIN (object);
gst_object_unref (self->video_sink);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
/**
* gst_clapper_gtk4_plugin_new:
*
* Creates a new GTK4 plugin.
*
* Returns: (transfer full): the new GstClapperGtk4Plugin
*/
GstClapperGtk4Plugin *
gst_clapper_gtk4_plugin_new (void)
{
return g_object_new (GST_TYPE_CLAPPER_GTK4_PLUGIN, NULL);
}

View File

@@ -1,72 +0,0 @@
/* GStreamer
*
* 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_GTK4_PLUGIN_H__
#define __GST_CLAPPER_GTK4_PLUGIN_H__
#include <gst/gst.h>
#include <gst/clapper/clapper-prelude.h>
G_BEGIN_DECLS
#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))
#define GST_CLAPPER_GTK4_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CLAPPER_GTK4_PLUGIN, GstClapperGtk4PluginClass))
#define GST_CLAPPER_GTK4_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_CLAPPER_GTK4_PLUGIN, GstClapperGtk4Plugin))
#define GST_CLAPPER_GTK4_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_CLAPPER_GTK4_PLUGIN, GstClapperGtk4PluginClass))
#define GST_CLAPPER_GTK4_PLUGIN_CAST(obj) ((GstClapperGtk4Plugin*)(obj))
typedef struct _GstClapperGtk4Plugin GstClapperGtk4Plugin;
typedef struct _GstClapperGtk4PluginClass GstClapperGtk4PluginClass;
/**
* GstClapperGtk4Plugin:
*
* Opaque #GstClapperGtk4Plugin object
*/
struct _GstClapperGtk4Plugin
{
/* <private> */
GObject parent;
GstElement *video_sink;
};
/**
* GstClapperGtk4PluginClass:
*
* The #GstClapperGtk4PluginClass struct only contains private data
*/
struct _GstClapperGtk4PluginClass
{
/* <private> */
GstElementClass parent_class;
};
GST_CLAPPER_API
GType gst_clapper_gtk4_plugin_get_type (void);
GST_CLAPPER_API
GstClapperGtk4Plugin * gst_clapper_gtk4_plugin_new (void);
G_END_DECLS
#endif /* __GST_CLAPPER_GTK4_PLUGIN__ */

View File

@@ -1,125 +0,0 @@
/* GStreamer
*
* Copyright (C) 2015 Brijesh Singh <brijesh.ksingh@gmail.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.
*/
#include "gstclapper-media-info.h"
#ifndef __GST_CLAPPER_MEDIA_INFO_PRIVATE_H__
#define __GST_CLAPPER_MEDIA_INFO_PRIVATE_H__
struct _GstClapperStreamInfo
{
GObject parent;
gchar *codec;
GstCaps *caps;
gint stream_index;
GstTagList *tags;
gchar *stream_id;
};
struct _GstClapperStreamInfoClass
{
GObjectClass parent_class;
};
struct _GstClapperSubtitleInfo
{
GstClapperStreamInfo parent;
gchar *title;
gchar *language;
};
struct _GstClapperSubtitleInfoClass
{
GstClapperStreamInfoClass parent_class;
};
struct _GstClapperAudioInfo
{
GstClapperStreamInfo parent;
gint channels;
gint sample_rate;
guint bitrate;
guint max_bitrate;
gchar *language;
};
struct _GstClapperAudioInfoClass
{
GstClapperStreamInfoClass parent_class;
};
struct _GstClapperVideoInfo
{
GstClapperStreamInfo parent;
gint width;
gint height;
gint framerate_num;
gint framerate_denom;
gint par_num;
gint par_denom;
guint bitrate;
guint max_bitrate;
};
struct _GstClapperVideoInfoClass
{
GstClapperStreamInfoClass parent_class;
};
struct _GstClapperMediaInfo
{
GObject parent;
gchar *uri;
gchar *title;
gchar *container;
gboolean seekable, is_live;
GstTagList *tags;
GstToc *toc;
GstSample *image_sample;
GList *stream_list;
GList *audio_stream_list;
GList *video_stream_list;
GList *subtitle_stream_list;
GstClockTime duration;
};
struct _GstClapperMediaInfoClass
{
GObjectClass parent_class;
};
G_GNUC_INTERNAL GstClapperMediaInfo * gst_clapper_media_info_new (const gchar *uri);
G_GNUC_INTERNAL GstClapperMediaInfo * gst_clapper_media_info_copy (GstClapperMediaInfo *ref);
G_GNUC_INTERNAL GstClapperStreamInfo * gst_clapper_stream_info_new (gint stream_index, GType type);
G_GNUC_INTERNAL GstClapperStreamInfo * gst_clapper_stream_info_copy (GstClapperStreamInfo *ref);
#endif /* __GST_CLAPPER_MEDIA_INFO_PRIVATE_H__ */

View File

@@ -1,885 +0,0 @@
/* GStreamer
*
* Copyright (C) 2015 Brijesh Singh <brijesh.ksingh@gmail.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.
*/
/**
* SECTION:gstclapper-mediainfo
* @title: GstClapperMediaInfo
* @short_description: Clapper Media Information
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstclapper-media-info.h"
#include "gstclapper-media-info-private.h"
/* Per-stream information */
G_DEFINE_ABSTRACT_TYPE (GstClapperStreamInfo, gst_clapper_stream_info,
G_TYPE_OBJECT);
static void
gst_clapper_stream_info_init (GstClapperStreamInfo * sinfo)
{
sinfo->stream_index = -1;
}
static void
gst_clapper_stream_info_finalize (GObject * object)
{
GstClapperStreamInfo *sinfo = GST_CLAPPER_STREAM_INFO (object);
g_free (sinfo->codec);
g_free (sinfo->stream_id);
if (sinfo->caps)
gst_caps_unref (sinfo->caps);
if (sinfo->tags)
gst_tag_list_unref (sinfo->tags);
G_OBJECT_CLASS (gst_clapper_stream_info_parent_class)->finalize (object);
}
static void
gst_clapper_stream_info_class_init (GstClapperStreamInfoClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
gobject_class->finalize = gst_clapper_stream_info_finalize;
}
/**
* gst_clapper_stream_info_get_index:
* @info: a #GstClapperStreamInfo
*
* Function to get stream index from #GstClapperStreamInfo instance.
*
* Returns: the stream index of this stream.
*/
gint
gst_clapper_stream_info_get_index (const GstClapperStreamInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_STREAM_INFO (info), -1);
return info->stream_index;
}
/**
* gst_clapper_stream_info_get_stream_type:
* @info: a #GstClapperStreamInfo
*
* Function to return human readable name for the stream type
* of the given @info (ex: "audio", "video", "subtitle")
*
* Returns: a human readable name
*/
const gchar *
gst_clapper_stream_info_get_stream_type (const GstClapperStreamInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_STREAM_INFO (info), NULL);
if (GST_IS_CLAPPER_VIDEO_INFO (info))
return "video";
else if (GST_IS_CLAPPER_AUDIO_INFO (info))
return "audio";
else
return "subtitle";
}
/**
* gst_clapper_stream_info_get_tags:
* @info: a #GstClapperStreamInfo
*
* Returns: (transfer none): the tags contained in this stream.
*/
GstTagList *
gst_clapper_stream_info_get_tags (const GstClapperStreamInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_STREAM_INFO (info), NULL);
return info->tags;
}
/**
* gst_clapper_stream_info_get_codec:
* @info: a #GstClapperStreamInfo
*
* A string describing codec used in #GstClapperStreamInfo.
*
* Returns: codec string or NULL on unknown.
*/
const gchar *
gst_clapper_stream_info_get_codec (const GstClapperStreamInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_STREAM_INFO (info), NULL);
return info->codec;
}
/**
* gst_clapper_stream_info_get_caps:
* @info: a #GstClapperStreamInfo
*
* Returns: (transfer none): the #GstCaps of the stream.
*/
GstCaps *
gst_clapper_stream_info_get_caps (const GstClapperStreamInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_STREAM_INFO (info), NULL);
return info->caps;
}
/* Video information */
G_DEFINE_TYPE (GstClapperVideoInfo, gst_clapper_video_info,
GST_TYPE_CLAPPER_STREAM_INFO);
static void
gst_clapper_video_info_init (GstClapperVideoInfo * info)
{
info->width = -1;
info->height = -1;
info->framerate_num = 0;
info->framerate_denom = 1;
info->par_num = 1;
info->par_denom = 1;
}
static void
gst_clapper_video_info_class_init (G_GNUC_UNUSED GstClapperVideoInfoClass * klass)
{
/* nothing to do here */
}
/**
* gst_clapper_video_info_get_width:
* @info: a #GstClapperVideoInfo
*
* Returns: the width of video in #GstClapperVideoInfo.
*/
gint
gst_clapper_video_info_get_width (const GstClapperVideoInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_VIDEO_INFO (info), -1);
return info->width;
}
/**
* gst_clapper_video_info_get_height:
* @info: a #GstClapperVideoInfo
*
* Returns: the height of video in #GstClapperVideoInfo.
*/
gint
gst_clapper_video_info_get_height (const GstClapperVideoInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_VIDEO_INFO (info), -1);
return info->height;
}
/**
* gst_clapper_video_info_get_framerate:
* @info: a #GstClapperVideoInfo
* @fps_n: (out): Numerator of frame rate
* @fps_d: (out): Denominator of frame rate
*
*/
void
gst_clapper_video_info_get_framerate (const GstClapperVideoInfo * info,
gint * fps_n, gint * fps_d)
{
g_return_if_fail (GST_IS_CLAPPER_VIDEO_INFO (info));
*fps_n = info->framerate_num;
*fps_d = info->framerate_denom;
}
/**
* gst_clapper_video_info_get_pixel_aspect_ratio:
* @info: a #GstClapperVideoInfo
* @par_n: (out): numerator
* @par_d: (out): denominator
*
* Returns the pixel aspect ratio in @par_n and @par_d
*
*/
void
gst_clapper_video_info_get_pixel_aspect_ratio (const GstClapperVideoInfo * info,
guint * par_n, guint * par_d)
{
g_return_if_fail (GST_IS_CLAPPER_VIDEO_INFO (info));
*par_n = info->par_num;
*par_d = info->par_denom;
}
/**
* gst_clapper_video_info_get_bitrate:
* @info: a #GstClapperVideoInfo
*
* Returns: the current bitrate of video in #GstClapperVideoInfo.
*/
gint
gst_clapper_video_info_get_bitrate (const GstClapperVideoInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_VIDEO_INFO (info), -1);
return info->bitrate;
}
/**
* gst_clapper_video_info_get_max_bitrate:
* @info: a #GstClapperVideoInfo
*
* Returns: the maximum bitrate of video in #GstClapperVideoInfo.
*/
gint
gst_clapper_video_info_get_max_bitrate (const GstClapperVideoInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_VIDEO_INFO (info), -1);
return info->max_bitrate;
}
/* Audio information */
G_DEFINE_TYPE (GstClapperAudioInfo, gst_clapper_audio_info,
GST_TYPE_CLAPPER_STREAM_INFO);
static void
gst_clapper_audio_info_init (GstClapperAudioInfo * info)
{
info->channels = 0;
info->sample_rate = 0;
info->bitrate = -1;
info->max_bitrate = -1;
}
static void
gst_clapper_audio_info_finalize (GObject * object)
{
GstClapperAudioInfo *info = GST_CLAPPER_AUDIO_INFO (object);
g_free (info->language);
G_OBJECT_CLASS (gst_clapper_audio_info_parent_class)->finalize (object);
}
static void
gst_clapper_audio_info_class_init (GstClapperAudioInfoClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
gobject_class->finalize = gst_clapper_audio_info_finalize;
}
/**
* gst_clapper_audio_info_get_language:
* @info: a #GstClapperAudioInfo
*
* Returns: the language of the stream, or NULL if unknown.
*/
const gchar *
gst_clapper_audio_info_get_language (const GstClapperAudioInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_AUDIO_INFO (info), NULL);
return info->language;
}
/**
* gst_clapper_audio_info_get_channels:
* @info: a #GstClapperAudioInfo
*
* Returns: the number of audio channels in #GstClapperAudioInfo.
*/
gint
gst_clapper_audio_info_get_channels (const GstClapperAudioInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_AUDIO_INFO (info), 0);
return info->channels;
}
/**
* gst_clapper_audio_info_get_sample_rate:
* @info: a #GstClapperAudioInfo
*
* Returns: the audio sample rate in #GstClapperAudioInfo.
*/
gint
gst_clapper_audio_info_get_sample_rate (const GstClapperAudioInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_AUDIO_INFO (info), 0);
return info->sample_rate;
}
/**
* gst_clapper_audio_info_get_bitrate:
* @info: a #GstClapperAudioInfo
*
* Returns: the audio bitrate in #GstClapperAudioInfo.
*/
gint
gst_clapper_audio_info_get_bitrate (const GstClapperAudioInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_AUDIO_INFO (info), -1);
return info->bitrate;
}
/**
* gst_clapper_audio_info_get_max_bitrate:
* @info: a #GstClapperAudioInfo
*
* Returns: the audio maximum bitrate in #GstClapperAudioInfo.
*/
gint
gst_clapper_audio_info_get_max_bitrate (const GstClapperAudioInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_AUDIO_INFO (info), -1);
return info->max_bitrate;
}
/* Subtitle information */
G_DEFINE_TYPE (GstClapperSubtitleInfo, gst_clapper_subtitle_info,
GST_TYPE_CLAPPER_STREAM_INFO);
static void
gst_clapper_subtitle_info_init (G_GNUC_UNUSED GstClapperSubtitleInfo * info)
{
/* nothing to do */
}
static void
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);
}
static void
gst_clapper_subtitle_info_class_init (GstClapperSubtitleInfoClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) 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
*
* Returns: the language of the stream, or NULL if unknown.
*/
const gchar *
gst_clapper_subtitle_info_get_language (const GstClapperSubtitleInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_SUBTITLE_INFO (info), NULL);
return info->language;
}
/* Global media information */
G_DEFINE_TYPE (GstClapperMediaInfo, gst_clapper_media_info, G_TYPE_OBJECT);
static void
gst_clapper_media_info_init (GstClapperMediaInfo * info)
{
info->duration = -1;
info->is_live = FALSE;
info->seekable = FALSE;
}
static void
gst_clapper_media_info_finalize (GObject * object)
{
GstClapperMediaInfo *info = GST_CLAPPER_MEDIA_INFO (object);
g_free (info->uri);
g_free (info->title);
g_free (info->container);
if (info->tags)
gst_tag_list_unref (info->tags);
if (info->toc)
gst_toc_unref (info->toc);
if (info->image_sample)
gst_sample_unref (info->image_sample);
if (info->audio_stream_list)
g_list_free (info->audio_stream_list);
if (info->video_stream_list)
g_list_free (info->video_stream_list);
if (info->subtitle_stream_list)
g_list_free (info->subtitle_stream_list);
if (info->stream_list)
g_list_free_full (info->stream_list, g_object_unref);
G_OBJECT_CLASS (gst_clapper_media_info_parent_class)->finalize (object);
}
static void
gst_clapper_media_info_class_init (GstClapperMediaInfoClass * klass)
{
GObjectClass *oclass = (GObjectClass *) klass;
oclass->finalize = gst_clapper_media_info_finalize;
}
static GstClapperVideoInfo *
gst_clapper_video_info_new (void)
{
return g_object_new (GST_TYPE_CLAPPER_VIDEO_INFO, NULL);
}
static GstClapperAudioInfo *
gst_clapper_audio_info_new (void)
{
return g_object_new (GST_TYPE_CLAPPER_AUDIO_INFO, NULL);
}
static GstClapperSubtitleInfo *
gst_clapper_subtitle_info_new (void)
{
return g_object_new (GST_TYPE_CLAPPER_SUBTITLE_INFO, NULL);
}
static GstClapperStreamInfo *
gst_clapper_video_info_copy (GstClapperVideoInfo * ref)
{
GstClapperVideoInfo *ret;
ret = gst_clapper_video_info_new ();
ret->width = ref->width;
ret->height = ref->height;
ret->framerate_num = ref->framerate_num;
ret->framerate_denom = ref->framerate_denom;
ret->par_num = ref->par_num;
ret->par_denom = ref->par_denom;
ret->bitrate = ref->bitrate;
ret->max_bitrate = ref->max_bitrate;
return (GstClapperStreamInfo *) ret;
}
static GstClapperStreamInfo *
gst_clapper_audio_info_copy (GstClapperAudioInfo * ref)
{
GstClapperAudioInfo *ret;
ret = gst_clapper_audio_info_new ();
ret->sample_rate = ref->sample_rate;
ret->channels = ref->channels;
ret->bitrate = ref->bitrate;
ret->max_bitrate = ref->max_bitrate;
if (ref->language)
ret->language = g_strdup (ref->language);
return (GstClapperStreamInfo *) ret;
}
static GstClapperStreamInfo *
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);
return (GstClapperStreamInfo *) ret;
}
GstClapperStreamInfo *
gst_clapper_stream_info_copy (GstClapperStreamInfo * ref)
{
GstClapperStreamInfo *info = NULL;
if (!ref)
return NULL;
if (GST_IS_CLAPPER_VIDEO_INFO (ref))
info = gst_clapper_video_info_copy ((GstClapperVideoInfo *) ref);
else if (GST_IS_CLAPPER_AUDIO_INFO (ref))
info = gst_clapper_audio_info_copy ((GstClapperAudioInfo *) ref);
else
info = gst_clapper_subtitle_info_copy ((GstClapperSubtitleInfo *) ref);
info->stream_index = ref->stream_index;
if (ref->tags)
info->tags = gst_tag_list_ref (ref->tags);
if (ref->caps)
info->caps = gst_caps_copy (ref->caps);
if (ref->codec)
info->codec = g_strdup (ref->codec);
if (ref->stream_id)
info->stream_id = g_strdup (ref->stream_id);
return info;
}
GstClapperMediaInfo *
gst_clapper_media_info_copy (GstClapperMediaInfo * ref)
{
GList *l;
GstClapperMediaInfo *info;
if (!ref)
return NULL;
info = gst_clapper_media_info_new (ref->uri);
info->duration = ref->duration;
info->seekable = ref->seekable;
info->is_live = ref->is_live;
if (ref->tags)
info->tags = gst_tag_list_ref (ref->tags);
if (ref->toc)
info->toc = gst_toc_ref (ref->toc);
if (ref->title)
info->title = g_strdup (ref->title);
if (ref->container)
info->container = g_strdup (ref->container);
if (ref->image_sample)
info->image_sample = gst_sample_ref (ref->image_sample);
for (l = ref->stream_list; l != NULL; l = l->next) {
GstClapperStreamInfo *s;
s = gst_clapper_stream_info_copy ((GstClapperStreamInfo *) l->data);
info->stream_list = g_list_append (info->stream_list, s);
if (GST_IS_CLAPPER_AUDIO_INFO (s))
info->audio_stream_list = g_list_append (info->audio_stream_list, s);
else if (GST_IS_CLAPPER_VIDEO_INFO (s))
info->video_stream_list = g_list_append (info->video_stream_list, s);
else
info->subtitle_stream_list =
g_list_append (info->subtitle_stream_list, s);
}
return info;
}
GstClapperStreamInfo *
gst_clapper_stream_info_new (gint stream_index, GType type)
{
GstClapperStreamInfo *info = NULL;
if (type == GST_TYPE_CLAPPER_AUDIO_INFO)
info = (GstClapperStreamInfo *) gst_clapper_audio_info_new ();
else if (type == GST_TYPE_CLAPPER_VIDEO_INFO)
info = (GstClapperStreamInfo *) gst_clapper_video_info_new ();
else
info = (GstClapperStreamInfo *) gst_clapper_subtitle_info_new ();
info->stream_index = stream_index;
return info;
}
GstClapperMediaInfo *
gst_clapper_media_info_new (const gchar * uri)
{
GstClapperMediaInfo *info;
g_return_val_if_fail (uri != NULL, NULL);
info = g_object_new (GST_TYPE_CLAPPER_MEDIA_INFO, NULL);
info->uri = g_strdup (uri);
return info;
}
/**
* gst_clapper_media_info_get_uri:
* @info: a #GstClapperMediaInfo
*
* Returns: the URI associated with #GstClapperMediaInfo.
*/
const gchar *
gst_clapper_media_info_get_uri (const GstClapperMediaInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_MEDIA_INFO (info), NULL);
return info->uri;
}
/**
* gst_clapper_media_info_is_seekable:
* @info: a #GstClapperMediaInfo
*
* Returns: %TRUE if the media is seekable.
*/
gboolean
gst_clapper_media_info_is_seekable (const GstClapperMediaInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_MEDIA_INFO (info), FALSE);
return info->seekable;
}
/**
* gst_clapper_media_info_is_live:
* @info: a #GstClapperMediaInfo
*
* Returns: %TRUE if the media is live.
*/
gboolean
gst_clapper_media_info_is_live (const GstClapperMediaInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_MEDIA_INFO (info), FALSE);
return info->is_live;
}
/**
* gst_clapper_media_info_get_stream_list:
* @info: a #GstClapperMediaInfo
*
* Returns: (transfer none) (element-type GstClapperStreamInfo): A #GList of
* matching #GstClapperStreamInfo.
*/
GList *
gst_clapper_media_info_get_stream_list (const GstClapperMediaInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_MEDIA_INFO (info), NULL);
return info->stream_list;
}
/**
* gst_clapper_media_info_get_video_streams:
* @info: a #GstClapperMediaInfo
*
* Returns: (transfer none) (element-type GstClapperVideoInfo): A #GList of
* matching #GstClapperVideoInfo.
*/
GList *
gst_clapper_media_info_get_video_streams (const GstClapperMediaInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_MEDIA_INFO (info), NULL);
return info->video_stream_list;
}
/**
* gst_clapper_media_info_get_subtitle_streams:
* @info: a #GstClapperMediaInfo
*
* Returns: (transfer none) (element-type GstClapperSubtitleInfo): A #GList of
* matching #GstClapperSubtitleInfo.
*/
GList *
gst_clapper_media_info_get_subtitle_streams (const GstClapperMediaInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_MEDIA_INFO (info), NULL);
return info->subtitle_stream_list;
}
/**
* gst_clapper_media_info_get_audio_streams:
* @info: a #GstClapperMediaInfo
*
* Returns: (transfer none) (element-type GstClapperAudioInfo): A #GList of
* matching #GstClapperAudioInfo.
*/
GList *
gst_clapper_media_info_get_audio_streams (const GstClapperMediaInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_MEDIA_INFO (info), NULL);
return info->audio_stream_list;
}
/**
* gst_clapper_media_info_get_duration:
* @info: a #GstClapperMediaInfo
*
* Returns: duration of the media.
*/
GstClockTime
gst_clapper_media_info_get_duration (const GstClapperMediaInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_MEDIA_INFO (info), -1);
return info->duration;
}
/**
* gst_clapper_media_info_get_tags:
* @info: a #GstClapperMediaInfo
*
* Returns: (transfer none): the tags contained in media info.
*/
GstTagList *
gst_clapper_media_info_get_tags (const GstClapperMediaInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_MEDIA_INFO (info), NULL);
return info->tags;
}
/**
* gst_clapper_media_info_get_toc:
* @info: a #GstClapperMediaInfo
*
* Returns: (transfer none): the toc contained in media info.
*/
GstToc *
gst_clapper_media_info_get_toc (const GstClapperMediaInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_MEDIA_INFO (info), NULL);
return info->toc;
}
/**
* gst_clapper_media_info_get_title:
* @info: a #GstClapperMediaInfo
*
* 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)
{
g_return_val_if_fail (GST_IS_CLAPPER_MEDIA_INFO (info), NULL);
return info->title;
}
/**
* gst_clapper_media_info_get_container_format:
* @info: a #GstClapperMediaInfo
*
* Returns: the container format.
*/
const gchar *
gst_clapper_media_info_get_container_format (const GstClapperMediaInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_MEDIA_INFO (info), NULL);
return info->container;
}
/**
* gst_clapper_media_info_get_image_sample:
* @info: a #GstClapperMediaInfo
*
* Function to get the image (or preview-image) stored in taglist.
* Application can use `gst_sample_*_()` API's to get caps, buffer etc.
*
* Returns: (transfer none): GstSample or NULL.
*/
GstSample *
gst_clapper_media_info_get_image_sample (const GstClapperMediaInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_MEDIA_INFO (info), NULL);
return info->image_sample;
}
/**
* gst_clapper_media_info_get_number_of_streams:
* @info: a #GstClapperMediaInfo
*
* Returns: number of total streams.
*/
guint
gst_clapper_media_info_get_number_of_streams (const GstClapperMediaInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_MEDIA_INFO (info), 0);
return g_list_length (info->stream_list);
}
/**
* gst_clapper_media_info_get_number_of_video_streams:
* @info: a #GstClapperMediaInfo
*
* Returns: number of video streams.
*/
guint
gst_clapper_media_info_get_number_of_video_streams (const GstClapperMediaInfo *
info)
{
g_return_val_if_fail (GST_IS_CLAPPER_MEDIA_INFO (info), 0);
return g_list_length (info->video_stream_list);
}
/**
* gst_clapper_media_info_get_number_of_audio_streams:
* @info: a #GstClapperMediaInfo
*
* Returns: number of audio streams.
*/
guint
gst_clapper_media_info_get_number_of_audio_streams (const GstClapperMediaInfo *
info)
{
g_return_val_if_fail (GST_IS_CLAPPER_MEDIA_INFO (info), 0);
return g_list_length (info->audio_stream_list);
}
/**
* gst_clapper_media_info_get_number_of_subtitle_streams:
* @info: a #GstClapperMediaInfo
*
* Returns: number of subtitle streams.
*/
guint gst_clapper_media_info_get_number_of_subtitle_streams
(const GstClapperMediaInfo * info)
{
g_return_val_if_fail (GST_IS_CLAPPER_MEDIA_INFO (info), 0);
return g_list_length (info->subtitle_stream_list);
}

View File

@@ -1,253 +0,0 @@
/* GStreamer
*
* Copyright (C) 2015 Brijesh Singh <brijesh.ksingh@gmail.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_MEDIA_INFO_H__
#define __GST_CLAPPER_MEDIA_INFO_H__
#include <gst/gst.h>
#include <gst/clapper/clapper-prelude.h>
G_BEGIN_DECLS
#define GST_TYPE_CLAPPER_STREAM_INFO \
(gst_clapper_stream_info_get_type ())
#define GST_CLAPPER_STREAM_INFO(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CLAPPER_STREAM_INFO,GstClapperStreamInfo))
#define GST_CLAPPER_STREAM_INFO_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CLAPPER_STREAM_INFO,GstClapperStreamInfo))
#define GST_IS_CLAPPER_STREAM_INFO(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CLAPPER_STREAM_INFO))
#define GST_IS_CLAPPER_STREAM_INFO_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CLAPPER_STREAM_INFO))
/**
* GstClapperStreamInfo:
*
* Base structure for information concerning a media stream. Depending on
* the stream type, one can find more media-specific information in
* #GstClapperVideoInfo, #GstClapperAudioInfo, #GstClapperSubtitleInfo.
*/
typedef struct _GstClapperStreamInfo GstClapperStreamInfo;
typedef struct _GstClapperStreamInfoClass GstClapperStreamInfoClass;
GST_CLAPPER_API
GType gst_clapper_stream_info_get_type (void);
GST_CLAPPER_API
gint gst_clapper_stream_info_get_index (const GstClapperStreamInfo *info);
GST_CLAPPER_API
const gchar* gst_clapper_stream_info_get_stream_type (const GstClapperStreamInfo *info);
GST_CLAPPER_API
GstTagList* gst_clapper_stream_info_get_tags (const GstClapperStreamInfo *info);
GST_CLAPPER_API
GstCaps* gst_clapper_stream_info_get_caps (const GstClapperStreamInfo *info);
GST_CLAPPER_API
const gchar* gst_clapper_stream_info_get_codec (const GstClapperStreamInfo *info);
#define GST_TYPE_CLAPPER_VIDEO_INFO \
(gst_clapper_video_info_get_type ())
#define GST_CLAPPER_VIDEO_INFO(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CLAPPER_VIDEO_INFO, GstClapperVideoInfo))
#define GST_CLAPPER_VIDEO_INFO_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((obj),GST_TYPE_CLAPPER_VIDEO_INFO, GstClapperVideoInfoClass))
#define GST_IS_CLAPPER_VIDEO_INFO(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CLAPPER_VIDEO_INFO))
#define GST_IS_CLAPPER_VIDEO_INFO_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((obj),GST_TYPE_CLAPPER_VIDEO_INFO))
/**
* GstClapperVideoInfo:
*
* #GstClapperStreamInfo specific to video streams.
*/
typedef struct _GstClapperVideoInfo GstClapperVideoInfo;
typedef struct _GstClapperVideoInfoClass GstClapperVideoInfoClass;
GST_CLAPPER_API
GType gst_clapper_video_info_get_type (void);
GST_CLAPPER_API
gint gst_clapper_video_info_get_bitrate (const GstClapperVideoInfo * info);
GST_CLAPPER_API
gint gst_clapper_video_info_get_max_bitrate (const GstClapperVideoInfo * info);
GST_CLAPPER_API
gint gst_clapper_video_info_get_width (const GstClapperVideoInfo * info);
GST_CLAPPER_API
gint gst_clapper_video_info_get_height (const GstClapperVideoInfo * info);
GST_CLAPPER_API
void gst_clapper_video_info_get_framerate (const GstClapperVideoInfo * info,
gint * fps_n,
gint * fps_d);
GST_CLAPPER_API
void gst_clapper_video_info_get_pixel_aspect_ratio (const GstClapperVideoInfo * info,
guint * par_n,
guint * par_d);
#define GST_TYPE_CLAPPER_AUDIO_INFO \
(gst_clapper_audio_info_get_type ())
#define GST_CLAPPER_AUDIO_INFO(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CLAPPER_AUDIO_INFO, GstClapperAudioInfo))
#define GST_CLAPPER_AUDIO_INFO_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CLAPPER_AUDIO_INFO, GstClapperAudioInfoClass))
#define GST_IS_CLAPPER_AUDIO_INFO(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CLAPPER_AUDIO_INFO))
#define GST_IS_CLAPPER_AUDIO_INFO_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CLAPPER_AUDIO_INFO))
/**
* GstClapperAudioInfo:
*
* #GstClapperStreamInfo specific to audio streams.
*/
typedef struct _GstClapperAudioInfo GstClapperAudioInfo;
typedef struct _GstClapperAudioInfoClass GstClapperAudioInfoClass;
GST_CLAPPER_API
GType gst_clapper_audio_info_get_type (void);
GST_CLAPPER_API
gint gst_clapper_audio_info_get_channels (const GstClapperAudioInfo* info);
GST_CLAPPER_API
gint gst_clapper_audio_info_get_sample_rate (const GstClapperAudioInfo* info);
GST_CLAPPER_API
gint gst_clapper_audio_info_get_bitrate (const GstClapperAudioInfo* info);
GST_CLAPPER_API
gint gst_clapper_audio_info_get_max_bitrate (const GstClapperAudioInfo* info);
GST_CLAPPER_API
const gchar* gst_clapper_audio_info_get_language (const GstClapperAudioInfo* info);
#define GST_TYPE_CLAPPER_SUBTITLE_INFO \
(gst_clapper_subtitle_info_get_type ())
#define GST_CLAPPER_SUBTITLE_INFO(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CLAPPER_SUBTITLE_INFO, GstClapperSubtitleInfo))
#define GST_CLAPPER_SUBTITLE_INFO_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CLAPPER_SUBTITLE_INFO,GstClapperSubtitleInfoClass))
#define GST_IS_CLAPPER_SUBTITLE_INFO(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CLAPPER_SUBTITLE_INFO))
#define GST_IS_CLAPPER_SUBTITLE_INFO_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CLAPPER_SUBTITLE_INFO))
/**
* GstClapperSubtitleInfo:
*
* #GstClapperStreamInfo specific to subtitle streams.
*/
typedef struct _GstClapperSubtitleInfo GstClapperSubtitleInfo;
typedef struct _GstClapperSubtitleInfoClass GstClapperSubtitleInfoClass;
GST_CLAPPER_API
GType gst_clapper_subtitle_info_get_type (void);
GST_CLAPPER_API
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())
#define GST_CLAPPER_MEDIA_INFO(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CLAPPER_MEDIA_INFO,GstClapperMediaInfo))
#define GST_CLAPPER_MEDIA_INFO_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CLAPPER_MEDIA_INFO,GstClapperMediaInfoClass))
#define GST_IS_CLAPPER_MEDIA_INFO(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CLAPPER_MEDIA_INFO))
#define GST_IS_CLAPPER_MEDIA_INFO_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CLAPPER_MEDIA_INFO))
/**
* GstClapperMediaInfo:
*
* Structure containing the media information of a URI.
*/
typedef struct _GstClapperMediaInfo GstClapperMediaInfo;
typedef struct _GstClapperMediaInfoClass GstClapperMediaInfoClass;
GST_CLAPPER_API
GType gst_clapper_media_info_get_type (void);
GST_CLAPPER_API
const gchar * gst_clapper_media_info_get_uri (const GstClapperMediaInfo *info);
GST_CLAPPER_API
gboolean gst_clapper_media_info_is_seekable (const GstClapperMediaInfo *info);
GST_CLAPPER_API
gboolean gst_clapper_media_info_is_live (const GstClapperMediaInfo *info);
GST_CLAPPER_API
GstClockTime gst_clapper_media_info_get_duration (const GstClapperMediaInfo *info);
GST_CLAPPER_API
GList * gst_clapper_media_info_get_stream_list (const GstClapperMediaInfo *info);
GST_CLAPPER_API
guint gst_clapper_media_info_get_number_of_streams (const GstClapperMediaInfo *info);
GST_CLAPPER_API
GList * gst_clapper_media_info_get_video_streams (const GstClapperMediaInfo *info);
GST_CLAPPER_API
guint gst_clapper_media_info_get_number_of_video_streams (const GstClapperMediaInfo *info);
GST_CLAPPER_API
GList * gst_clapper_media_info_get_audio_streams (const GstClapperMediaInfo *info);
GST_CLAPPER_API
guint gst_clapper_media_info_get_number_of_audio_streams (const GstClapperMediaInfo *info);
GST_CLAPPER_API
GList * gst_clapper_media_info_get_subtitle_streams (const GstClapperMediaInfo *info);
GST_CLAPPER_API
guint gst_clapper_media_info_get_number_of_subtitle_streams (const GstClapperMediaInfo *info);
GST_CLAPPER_API
GstTagList * gst_clapper_media_info_get_tags (const GstClapperMediaInfo *info);
GST_CLAPPER_API
GstToc * gst_clapper_media_info_get_toc (const GstClapperMediaInfo *info);
GST_CLAPPER_API
const gchar * gst_clapper_media_info_get_title (const GstClapperMediaInfo *info);
GST_CLAPPER_API
const gchar * gst_clapper_media_info_get_container_format (const GstClapperMediaInfo *info);
GST_CLAPPER_API
GstSample * gst_clapper_media_info_get_image_sample (const GstClapperMediaInfo *info);
G_END_DECLS
#endif /* __GST_CLAPPER_MEDIA_INFO_H */

View File

@@ -1,43 +0,0 @@
/*
* Copyright (C) 2021 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_CLAPPER_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

@@ -1,793 +0,0 @@
/*
* Copyright (C) 2021 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstclapper-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

@@ -1,54 +0,0 @@
/*
* Copyright (C) 2021 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_CLAPPER_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__ */

View File

@@ -1,35 +0,0 @@
/* GStreamer
*
* Copyright (C) 2014-2015 Sebastian Dröge <sebastian@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_SIGNAL_DISPATCHER_PRIVATE_H__
#define __GST_CLAPPER_SIGNAL_DISPATCHER_PRIVATE_H__
#include <gst/clapper/gstclapper-signal-dispatcher.h>
G_BEGIN_DECLS
G_GNUC_INTERNAL void gst_clapper_signal_dispatcher_dispatch (GstClapperSignalDispatcher * self,
GstClapper * clapper, GstClapperSignalDispatcherFunc emitter, gpointer data,
GDestroyNotify destroy);
G_END_DECLS
#endif /* __GST_CLAPPER_SIGNAL_DISPATCHER_PRIVATE_H__ */

View File

@@ -1,58 +0,0 @@
/* GStreamer
*
* Copyright (C) 2014-2015 Sebastian Dröge <sebastian@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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstclapper-signal-dispatcher.h"
#include "gstclapper-signal-dispatcher-private.h"
G_DEFINE_INTERFACE (GstClapperSignalDispatcher, gst_clapper_signal_dispatcher,
G_TYPE_OBJECT);
static void
gst_clapper_signal_dispatcher_default_init (G_GNUC_UNUSED
GstClapperSignalDispatcherInterface * iface)
{
}
void
gst_clapper_signal_dispatcher_dispatch (GstClapperSignalDispatcher * self,
GstClapper * clapper, GstClapperSignalDispatcherFunc emitter, gpointer data,
GDestroyNotify destroy)
{
GstClapperSignalDispatcherInterface *iface;
if (!self) {
emitter (data);
if (destroy)
destroy (data);
return;
}
g_return_if_fail (GST_IS_CLAPPER_SIGNAL_DISPATCHER (self));
iface = GST_CLAPPER_SIGNAL_DISPATCHER_GET_INTERFACE (self);
g_return_if_fail (iface->dispatch != NULL);
iface->dispatch (self, clapper, emitter, data, destroy);
}

View File

@@ -1,53 +0,0 @@
/* GStreamer
*
* Copyright (C) 2014-2015 Sebastian Dröge <sebastian@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_SIGNAL_DISPATCHER_H__
#define __GST_CLAPPER_SIGNAL_DISPATCHER_H__
#include <gst/gst.h>
#include <gst/clapper/gstclapper-types.h>
G_BEGIN_DECLS
typedef struct _GstClapperSignalDispatcher GstClapperSignalDispatcher;
typedef struct _GstClapperSignalDispatcherInterface GstClapperSignalDispatcherInterface;
#define GST_TYPE_CLAPPER_SIGNAL_DISPATCHER (gst_clapper_signal_dispatcher_get_type ())
#define GST_CLAPPER_SIGNAL_DISPATCHER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_CLAPPER_SIGNAL_DISPATCHER, GstClapperSignalDispatcher))
#define GST_IS_CLAPPER_SIGNAL_DISPATCHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CLAPPER_SIGNAL_DISPATCHER))
#define GST_CLAPPER_SIGNAL_DISPATCHER_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GST_TYPE_CLAPPER_SIGNAL_DISPATCHER, GstClapperSignalDispatcherInterface))
typedef void (*GstClapperSignalDispatcherFunc) (gpointer data);
struct _GstClapperSignalDispatcherInterface {
GTypeInterface parent_iface;
void (*dispatch) (GstClapperSignalDispatcher * self, GstClapper * clapper,
GstClapperSignalDispatcherFunc emitter, gpointer data,
GDestroyNotify destroy);
};
GST_CLAPPER_API
GType gst_clapper_signal_dispatcher_get_type (void);
G_END_DECLS
#endif /* __GST_CLAPPER_SIGNAL_DISPATCHER_H__ */

View File

@@ -1,37 +0,0 @@
/* GStreamer
*
* Copyright (C) 2015 Sebastian Dröge <sebastian@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_TYPES_H__
#define __GST_CLAPPER_TYPES_H__
#include <gst/gst.h>
#include <gst/clapper/clapper-prelude.h>
G_BEGIN_DECLS
typedef struct _GstClapper GstClapper;
typedef struct _GstClapperClass GstClapperClass;
G_END_DECLS
#endif /* __GST_CLAPPER_TYPES_H__ */

View File

@@ -1,345 +0,0 @@
/* GStreamer
*
* Copyright (C) 2014-2015 Sebastian Dröge <sebastian@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.
*/
/**
* SECTION:gstclapper-videooverlayvideorenderer
* @title: GstClapperVideoOverlayVideoRenderer
* @short_description: Clapper Video Overlay Video Renderer
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstclapper-video-overlay-video-renderer.h"
#include "gstclapper.h"
#include <gst/video/video.h>
struct _GstClapperVideoOverlayVideoRenderer
{
GObject parent;
GstVideoOverlay *video_overlay;
gpointer window_handle;
gint x, y, width, height;
GstElement *video_sink; /* configured video sink, or NULL */
};
struct _GstClapperVideoOverlayVideoRendererClass
{
GObjectClass parent_class;
};
static void
gst_clapper_video_overlay_video_renderer_interface_init
(GstClapperVideoRendererInterface * iface);
enum
{
VIDEO_OVERLAY_VIDEO_RENDERER_PROP_0,
VIDEO_OVERLAY_VIDEO_RENDERER_PROP_WINDOW_HANDLE,
VIDEO_OVERLAY_VIDEO_RENDERER_PROP_VIDEO_SINK,
VIDEO_OVERLAY_VIDEO_RENDERER_PROP_LAST
};
G_DEFINE_TYPE_WITH_CODE (GstClapperVideoOverlayVideoRenderer,
gst_clapper_video_overlay_video_renderer, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GST_TYPE_CLAPPER_VIDEO_RENDERER,
gst_clapper_video_overlay_video_renderer_interface_init));
static GParamSpec
* video_overlay_video_renderer_param_specs
[VIDEO_OVERLAY_VIDEO_RENDERER_PROP_LAST] = { NULL, };
static void
gst_clapper_video_overlay_video_renderer_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)
{
GstClapperVideoOverlayVideoRenderer *self =
GST_CLAPPER_VIDEO_OVERLAY_VIDEO_RENDERER (object);
switch (prop_id) {
case VIDEO_OVERLAY_VIDEO_RENDERER_PROP_WINDOW_HANDLE:
self->window_handle = g_value_get_pointer (value);
if (self->video_overlay)
gst_video_overlay_set_window_handle (self->video_overlay,
(guintptr) self->window_handle);
break;
case VIDEO_OVERLAY_VIDEO_RENDERER_PROP_VIDEO_SINK:
self->video_sink = gst_object_ref_sink (g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_clapper_video_overlay_video_renderer_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec)
{
GstClapperVideoOverlayVideoRenderer *self =
GST_CLAPPER_VIDEO_OVERLAY_VIDEO_RENDERER (object);
switch (prop_id) {
case VIDEO_OVERLAY_VIDEO_RENDERER_PROP_WINDOW_HANDLE:
g_value_set_pointer (value, self->window_handle);
break;
case VIDEO_OVERLAY_VIDEO_RENDERER_PROP_VIDEO_SINK:
g_value_set_object (value, self->video_sink);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_clapper_video_overlay_video_renderer_finalize (GObject * object)
{
GstClapperVideoOverlayVideoRenderer *self =
GST_CLAPPER_VIDEO_OVERLAY_VIDEO_RENDERER (object);
if (self->video_overlay)
gst_object_unref (self->video_overlay);
if (self->video_sink)
gst_object_unref (self->video_sink);
G_OBJECT_CLASS
(gst_clapper_video_overlay_video_renderer_parent_class)->finalize (object);
}
static void
gst_clapper_video_overlay_video_renderer_class_init
(GstClapperVideoOverlayVideoRendererClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->set_property =
gst_clapper_video_overlay_video_renderer_set_property;
gobject_class->get_property =
gst_clapper_video_overlay_video_renderer_get_property;
gobject_class->finalize = gst_clapper_video_overlay_video_renderer_finalize;
video_overlay_video_renderer_param_specs
[VIDEO_OVERLAY_VIDEO_RENDERER_PROP_WINDOW_HANDLE] =
g_param_spec_pointer ("window-handle", "Window Handle",
"Window handle to embed the video into",
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
video_overlay_video_renderer_param_specs
[VIDEO_OVERLAY_VIDEO_RENDERER_PROP_VIDEO_SINK] =
g_param_spec_object ("video-sink", "Video Sink",
"the video output element to use (NULL = default sink)",
GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class,
VIDEO_OVERLAY_VIDEO_RENDERER_PROP_LAST,
video_overlay_video_renderer_param_specs);
}
static void
gst_clapper_video_overlay_video_renderer_init
(GstClapperVideoOverlayVideoRenderer * self)
{
self->x = self->y = self->width = self->height = -1;
self->video_sink = NULL;
}
static GstElement *gst_clapper_video_overlay_video_renderer_create_video_sink
(GstClapperVideoRenderer * iface, GstClapper * clapper)
{
GstElement *video_overlay;
GstClapperVideoOverlayVideoRenderer *self =
GST_CLAPPER_VIDEO_OVERLAY_VIDEO_RENDERER (iface);
if (self->video_overlay)
gst_object_unref (self->video_overlay);
video_overlay = gst_clapper_get_pipeline (clapper);
g_return_val_if_fail (GST_IS_VIDEO_OVERLAY (video_overlay), NULL);
self->video_overlay = GST_VIDEO_OVERLAY (video_overlay);
gst_video_overlay_set_window_handle (self->video_overlay,
(guintptr) self->window_handle);
if (self->width != -1 || self->height != -1)
gst_video_overlay_set_render_rectangle (self->video_overlay, self->x,
self->y, self->width, self->height);
return self->video_sink;
}
static void
gst_clapper_video_overlay_video_renderer_interface_init
(GstClapperVideoRendererInterface * iface)
{
iface->create_video_sink =
gst_clapper_video_overlay_video_renderer_create_video_sink;
}
/**
* gst_clapper_video_overlay_video_renderer_new:
* @window_handle: (allow-none): Window handle to use or %NULL
*
* Returns: (transfer full):
*/
GstClapperVideoRenderer *
gst_clapper_video_overlay_video_renderer_new (gpointer window_handle)
{
return g_object_new (GST_TYPE_CLAPPER_VIDEO_OVERLAY_VIDEO_RENDERER,
"window-handle", window_handle, NULL);
}
/**
* gst_clapper_video_overlay_video_renderer_new_with_sink:
* @window_handle: (allow-none): Window handle to use or %NULL
* @video_sink: (transfer floating): the custom video_sink element to be set for the video renderer
*
* Returns: (transfer full): clapper video renderer
*/
GstClapperVideoRenderer *
gst_clapper_video_overlay_video_renderer_new_with_sink (gpointer window_handle,
GstElement * video_sink)
{
return g_object_new (GST_TYPE_CLAPPER_VIDEO_OVERLAY_VIDEO_RENDERER,
"window-handle", window_handle, "video-sink", video_sink, NULL);
}
/**
* gst_clapper_video_overlay_video_renderer_set_window_handle:
* @self: #GstClapperVideoRenderer instance
* @window_handle: handle referencing to the platform specific window
*
* Sets the platform specific window handle into which the video
* should be rendered
**/
void gst_clapper_video_overlay_video_renderer_set_window_handle
(GstClapperVideoOverlayVideoRenderer * self, gpointer window_handle)
{
g_return_if_fail (GST_IS_CLAPPER_VIDEO_OVERLAY_VIDEO_RENDERER (self));
g_object_set (self, "window-handle", window_handle, NULL);
}
/**
* gst_clapper_video_overlay_video_renderer_get_window_handle:
* @self: #GstClapperVideoRenderer instance
*
* Returns: (transfer none): The currently set, platform specific window
* handle
*/
gpointer
gst_clapper_video_overlay_video_renderer_get_window_handle
(GstClapperVideoOverlayVideoRenderer * self) {
gpointer window_handle;
g_return_val_if_fail (GST_IS_CLAPPER_VIDEO_OVERLAY_VIDEO_RENDERER (self),
NULL);
g_object_get (self, "window-handle", &window_handle, NULL);
return window_handle;
}
/**
* gst_clapper_video_overlay_video_renderer_expose:
* @self: a #GstClapperVideoOverlayVideoRenderer instance.
*
* Tell an overlay that it has been exposed. This will redraw the current frame
* in the drawable even if the pipeline is PAUSED.
*/
void gst_clapper_video_overlay_video_renderer_expose
(GstClapperVideoOverlayVideoRenderer * self)
{
g_return_if_fail (GST_IS_CLAPPER_VIDEO_OVERLAY_VIDEO_RENDERER (self));
if (self->video_overlay)
gst_video_overlay_expose (self->video_overlay);
}
/**
* gst_clapper_video_overlay_video_renderer_set_render_rectangle:
* @self: a #GstClapperVideoOverlayVideoRenderer instance
* @x: the horizontal offset of the render area inside the window
* @y: the vertical offset of the render area inside the window
* @width: the width of the render area inside the window
* @height: the height of the render area inside the window
*
* Configure a subregion as a video target within the window set by
* gst_clapper_video_overlay_video_renderer_set_window_handle(). If this is not
* used or not supported the video will fill the area of the window set as the
* overlay to 100%. By specifying the rectangle, the video can be overlaid to
* a specific region of that window only. After setting the new rectangle one
* should call gst_clapper_video_overlay_video_renderer_expose() to force a
* redraw. To unset the region pass -1 for the @width and @height parameters.
*
* This method is needed for non fullscreen video overlay in UI toolkits that
* do not support subwindows.
*
*/
void gst_clapper_video_overlay_video_renderer_set_render_rectangle
(GstClapperVideoOverlayVideoRenderer * self, gint x, gint y, gint width,
gint height)
{
g_return_if_fail (GST_IS_CLAPPER_VIDEO_OVERLAY_VIDEO_RENDERER (self));
self->x = x;
self->y = y;
self->width = width;
self->height = height;
if (self->video_overlay)
gst_video_overlay_set_render_rectangle (self->video_overlay,
x, y, width, height);
}
/**
* gst_clapper_video_overlay_video_renderer_get_render_rectangle:
* @self: a #GstClapperVideoOverlayVideoRenderer instance
* @x: (out) (allow-none): the horizontal offset of the render area inside the window
* @y: (out) (allow-none): the vertical offset of the render area inside the window
* @width: (out) (allow-none): the width of the render area inside the window
* @height: (out) (allow-none): the height of the render area inside the window
*
* Return the currently configured render rectangle. See gst_clapper_video_overlay_video_renderer_set_render_rectangle()
* for details.
*
*/
void gst_clapper_video_overlay_video_renderer_get_render_rectangle
(GstClapperVideoOverlayVideoRenderer * self, gint * x, gint * y,
gint * width, gint * height)
{
g_return_if_fail (GST_IS_CLAPPER_VIDEO_OVERLAY_VIDEO_RENDERER (self));
if (x)
*x = self->x;
if (y)
*y = self->y;
if (width)
*width = self->width;
if (height)
*height = self->height;
}

View File

@@ -1,71 +0,0 @@
/* GStreamer
*
* Copyright (C) 2014-2015 Sebastian Dröge <sebastian@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_VIDEO_OVERLAY_VIDEO_RENDERER_H__
#define __GST_CLAPPER_VIDEO_OVERLAY_VIDEO_RENDERER_H__
#include <gst/clapper/gstclapper-types.h>
#include <gst/clapper/gstclapper-video-renderer.h>
G_BEGIN_DECLS
typedef struct _GstClapperVideoOverlayVideoRenderer
GstClapperVideoOverlayVideoRenderer;
typedef struct _GstClapperVideoOverlayVideoRendererClass
GstClapperVideoOverlayVideoRendererClass;
#define GST_TYPE_CLAPPER_VIDEO_OVERLAY_VIDEO_RENDERER (gst_clapper_video_overlay_video_renderer_get_type ())
#define GST_IS_CLAPPER_VIDEO_OVERLAY_VIDEO_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CLAPPER_VIDEO_OVERLAY_VIDEO_RENDERER))
#define GST_IS_CLAPPER_VIDEO_OVERLAY_VIDEO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_CLAPPER_VIDEO_OVERLAY_VIDEO_RENDERER))
#define GST_CLAPPER_VIDEO_OVERLAY_VIDEO_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CLAPPER_VIDEO_OVERLAY_VIDEO_RENDERER, GstClapperVideoOverlayVideoRendererClass))
#define GST_CLAPPER_VIDEO_OVERLAY_VIDEO_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_CLAPPER_VIDEO_OVERLAY_VIDEO_RENDERER, GstClapperVideoOverlayVideoRenderer))
#define GST_CLAPPER_VIDEO_OVERLAY_VIDEO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_CLAPPER_VIDEO_OVERLAY_VIDEO_RENDERER, GstClapperVideoOverlayVideoRendererClass))
#define GST_CLAPPER_VIDEO_OVERLAY_VIDEO_RENDERER_CAST(obj) ((GstClapperVideoOverlayVideoRenderer*)(obj))
GST_CLAPPER_API
GType gst_clapper_video_overlay_video_renderer_get_type (void);
GST_CLAPPER_API
GstClapperVideoRenderer *
gst_clapper_video_overlay_video_renderer_new (gpointer window_handle);
GST_CLAPPER_API
GstClapperVideoRenderer *
gst_clapper_video_overlay_video_renderer_new_with_sink (gpointer window_handle, GstElement *video_sink);
GST_CLAPPER_API
void gst_clapper_video_overlay_video_renderer_set_window_handle (GstClapperVideoOverlayVideoRenderer *self, gpointer window_handle);
GST_CLAPPER_API
gpointer gst_clapper_video_overlay_video_renderer_get_window_handle (GstClapperVideoOverlayVideoRenderer *self);
GST_CLAPPER_API
void gst_clapper_video_overlay_video_renderer_expose (GstClapperVideoOverlayVideoRenderer *self);
GST_CLAPPER_API
void gst_clapper_video_overlay_video_renderer_set_render_rectangle (GstClapperVideoOverlayVideoRenderer *self, gint x, gint y, gint width, gint height);
GST_CLAPPER_API
void gst_clapper_video_overlay_video_renderer_get_render_rectangle (GstClapperVideoOverlayVideoRenderer *self, gint *x, gint *y, gint *width, gint *height);
G_END_DECLS
#endif /* __GST_CLAPPER_VIDEO_OVERLAY_VIDEO_RENDERER_H__ */

View File

@@ -1,33 +0,0 @@
/* GStreamer
*
* Copyright (C) 2014-2015 Sebastian Dröge <sebastian@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_VIDEO_RENDERER_PRIVATE_H__
#define __GST_CLAPPER_VIDEO_RENDERER_PRIVATE_H__
#include <gst/clapper/gstclapper-video-renderer.h>
G_BEGIN_DECLS
G_GNUC_INTERNAL GstElement * gst_clapper_video_renderer_create_video_sink (GstClapperVideoRenderer *self, GstClapper *clapper);
G_END_DECLS
#endif /* __GST_CLAPPER_VIDEO_RENDERER_PRIVATE_H__ */

View File

@@ -1,50 +0,0 @@
/* GStreamer
*
* Copyright (C) 2014-2015 Sebastian Dröge <sebastian@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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstclapper-video-renderer.h"
#include "gstclapper-video-renderer-private.h"
G_DEFINE_INTERFACE (GstClapperVideoRenderer, gst_clapper_video_renderer,
G_TYPE_OBJECT);
static void
gst_clapper_video_renderer_default_init (G_GNUC_UNUSED
GstClapperVideoRendererInterface * iface)
{
}
GstElement *
gst_clapper_video_renderer_create_video_sink (GstClapperVideoRenderer * self,
GstClapper * clapper)
{
GstClapperVideoRendererInterface *iface;
g_return_val_if_fail (GST_IS_CLAPPER_VIDEO_RENDERER (self), NULL);
iface = GST_CLAPPER_VIDEO_RENDERER_GET_INTERFACE (self);
g_return_val_if_fail (iface->create_video_sink != NULL, NULL);
return iface->create_video_sink (self, clapper);
}

View File

@@ -1,49 +0,0 @@
/* GStreamer
*
* Copyright (C) 2014-2015 Sebastian Dröge <sebastian@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_VIDEO_RENDERER_H__
#define __GST_CLAPPER_VIDEO_RENDERER_H__
#include <gst/gst.h>
#include <gst/clapper/gstclapper-types.h>
G_BEGIN_DECLS
typedef struct _GstClapperVideoRenderer GstClapperVideoRenderer;
typedef struct _GstClapperVideoRendererInterface GstClapperVideoRendererInterface;
#define GST_TYPE_CLAPPER_VIDEO_RENDERER (gst_clapper_video_renderer_get_type ())
#define GST_CLAPPER_VIDEO_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_CLAPPER_VIDEO_RENDERER, GstClapperVideoRenderer))
#define GST_IS_CLAPPER_VIDEO_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CLAPPER_VIDEO_RENDERER))
#define GST_CLAPPER_VIDEO_RENDERER_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GST_TYPE_CLAPPER_VIDEO_RENDERER, GstClapperVideoRendererInterface))
struct _GstClapperVideoRendererInterface {
GTypeInterface parent_iface;
GstElement * (*create_video_sink) (GstClapperVideoRenderer * self, GstClapper * clapper);
};
GST_CLAPPER_API
GType gst_clapper_video_renderer_get_type (void);
G_END_DECLS
#endif /* __GST_CLAPPER_VIDEO_RENDERER_H__ */

View File

@@ -1,180 +0,0 @@
/* GStreamer
*
* Copyright (C) 2014-2015 Sebastian Dröge <sebastian@centricular.com>
* Copyright (C) 2015 Brijesh Singh <brijesh.ksingh@gmail.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.
*/
/**
* SECTION:gstclapper-visualization
* @title: GstClapperVisualization
* @short_description: Clapper Visualization
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstclapper-visualization.h"
#include <string.h>
static GMutex vis_lock;
static GQueue vis_list = G_QUEUE_INIT;
static guint32 vis_cookie;
G_DEFINE_BOXED_TYPE (GstClapperVisualization, gst_clapper_visualization,
(GBoxedCopyFunc) gst_clapper_visualization_copy,
(GBoxedFreeFunc) gst_clapper_visualization_free);
/**
* gst_clapper_visualization_free:
* @vis: #GstClapperVisualization instance
*
* Frees a #GstClapperVisualization.
*/
void
gst_clapper_visualization_free (GstClapperVisualization * vis)
{
g_return_if_fail (vis != NULL);
g_free (vis->name);
g_free (vis->description);
g_free (vis);
}
/**
* gst_clapper_visualization_copy:
* @vis: #GstClapperVisualization instance
*
* Makes a copy of the #GstClapperVisualization. The result must be
* freed using gst_clapper_visualization_free().
*
* Returns: (transfer full): an allocated copy of @vis.
*/
GstClapperVisualization *
gst_clapper_visualization_copy (const GstClapperVisualization * vis)
{
GstClapperVisualization *ret;
g_return_val_if_fail (vis != NULL, NULL);
ret = g_new0 (GstClapperVisualization, 1);
ret->name = vis->name ? g_strdup (vis->name) : NULL;
ret->description = vis->description ? g_strdup (vis->description) : NULL;
return ret;
}
/**
* gst_clapper_visualizations_free:
* @viss: a %NULL terminated array of #GstClapperVisualization to free
*
* Frees a %NULL terminated array of #GstClapperVisualization.
*/
void
gst_clapper_visualizations_free (GstClapperVisualization ** viss)
{
GstClapperVisualization **p;
g_return_if_fail (viss != NULL);
p = viss;
while (*p) {
g_free ((*p)->name);
g_free ((*p)->description);
g_free (*p);
p++;
}
g_free (viss);
}
static void
gst_clapper_update_visualization_list (void)
{
GList *features;
GList *l;
guint32 cookie;
GstClapperVisualization *vis;
g_mutex_lock (&vis_lock);
/* check if we need to update the list */
cookie = gst_registry_get_feature_list_cookie (gst_registry_get ());
if (vis_cookie == cookie) {
g_mutex_unlock (&vis_lock);
return;
}
/* if update is needed then first free the existing list */
while ((vis = g_queue_pop_head (&vis_list)))
gst_clapper_visualization_free (vis);
features = gst_registry_get_feature_list (gst_registry_get (),
GST_TYPE_ELEMENT_FACTORY);
for (l = features; l; l = l->next) {
GstPluginFeature *feature = l->data;
const gchar *klass;
klass = gst_element_factory_get_metadata (GST_ELEMENT_FACTORY (feature),
GST_ELEMENT_METADATA_KLASS);
if (strstr (klass, "Visualization")) {
vis = g_new0 (GstClapperVisualization, 1);
vis->name = g_strdup (gst_plugin_feature_get_name (feature));
vis->description =
g_strdup (gst_element_factory_get_metadata (GST_ELEMENT_FACTORY
(feature), GST_ELEMENT_METADATA_DESCRIPTION));
g_queue_push_tail (&vis_list, vis);
}
}
gst_plugin_feature_list_free (features);
vis_cookie = cookie;
g_mutex_unlock (&vis_lock);
}
/**
* gst_clapper_visualizations_get:
*
* Returns: (transfer full) (array zero-terminated=1) (element-type GstClapperVisualization):
* a %NULL terminated array containing all available
* visualizations. Use gst_clapper_visualizations_free() after
* usage.
*/
GstClapperVisualization **
gst_clapper_visualizations_get (void)
{
gint i = 0;
GList *l;
GstClapperVisualization **ret;
gst_clapper_update_visualization_list ();
g_mutex_lock (&vis_lock);
ret = g_new0 (GstClapperVisualization *, g_queue_get_length (&vis_list) + 1);
for (l = vis_list.head; l; l = l->next)
ret[i++] = gst_clapper_visualization_copy (l->data);
g_mutex_unlock (&vis_lock);
return ret;
}

View File

@@ -1,61 +0,0 @@
/* GStreamer
*
* Copyright (C) 2014-2015 Sebastian Dröge <sebastian@centricular.com>
* Copyright (C) 2015 Brijesh Singh <brijesh.ksingh@gmail.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_VISUALIZATION_H__
#define __GST_CLAPPER_VISUALIZATION_H__
#include <gst/gst.h>
#include <gst/clapper/clapper-prelude.h>
G_BEGIN_DECLS
typedef struct _GstClapperVisualization GstClapperVisualization;
/**
* GstClapperVisualization:
* @name: name of the visualization.
* @description: description of the visualization.
*
* A #GstClapperVisualization descriptor.
*/
struct _GstClapperVisualization {
gchar *name;
gchar *description;
};
GST_CLAPPER_API
GType gst_clapper_visualization_get_type (void);
GST_CLAPPER_API
GstClapperVisualization * gst_clapper_visualization_copy (const GstClapperVisualization *vis);
GST_CLAPPER_API
void gst_clapper_visualization_free (GstClapperVisualization *vis);
GST_CLAPPER_API
GstClapperVisualization ** gst_clapper_visualizations_get (void);
GST_CLAPPER_API
void gst_clapper_visualizations_free (GstClapperVisualization **viss);
G_END_DECLS
#endif /* __GST_CLAPPER_VISUALIZATION_H__ */

File diff suppressed because it is too large Load Diff

View File

@@ -1,318 +0,0 @@
/* GStreamer
*
* Copyright (C) 2014-2015 Sebastian Dröge <sebastian@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_H__
#define __GST_CLAPPER_H__
#include <gst/gst.h>
#include <gst/video/video.h>
#include <gst/clapper/clapper-prelude.h>
#include <gst/clapper/gstclapper-types.h>
#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
/* ClapperState */
GST_CLAPPER_API
GType gst_clapper_state_get_type (void);
#define GST_TYPE_CLAPPER_STATE (gst_clapper_state_get_type ())
/**
* GstClapperState:
* @GST_CLAPPER_STATE_STOPPED: clapper is stopped.
* @GST_CLAPPER_STATE_BUFFERING: clapper is buffering.
* @GST_CLAPPER_STATE_PAUSED: clapper is paused.
* @GST_CLAPPER_STATE_PLAYING: clapper is currently playing a stream.
*/
typedef enum
{
GST_CLAPPER_STATE_STOPPED,
GST_CLAPPER_STATE_BUFFERING,
GST_CLAPPER_STATE_PAUSED,
GST_CLAPPER_STATE_PLAYING
} GstClapperState;
GST_CLAPPER_API
const gchar * gst_clapper_state_get_name (GstClapperState state);
/* ClapperSeekMode */
GST_CLAPPER_API
GType gst_clapper_seek_mode_get_type (void);
#define GST_TYPE_CLAPPER_SEEK_MODE (gst_clapper_seek_mode_get_type ())
/**
* GstClapperSeekMode:
* @GST_CLAPPER_SEEK_MODE_DEFAULT: default seek method (flush only).
* @GST_CLAPPER_SEEK_MODE_ACCURATE: accurate seek method.
* @GST_CLAPPER_SEEK_MODE_FAST: fast seek method (next keyframe).
*/
typedef enum
{
GST_CLAPPER_SEEK_MODE_DEFAULT,
GST_CLAPPER_SEEK_MODE_ACCURATE,
GST_CLAPPER_SEEK_MODE_FAST,
} GstClapperSeekMode;
/* ClapperError */
GST_CLAPPER_API
GQuark gst_clapper_error_quark (void);
GST_CLAPPER_API
GType gst_clapper_error_get_type (void);
#define GST_CLAPPER_ERROR (gst_clapper_error_quark ())
#define GST_TYPE_CLAPPER_ERROR (gst_clapper_error_get_type ())
/**
* GstClapperError:
* @GST_CLAPPER_ERROR_FAILED: generic error.
*/
typedef enum {
GST_CLAPPER_ERROR_FAILED = 0
} GstClapperError;
GST_CLAPPER_API
const gchar * gst_clapper_error_get_name (GstClapperError error);
/* ClapperColorBalanceType */
GST_CLAPPER_API
GType gst_clapper_color_balance_type_get_type (void);
#define GST_TYPE_CLAPPER_COLOR_BALANCE_TYPE (gst_clapper_color_balance_type_get_type ())
/**
* GstClapperColorBalanceType:
* @GST_CLAPPER_COLOR_BALANCE_BRIGHTNESS: brightness or black level.
* @GST_CLAPPER_COLOR_BALANCE_CONTRAST: contrast or luma gain.
* @GST_CLAPPER_COLOR_BALANCE_SATURATION: color saturation or chroma
* gain.
* @GST_CLAPPER_COLOR_BALANCE_HUE: hue or color balance.
*/
typedef enum
{
GST_CLAPPER_COLOR_BALANCE_BRIGHTNESS,
GST_CLAPPER_COLOR_BALANCE_CONTRAST,
GST_CLAPPER_COLOR_BALANCE_SATURATION,
GST_CLAPPER_COLOR_BALANCE_HUE,
} GstClapperColorBalanceType;
GST_CLAPPER_API
const gchar * gst_clapper_color_balance_type_get_name (GstClapperColorBalanceType type);
/* ClapperSnapshotFormat */
/**
* GstClapperSnapshotFormat:
* @GST_CLAPPER_THUMBNAIL_RAW_NATIVE: RAW Native.
* @GST_CLAPPER_THUMBNAIL_RAW_xRGB: RAW xRGB.
* @GST_CLAPPER_THUMBNAIL_RAW_BGRx: RAW BGRx.
* @GST_CLAPPER_THUMBNAIL_JPG: JPG.
* @GST_CLAPPER_THUMBNAIL_PNG: PNG.
*/
typedef enum
{
GST_CLAPPER_THUMBNAIL_RAW_NATIVE = 0,
GST_CLAPPER_THUMBNAIL_RAW_xRGB,
GST_CLAPPER_THUMBNAIL_RAW_BGRx,
GST_CLAPPER_THUMBNAIL_JPG,
GST_CLAPPER_THUMBNAIL_PNG
} GstClapperSnapshotFormat;
#define GST_TYPE_CLAPPER (gst_clapper_get_type ())
#define GST_IS_CLAPPER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CLAPPER))
#define GST_IS_CLAPPER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_CLAPPER))
#define GST_CLAPPER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CLAPPER, GstClapperClass))
#define GST_CLAPPER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_CLAPPER, GstClapper))
#define GST_CLAPPER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_CLAPPER, GstClapperClass))
#define GST_CLAPPER_CAST(obj) ((GstClapper*)(obj))
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstClapper, gst_object_unref)
#endif
GST_CLAPPER_API
GType gst_clapper_get_type (void);
GST_CLAPPER_API
void gst_clapper_gst_init (int *argc, char **argv[]);
GST_CLAPPER_API
GstClapper * gst_clapper_new (GstClapperVideoRenderer *video_renderer, GstClapperSignalDispatcher *signal_dispatcher,
GstClapperMpris *mpris);
GST_CLAPPER_API
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);
GST_CLAPPER_API
void gst_clapper_set_seek_mode (GstClapper *clapper, GstClapperSeekMode mode);
GST_CLAPPER_API
void gst_clapper_set_rate (GstClapper *clapper, gdouble rate);
GST_CLAPPER_API
gdouble gst_clapper_get_rate (GstClapper *clapper);
GST_CLAPPER_API
gchar * gst_clapper_get_uri (GstClapper *clapper);
GST_CLAPPER_API
void gst_clapper_set_uri (GstClapper *clapper, const gchar *uri);
GST_CLAPPER_API
gchar * gst_clapper_get_subtitle_uri (GstClapper *clapper);
GST_CLAPPER_API
void gst_clapper_set_subtitle_uri (GstClapper *clapper, const gchar *uri);
GST_CLAPPER_API
GstClockTime gst_clapper_get_position (GstClapper *clapper);
GST_CLAPPER_API
GstClockTime gst_clapper_get_duration (GstClapper *clapper);
GST_CLAPPER_API
gdouble gst_clapper_get_volume (GstClapper *clapper);
GST_CLAPPER_API
void gst_clapper_set_volume (GstClapper *clapper, gdouble val);
GST_CLAPPER_API
gboolean gst_clapper_get_mute (GstClapper *clapper);
GST_CLAPPER_API
void gst_clapper_set_mute (GstClapper *clapper, gboolean val);
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);
GST_CLAPPER_API
void gst_clapper_set_audio_track_enabled (GstClapper *clapper, gboolean enabled);
GST_CLAPPER_API
void gst_clapper_set_subtitle_track_enabled (GstClapper *clapper, gboolean enabled);
GST_CLAPPER_API
gboolean gst_clapper_set_audio_track (GstClapper *clapper, gint stream_index);
GST_CLAPPER_API
gboolean gst_clapper_set_video_track (GstClapper *clapper, gint stream_index);
GST_CLAPPER_API
gboolean gst_clapper_set_subtitle_track (GstClapper *clapper, gint stream_index);
GST_CLAPPER_API
GstClapperMediaInfo *
gst_clapper_get_media_info (GstClapper *clapper);
GST_CLAPPER_API
GstClapperAudioInfo *
gst_clapper_get_current_audio_track (GstClapper *clapper);
GST_CLAPPER_API
GstClapperVideoInfo *
gst_clapper_get_current_video_track (GstClapper *clapper);
GST_CLAPPER_API
GstClapperSubtitleInfo *
gst_clapper_get_current_subtitle_track (GstClapper *clapper);
GST_CLAPPER_API
gboolean gst_clapper_set_visualization (GstClapper *clapper, const gchar *name);
GST_CLAPPER_API
void gst_clapper_set_visualization_enabled (GstClapper *clapper, gboolean enabled);
GST_CLAPPER_API
gchar * gst_clapper_get_current_visualization (GstClapper *clapper);
GST_CLAPPER_API
gboolean gst_clapper_has_color_balance (GstClapper *clapper);
GST_CLAPPER_API
void gst_clapper_set_color_balance (GstClapper *clapper, GstClapperColorBalanceType type, gdouble value);
GST_CLAPPER_API
gdouble gst_clapper_get_color_balance (GstClapper *clapper, GstClapperColorBalanceType type);
GST_CLAPPER_API
GstVideoMultiviewFramePacking
gst_clapper_get_multiview_mode (GstClapper *clapper);
GST_CLAPPER_API
void gst_clapper_set_multiview_mode (GstClapper *clapper, GstVideoMultiviewFramePacking mode);
GST_CLAPPER_API
GstVideoMultiviewFlags
gst_clapper_get_multiview_flags (GstClapper *clapper);
GST_CLAPPER_API
void gst_clapper_set_multiview_flags (GstClapper *clapper, GstVideoMultiviewFlags flags);
GST_CLAPPER_API
gint64 gst_clapper_get_audio_video_offset (GstClapper *clapper);
GST_CLAPPER_API
void gst_clapper_set_audio_video_offset (GstClapper *clapper, gint64 offset);
GST_CLAPPER_API
gint64 gst_clapper_get_subtitle_video_offset (GstClapper *clapper);
GST_CLAPPER_API
void gst_clapper_set_subtitle_video_offset (GstClapper *clapper, gint64 offset);
GST_CLAPPER_API
GstSample * gst_clapper_get_video_snapshot (GstClapper *clapper, GstClapperSnapshotFormat format, const GstStructure *config);
G_END_DECLS
#endif /* __GST_CLAPPER_H__ */

View File

@@ -1,759 +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: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

@@ -1,105 +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 __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

@@ -1,103 +0,0 @@
/*
* 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

@@ -1,29 +0,0 @@
/*
* 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,95 +0,0 @@
/*
* 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 <gst/gst.h>
#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;
}
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

@@ -1,46 +0,0 @@
/*
* 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.
*/
#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

@@ -1,113 +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_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,120 +0,0 @@
gstclapper_sources = [
'gstclapper.c',
'gstclapper-signal-dispatcher.c',
'gstclapper-video-renderer.c',
'gstclapper-media-info.c',
'gstclapper-g-main-context-signal-dispatcher.c',
'gstclapper-video-overlay-video-renderer.c',
'gstclapper-visualization.c',
'gstclapper-mpris.c',
'gstclapper-gtk4-plugin.c',
'gtk4/gstclapperglsink.c',
'gtk4/gstgtkutils.c',
'gtk4/gtkclapperglwidget.c',
'gtk4/gstclapperglutils.c',
]
gstclapper_headers = [
'clapper.h',
'clapper-prelude.h',
'gstclapper.h',
'gstclapper-types.h',
'gstclapper-signal-dispatcher.h',
'gstclapper-video-renderer.h',
'gstclapper-media-info.h',
'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 = [
'-DHAVE_CONFIG_H',
'-DBUILDING_GST_CLAPPER',
'-DGST_USE_UNSTABLE_API',
'-DHAVE_GTK_GL',
]
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 not gir.found()
error('Cannot build lib without GIR support')
endif
if gst_gl_have_window_x11 and (gst_gl_have_platform_egl or gst_gl_have_platform_glx)
gtk_x11_dep = dependency('gtk4-x11', required: false)
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
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_deps = [
gtk4_dep, glib_dep, gio_dep,
gstbase_dep, gstvideo_dep, gstaudio_dep,
gsttag_dep, gstpbutils_dep, libm
] + gtk_deps
if os_unix
gstclapper_deps += giounix_dep
else
gstclapper_deps += giowin_dep
endif
gstclapper = library('gstclapper-' + api_version,
gstclapper_sources + gstclapper_mpris_gdbus,
c_args: gstclapper_defines,
link_args: noseh_link_args,
include_directories: [configinc, libsinc],
version: libversion,
install: true,
install_dir: pkglibdir,
dependencies: gstclapper_deps,
)
clapper_gir = gnome.generate_gir(gstclapper,
sources: gstclapper_sources + gstclapper_headers,
namespace: 'GstClapper',
nsversion: api_version,
identifier_prefix: 'Gst',
symbol_prefix: 'gst',
export_packages: 'gstreamer-clapper-1.0',
includes: ['Gst-1.0', 'GstPbutils-1.0', 'GstBase-1.0', 'GstVideo-1.0',
'GstAudio-1.0', 'GstTag-1.0'],
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]
)

View File

@@ -1,5 +0,0 @@
if get_option('lib')
subdir('clapper')
endif
subdir('plugin')

View File

@@ -1,83 +0,0 @@
gst_clapper_gl_ch_dep = dependency('', required: false)
build_gl_ch = (
not get_option('glimporter').disabled()
or not get_option('gluploader').disabled()
)
gl_support_required = (
get_option('glimporter').enabled()
or get_option('gluploader').enabled()
)
gst_plugin_gl_ch_deps = [gst_clapper_sink_dep, gstgl_dep, gstglproto_dep]
have_gtk_gl_windowing = false
if gst_gl_have_window_x11 and (gst_gl_have_platform_egl or gst_gl_have_platform_glx)
gtk_x11_dep = dependency('gtk4-x11', required: false)
if gtk_x11_dep.found()
gst_plugin_gl_ch_deps += gtk_x11_dep
if gst_gl_have_platform_glx
gst_plugin_gl_ch_deps += gstglx11_dep
endif
have_gtk_gl_windowing = true
endif
endif
if gst_gl_have_window_wayland and gst_gl_have_platform_egl
gtk_wayland_dep = dependency('gtk4-wayland', required: false)
if gtk_wayland_dep.found()
gst_plugin_gl_ch_deps += [gtk_wayland_dep, gstglwayland_dep]
have_gtk_gl_windowing = true
endif
endif
if gst_gl_have_window_win32 and (gst_gl_have_platform_egl or gst_gl_have_platform_wgl)
gtk_win32_dep = dependency('gtk4-win32', required: false)
if gtk_win32_dep.found()
gst_plugin_gl_ch_deps += gtk_win32_dep
have_gtk_gl_windowing = true
endif
endif
if gst_gl_have_window_cocoa and gst_gl_have_platform_cgl
gtk_macos_dep = dependency('gtk4-macos', required: false)
if gtk_macos_dep.found()
gst_plugin_gl_ch_deps += gtk_macos_dep
have_gtk_gl_windowing = true
endif
endif
if not have_gtk_gl_windowing
if gl_support_required
error('GL-based importer was enabled, but support for current GL windowing is missing')
endif
build_gl_ch = false
endif
if gst_gl_have_platform_egl
gst_plugin_gl_ch_deps += gstglegl_dep
endif
foreach dep : gst_plugin_gl_ch_deps
if not dep.found()
if gl_support_required
error('GL-based importer was enabled, but required dependencies were not found')
endif
build_gl_ch = false
endif
endforeach
if build_gl_ch
gst_clapper_gl_ch_dep = declare_dependency(
link_with: library('gstclapperglcontexthandler',
'gstclapperglcontexthandler.c',
c_args: gst_clapper_plugin_args,
include_directories: configinc,
dependencies: gst_plugin_gl_ch_deps,
version: libversion,
install: true,
),
include_directories: configinc,
dependencies: gst_plugin_gl_ch_deps,
)
endif

View File

@@ -1,274 +0,0 @@
glib_req = '>= 2.68.0'
gst_req = '>= 1.20.0'
api_version = '1.0'
libversion = meson.project_version()
cc = meson.get_compiler('c')
cxx = meson.get_compiler('cpp')
cdata = configuration_data()
os_unix = host_machine.system() != 'windows'
if cc.get_id() == 'msvc'
msvc_args = [
# Ignore several spurious warnings for things gstreamer does very commonly
# If a warning is completely useless and spammy, use '/wdXXXX' to suppress it
# If a warning is harmless but hard to fix, use '/woXXXX' so it's shown once
# NOTE: Only add warnings here if you are sure they're spurious
'/wd4018', # implicit signed/unsigned conversion
'/wd4146', # unary minus on unsigned (beware INT_MIN)
'/wd4244', # lossy type conversion (e.g. double -> int)
'/wd4305', # truncating type conversion (e.g. double -> float)
cc.get_supported_arguments(['/utf-8']), # set the input encoding to utf-8
# Enable some warnings on MSVC to match GCC/Clang behaviour
'/w14062', # enumerator 'identifier' in switch of enum 'enumeration' is not handled
'/w14101', # 'identifier' : unreferenced local variable
'/w14189', # 'identifier' : local variable is initialized but not referenced
]
add_project_arguments(msvc_args, language: ['c', 'cpp'])
noseh_link_args = ['/SAFESEH:NO']
else
if cxx.has_argument('-Wno-non-virtual-dtor')
add_project_arguments('-Wno-non-virtual-dtor', language: 'cpp')
endif
noseh_link_args = []
endif
if cc.has_link_argument('-Wl,-Bsymbolic-functions')
add_project_link_arguments('-Wl,-Bsymbolic-functions', language: 'c')
endif
# Symbol visibility
if cc.get_id() == 'msvc'
export_define = '__declspec(dllexport) extern'
else
export_define = 'extern'
endif
# Passing this through the command line would be too messy
cdata.set('GST_API_EXPORT', export_define)
# Disable strict aliasing
if cc.has_argument('-fno-strict-aliasing')
add_project_arguments('-fno-strict-aliasing', language: 'c')
endif
if cxx.has_argument('-fno-strict-aliasing')
add_project_arguments('-fno-strict-aliasing', language: 'cpp')
endif
if not get_option('deprecated-glib-api')
message('Disabling deprecated GLib API')
add_project_arguments('-DG_DISABLE_DEPRECATED', language: 'c')
endif
if not get_option('devel-checks')
message('Disabling GLib cast checks')
add_project_arguments('-DG_DISABLE_CAST_CHECKS', language: 'c')
message('Disabling GLib asserts')
add_project_arguments('-DG_DISABLE_ASSERT', language: 'c')
message('Disabling GLib checks')
add_project_arguments('-DG_DISABLE_CHECKS', language: 'c')
endif
check_headers = [
['HAVE_DLFCN_H', 'dlfcn.h'],
['HAVE_FCNTL_H', 'fcntl.h'],
['HAVE_INTTYPES_H', 'inttypes.h'],
['HAVE_MEMORY_H', 'memory.h'],
['HAVE_NETINET_IN_H', 'netinet/in.h'],
['HAVE_NETINET_IP_H', 'netinet/ip.h'],
['HAVE_NETINET_TCP_H', 'netinet/tcp.h'],
['HAVE_PTHREAD_H', 'pthread.h'],
['HAVE_STDINT_H', 'stdint.h'],
['HAVE_STDLIB_H', 'stdlib.h'],
['HAVE_STRINGS_H', 'strings.h'],
['HAVE_STRING_H', 'string.h'],
['HAVE_SYS_PARAM_H', 'sys/param.h'],
['HAVE_SYS_SOCKET_H', 'sys/socket.h'],
['HAVE_SYS_STAT_H', 'sys/stat.h'],
['HAVE_SYS_TIME_H', 'sys/time.h'],
['HAVE_SYS_TYPES_H', 'sys/types.h'],
['HAVE_SYS_UTSNAME_H', 'sys/utsname.h'],
['HAVE_UNISTD_H', 'unistd.h'],
]
foreach h : check_headers
if cc.has_header(h.get(1))
cdata.set(h.get(0), 1)
endif
endforeach
check_functions = [
['HAVE_DCGETTEXT', 'dcgettext'],
['HAVE_GETPAGESIZE', 'getpagesize'],
['HAVE_GMTIME_R', 'gmtime_r'],
['HAVE_MEMFD_CREATE', 'memfd_create'],
['HAVE_MMAP', 'mmap'],
['HAVE_PIPE2', 'pipe2'],
['HAVE_GETRUSAGE', 'getrusage', '#include<sys/resource.h>'],
]
foreach f : check_functions
prefix = ''
if f.length() == 3
prefix = f.get(2)
endif
if cc.has_function(f.get(1), prefix: prefix)
cdata.set(f.get(0), 1)
endif
endforeach
cdata.set('SIZEOF_CHAR', cc.sizeof('char'))
cdata.set('SIZEOF_INT', cc.sizeof('int'))
cdata.set('SIZEOF_LONG', cc.sizeof('long'))
cdata.set('SIZEOF_SHORT', cc.sizeof('short'))
cdata.set('SIZEOF_VOIDP', cc.sizeof('void*'))
cdata.set_quoted('VERSION', libversion)
cdata.set_quoted('PACKAGE', 'clapper')
cdata.set_quoted('PACKAGE_VERSION', libversion)
cdata.set_quoted('PACKAGE_BUGREPORT', 'https://github.com/Rafostar/clapper/issues/new')
cdata.set_quoted('PACKAGE_NAME', 'GStreamer Clapper Libs')
cdata.set_quoted('GST_API_VERSION', api_version)
cdata.set_quoted('GST_LICENSE', 'LGPL')
cdata.set_quoted('LIBDIR', pkglibdir)
cdata.set_quoted('LOCALEDIR', join_paths(get_option('prefix'), get_option('localedir')))
warning_flags = [
'-Wmissing-declarations',
'-Wredundant-decls',
'-Wwrite-strings',
'-Wformat',
'-Wformat-security',
'-Winit-self',
'-Wmissing-include-dirs',
'-Waddress',
'-Wno-multichar',
'-Wvla',
'-Wpointer-arith',
]
warning_c_flags = [
'-Wmissing-prototypes',
'-Wdeclaration-after-statement',
'-Wold-style-definition',
]
warning_cxx_flags = [
'-Wformat-nonliteral',
]
foreach extra_arg : warning_c_flags
if cc.has_argument (extra_arg)
add_project_arguments([extra_arg], language: 'c')
endif
endforeach
foreach extra_arg : warning_cxx_flags
if cxx.has_argument (extra_arg)
add_project_arguments([extra_arg], language: 'cpp')
endif
endforeach
foreach extra_arg : warning_flags
if cc.has_argument (extra_arg)
add_project_arguments([extra_arg], language: 'c')
endif
if cxx.has_argument (extra_arg)
add_project_arguments([extra_arg], language: 'cpp')
endif
endforeach
cdata.set_quoted('GST_PACKAGE_NAME', 'gst-plugin-clapper')
cdata.set_quoted('GST_PACKAGE_ORIGIN', 'https://github.com/Rafostar/clapper')
# Mandatory GST deps
gst_dep = dependency('gstreamer-1.0', version: gst_req,
fallback: ['gstreamer', 'gst_dep'])
gstbase_dep = dependency('gstreamer-base-1.0', version: gst_req,
fallback: ['gstreamer', 'gst_base_dep'])
gstpbutils_dep = dependency('gstreamer-pbutils-1.0', version: gst_req,
fallback: ['gst-plugins-base', 'pbutils_dep'])
gstaudio_dep = dependency('gstreamer-audio-1.0', version: gst_req,
fallback: ['gst-plugins-base', 'audio_dep'])
gsttag_dep = dependency('gstreamer-tag-1.0', version: gst_req,
fallback: ['gst-plugins-base', 'tag_dep'])
gstvideo_dep = dependency('gstreamer-video-1.0', version: gst_req,
fallback: ['gst-plugins-base', 'video_dep'])
# 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)
gst_gl_apis = gstgl_dep.get_pkgconfig_variable('gl_apis')
gst_gl_winsys = gstgl_dep.get_pkgconfig_variable('gl_winsys')
gst_gl_platforms = gstgl_dep.get_pkgconfig_variable('gl_platforms')
message('GStreamer OpenGL window systems: @0@'.format(gst_gl_winsys))
message('GStreamer OpenGL platforms: @0@'.format(gst_gl_platforms))
message('GStreamer OpenGL apis: @0@'.format(gst_gl_apis))
foreach ws : ['x11', 'wayland', 'android', 'cocoa', 'eagl', 'win32', 'dispmanx', 'viv_fb']
set_variable('gst_gl_have_window_@0@'.format(ws), gst_gl_winsys.contains(ws))
endforeach
foreach p : ['glx', 'egl', 'cgl', 'eagl', 'wgl']
set_variable('gst_gl_have_platform_@0@'.format(p), gst_gl_platforms.contains(p))
endforeach
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)
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)
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)
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)
endif
libm = cc.find_library('m', required: false)
glib_dep = dependency('glib-2.0', version: glib_req, fallback: ['glib', 'libglib_dep'])
gmodule_dep = dependency('gmodule-2.0', fallback: ['glib', 'libgmodule_dep'])
gio_dep = dependency('gio-2.0', fallback: ['glib', 'libgio_dep'])
if os_unix
giounix_dep = dependency('gio-unix-2.0', version: glib_req, fallback: ['glib', 'libgio_dep'])
else
giowin_dep = dependency('gio-windows-2.0', version: glib_req, fallback : ['glib', 'libgio_dep'])
endif
cdata.set('DISABLE_ORC', 1)
cdata.set('GST_ENABLE_EXTRA_CHECKS', get_option('devel-checks'))
configinc = include_directories('.')
libsinc = include_directories('gst')
gir = find_program('g-ir-scanner', required: false)
gir_init_section = ['--add-init-section=extern void gst_init(gint*,gchar**);' + \
'g_setenv("GST_REGISTRY_1.0", "@0@", TRUE);'.format(meson.current_build_dir() + '/gir_empty_registry.reg') + \
'g_setenv("GST_PLUGIN_PATH_1_0", "", TRUE);' + \
'g_setenv("GST_PLUGIN_SYSTEM_PATH_1_0", "", TRUE);' + \
'gst_init(NULL,NULL);', '--quiet'
]
gst_clapper_plugin_libdir = join_paths(get_option('prefix'), libdir, 'clapper-@0@'.format(api_version), 'gst', 'plugin')
gst_clapper_importers_libdir = join_paths(gst_clapper_plugin_libdir, 'importers')
cdata.set_quoted('CLAPPER_SINK_IMPORTER_PATH', gst_clapper_importers_libdir)
subdir('gst')
configure_file(output: 'config.h', configuration: cdata)

View File

@@ -1,39 +1,147 @@
project('com.github.rafostar.Clapper', 'c', 'cpp',
project('clapper', 'c',
version: '0.5.2',
meson_version: '>= 0.50.0',
license: 'GPL-3.0-or-later',
meson_version: '>= 0.64.0',
license: 'LGPL-2.1-or-later AND GPL-3.0-or-later', # LGPL-2.1+ for libs and gst-plugin, GPL-3.0+ for app
default_options: [
'warning_level=1',
'buildtype=debugoptimized'
]
'buildtype=debugoptimized',
],
)
glib_req = '>= 2.76.0'
gst_req = '>= 1.20.0'
gtk4_req = '>= 4.10.0'
adw_req = '>= 1.4.0'
clapper_version = meson.project_version().split('-')[0]
version_array = clapper_version.split('.')
clapper_version_suffix = '-' + version_array[0] + '.0'
clapper_api_name = meson.project_name() + clapper_version_suffix
gnome = import('gnome')
pkgconfig = import('pkgconfig')
i18n = import('i18n')
python = import('python')
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'))
prefix = get_option('prefix')
bindir = get_option('bindir')
datadir = get_option('datadir')
libdir = get_option('libdir')
localedir = get_option('localedir')
includedir = get_option('includedir')
optimization = get_option('optimization')
pkglibdir = join_paths(libdir, meson.project_name())
pkgdatadir = join_paths(datadir, meson.project_name())
clapper_libdir = join_paths(prefix, libdir, clapper_api_name)
build_optimized = optimization in ['2', '3', 's']
subdir('lib')
gst_dep = dependency('gstreamer-1.0',
version: gst_req,
required: false,
)
gst_base_dep = dependency('gstreamer-base-1.0',
version: gst_req,
required: false,
)
gst_video_dep = dependency('gstreamer-video-1.0',
version: gst_req,
required: false,
)
gst_audio_dep = dependency('gstreamer-audio-1.0',
version: gst_req,
required: false,
)
gst_pbutils_dep = dependency('gstreamer-pbutils-1.0',
version: gst_req,
required: false,
)
gst_tag_dep = dependency('gstreamer-tag-1.0',
version: gst_req,
required: false,
)
glib_dep = dependency('glib-2.0',
version: glib_req,
required: false,
)
gobject_dep = dependency('gobject-2.0',
version: glib_req,
required: false,
)
gio_dep = dependency('gio-2.0',
version: glib_req,
required: false,
)
gmodule_dep = dependency('gmodule-2.0',
version: glib_req,
required: false,
)
gtk4_dep = dependency('gtk4',
version: gtk4_req,
required: false,
)
libadwaita_dep = dependency('libadwaita-1',
version: adw_req,
required: false,
)
if get_option('player')
subdir('bin')
subdir('data')
subdir('po')
cc = meson.get_compiler('c')
libm = cc.find_library('m', required: false)
install_subdir('src', install_dir: pkgdatadir)
install_subdir('extras', install_dir: pkgdatadir)
install_subdir('css', install_dir: pkgdatadir)
install_subdir('ui', install_dir: pkgdatadir)
warning_flags = [
'-Wmissing-declarations',
'-Wredundant-decls',
'-Wwrite-strings',
'-Wformat',
'-Wformat-security',
'-Winit-self',
'-Wmissing-include-dirs',
'-Waddress',
'-Wno-multichar',
'-Wvla',
'-Wpointer-arith',
'-Wmissing-prototypes',
'-Wdeclaration-after-statement',
'-Wold-style-definition',
'-Wsign-compare',
]
python_bin = python.find_installation('python3')
if not python_bin.found()
error('No valid python3 binary found')
foreach extra_arg : warning_flags
if cc.has_argument (extra_arg)
add_project_arguments([extra_arg], language: 'c')
endif
meson.add_install_script('build-aux/meson/postinstall.py')
endforeach
if build_optimized
message('Disabling GLib cast checks')
add_project_arguments('-DG_DISABLE_CAST_CHECKS', language: 'c')
message('Disabling GLib asserts')
add_project_arguments('-DG_DISABLE_ASSERT', language: 'c')
message('Disabling GLib checks')
add_project_arguments('-DG_DISABLE_CHECKS', language: 'c')
endif
subdir('src')
subdir('doc')
summary({
'prefix': prefix,
'bindir': bindir,
'datadir': datadir,
'libdir': libdir,
'localedir': localedir,
'includedir': includedir,
'optimization': optimization,
}, section: 'Directories')
summary('clapper', build_clapper ? 'Yes' : 'No', section: 'Build')
summary('clapper-gtk', build_clappergtk ? 'Yes' : 'No', section: 'Build')
summary('clapper-app', build_clapperapp ? 'Yes' : 'No', section: 'Build')
summary('gst-plugin', build_gst_plugin ? 'Yes' : 'No', section: 'Build')
summary('introspection', build_gir ? 'Yes' : 'No', section: 'Build')
summary('vapi', build_vapi ? 'Yes' : 'No', section: 'Build')
summary('doc', build_doc ? 'Yes' : 'No', section: 'Build')
foreach name : clapper_possible_features
summary(name, clapper_available_features.contains(name) ? 'Yes' : 'No', section: 'Features')
endforeach

View File

@@ -1,19 +1,58 @@
option('player',
type: 'boolean',
value: true,
description: 'Build Clapper player'
# Build
option('clapper',
type: 'feature',
value: 'auto',
description: 'Build Clapper library'
)
option('lib',
type: 'boolean',
value: true,
description: 'Build GstClapper lib'
option('clapper-gtk',
type: 'feature',
value: 'auto',
description: 'Build Clapper GTK integration library'
)
option('clapper-app',
type: 'feature',
value: 'auto',
description: 'Build Clapper application'
)
option('gst-plugin',
type: 'feature',
value: 'auto',
description: 'Build GStreamer plugin (includes GTK video sink element)'
)
option('introspection',
type: 'feature',
value: 'auto',
description: 'Build GObject Introspection data'
)
option('vapi',
type: 'feature',
value: 'auto',
description: 'Build Vala bindings'
)
option('doc',
type: 'boolean',
value: false,
description: 'Build documentation'
)
# Features
option('discoverer',
type: 'feature',
value: 'auto',
description: 'Build Clapper Discoverer feature'
)
option('mpris',
type: 'feature',
value: 'auto',
description: 'Build Clapper MPRIS feature'
)
option('server',
type: 'feature',
value: 'auto',
description: 'Build Clapper Server feature'
)
# GStreamer plugin options
option('glimporter',
type: 'feature',
value: 'auto',
@@ -29,14 +68,3 @@ option('rawimporter',
value: 'auto',
description: 'Build RAW system memory importer for clappersink'
)
option('devel-checks',
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,
description: 'Allow using of deprecated GLib API'
)

View File

@@ -31,7 +31,6 @@
"--filesystem=xdg-run/gvfsd",
"--own-name=org.mpris.MediaPlayer2.Clapper",
"--talk-name=org.gtk.vfs.*",
"--talk-name=org.gnome.Shell",
"--env=GST_PLUGIN_SYSTEM_PATH=/app/lib/gstreamer-1.0"
],
"build-options": {
@@ -40,7 +39,7 @@
},
"modules": [
"flathub/shared-modules/gudev/gudev.json",
"flathub/lib/liba52.json",
"testing/liba52.json",
"flathub/lib/libmpeg2.json",
"flathub/lib/libdv.json",
"flathub/lib/libdvdcss.json",

View File

@@ -1,9 +1,18 @@
{
"app-id": "com.github.rafostar.Clapper",
"runtime": "org.gnome.Platform",
"runtime-version": "43",
"runtime-version": "45",
"sdk": "org.gnome.Sdk",
"command": "com.github.rafostar.Clapper",
"add-extensions": {
"org.freedesktop.Platform.ffmpeg-full": {
"version": "23.08",
"directory": "lib/ffmpeg",
"add-ld-path": ".",
"no-autodownload": false,
"autodelete": false
}
},
"command": "clapper",
"separate-locales": false,
"finish-args": [
"--share=ipc",
@@ -18,24 +27,20 @@
"--filesystem=xdg-run/gvfsd",
"--own-name=org.mpris.MediaPlayer2.Clapper",
"--talk-name=org.gtk.vfs.*",
"--talk-name=org.gnome.Shell",
"--env=GST_PLUGIN_SYSTEM_PATH=/app/lib/gstreamer-1.0"
],
"modules": [
"flathub/shared-modules/gudev/gudev.json",
"flathub/lib/libsass.json",
"flathub/lib/sassc.json",
"flathub/lib/liba52.json",
"testing/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/libmicrodns.json",
"flathub/gstreamer-1.0/gstreamer.json",
"flathub/lib/libadwaita.json",
"testing/gtuber.json",
{
"name": "clapper",
@@ -52,6 +57,7 @@
}
],
"cleanup-commands": [
"mkdir -p /app/lib/ffmpeg",
"ln -s /lib/$FLATPAK_ARCH-linux-*/gstreamer-1.0/libgstpipewire.so /app/lib/gstreamer-1.0/"
]
}

View File

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

View File

@@ -0,0 +1,24 @@
{
"name": "liba52",
"config-opts": [ "--enable-shared", "--disable-static" ],
"rm-configure": true,
"cleanup": [ "/bin/*a52*" ],
"sources": [
{
"type": "archive",
"url": "https://distfiles.adelielinux.org/source/a52dec/a52dec-0.8.0.tar.gz",
"sha256": "03c181ce9c3fe0d2f5130de18dab9bd8bc63c354071515aa56983c74a9cffcc9"
},
{
"type": "patch",
"path": "a52dec-configure-optflags.patch"
},
{
"type": "script",
"commands": [
"autoreconf -fiv"
],
"dest-filename": "autogen.sh"
}
]
}

View File

@@ -0,0 +1,21 @@
{
"name": "libmicrodns",
"buildsystem": "meson",
"config-opts": [
"--buildtype=release",
"-Dtests=disabled",
"-Dexamples=disabled"
],
"cleanup": [
"/include",
"/lib/pkgconfig"
],
"sources": [
{
"type": "git",
"url": "https://github.com/videolabs/libmicrodns.git",
"tag": "0.2.0",
"commit": "deb7ed7bf05dc26802a0ca1987049b31405b8930"
}
]
}

View File

@@ -1,111 +0,0 @@
const { Gtk } = imports.gi;
const Dialogs = imports.src.dialogs;
const Prefs = imports.src.prefs;
const Misc = imports.src.misc;
var actions = {
open_local: ['<Ctrl>O'],
export_playlist: ['<Ctrl>E'],
open_uri: ['<Ctrl>U'],
prefs: ['<Ctrl>comma'],
shortcuts: ['F1', '<Ctrl>question'],
about: null,
progress_forward: ['Right'],
progress_backward: ['Left'],
next_chapter: ['<Shift>Right'],
prev_chapter: ['<Shift>Left'],
next_track: ['<Ctrl>Right'],
prev_track: ['<Ctrl>Left'],
volume_up: ['Up'],
volume_down: ['Down'],
mute: ['<Ctrl>M', 'M'],
toggle_play: ['space'],
change_repeat: ['<Ctrl>R'],
reveal_controls: ['Return'],
toggle_fullscreen: ['F11', 'F'],
leave_fullscreen: ['Escape'],
quit: ['<Ctrl>Q', 'Q'],
};
function handleAction(action, window)
{
const clapperWidget = window.child;
if(!clapperWidget) return;
const { player } = clapperWidget;
let bool = false;
switch(action.name) {
case 'open_local':
case 'export_playlist':
new Dialogs.FileChooser(window, action.name);
break;
case 'open_uri':
new Dialogs.UriDialog(window);
break;
case 'prefs':
new Prefs.PrefsWindow(window);
break;
case 'shortcuts':
if(!window.get_help_overlay()) {
const helpBuilder = Misc.getBuilderForName('help-overlay.ui');
window.set_help_overlay(helpBuilder.get_object('help_overlay'));
}
clapperWidget.activate_action('win.show-help-overlay', null);
break;
case 'about':
new Dialogs.AboutDialog(window);
break;
case 'progress_forward':
bool = true;
case 'progress_backward':
player.adjust_position(bool);
if(
clapperWidget.isReleaseKeyEnabled
&& clapperWidget.isFullscreenMode
)
clapperWidget.revealControls();
/* Actual seek is handled on release */
clapperWidget.isReleaseKeyEnabled = true;
if(!clapperWidget.has_focus)
clapperWidget.grab_focus();
break;
case 'volume_up':
bool = true;
case 'volume_down':
player.adjust_volume(bool);
break;
case 'mute':
player.mute ^= true;
break;
case 'next_track':
player.playlistWidget.nextTrack();
break;
case 'prev_track':
player.playlistWidget.prevTrack();
break;
case 'reveal_controls':
if(clapperWidget.isFullscreenMode)
clapperWidget.revealControls();
break;
case 'leave_fullscreen':
if(!clapperWidget.isFullscreenMode)
break;
case 'toggle_fullscreen':
clapperWidget.toggleFullscreen();
break;
case 'change_repeat':
player.playlistWidget.changeRepeatMode();
break;
case 'quit':
clapperWidget.activate_action('window.close', null);
break;
case 'toggle_play':
case 'next_chapter':
case 'prev_chapter':
player[action.name]();
break;
default:
break;
}
}

View File

@@ -1,126 +0,0 @@
const { Gio, GObject, Gtk } = imports.gi;
const { Widget } = imports.src.widget;
const Debug = imports.src.debug;
const FileOps = imports.src.fileOps;
const Misc = imports.src.misc;
const Actions = imports.src.actions;
const { debug } = Debug;
const { settings } = Misc;
var App = GObject.registerClass({
GTypeName: 'ClapperApp',
},
class ClapperApp extends Gtk.Application
{
_init()
{
super._init({
application_id: Misc.appId,
flags: Gio.ApplicationFlags.HANDLES_OPEN,
});
this.doneFirstActivate = false;
this.isFileAppend = false;
this.mapSignal = null;
}
vfunc_open(files, hint)
{
super.vfunc_open(files, hint);
this.activate();
this._openFilesAsync(files).catch(debug);
}
vfunc_activate()
{
super.vfunc_activate();
if(!this.doneFirstActivate)
this._onFirstActivate();
this.active_window.present();
}
async _openFilesAsync(files)
{
const urisArr = [];
for(let file of files) {
const uri = file.get_uri();
if(!uri.startsWith('file:')) {
urisArr.push(uri);
continue;
}
/* If file is not a dir its URI will be returned in an array */
const uris = await FileOps.getDirFilesUrisPromise(file).catch(debug);
if(uris && uris.length)
urisArr.push(...uris);
}
const [playlist, subs] = Misc.parsePlaylistFiles(urisArr);
const { player } = this.active_window.get_child();
const action = (this.isFileAppend) ? 'append' : 'set';
if(playlist && playlist.length)
player[`${action}_playlist`](playlist);
if(subs)
player.set_subtitles(subs);
/* Restore default behavior */
this.isFileAppend = false;
}
_onFirstActivate()
{
const window = new Gtk.ApplicationWindow({
application: this,
title: Misc.appName,
});
window.add_css_class('adwrounded');
if(!settings.get_boolean('render-shadows'))
window.add_css_class('gpufriendly');
window.add_css_class('gpufriendlyfs');
const clapperWidget = new Widget();
const dummyHeaderbar = new Gtk.Box({
can_focus: false,
focusable: false,
visible: false,
});
window.add_css_class('nobackground');
window.set_child(clapperWidget);
window.set_titlebar(dummyHeaderbar);
for(let name in Actions.actions) {
const simpleAction = new Gio.SimpleAction({ name });
simpleAction.connect('activate', (action) =>
Actions.handleAction(action, window)
);
this.add_action(simpleAction);
const accels = Actions.actions[name];
if(accels)
this.set_accels_for_action(`app.${name}`, accels);
}
this.mapSignal = window.connect('map', this._onWindowMap.bind(this));
this.doneFirstActivate = true;
}
_onWindowMap(window)
{
window.disconnect(this.mapSignal);
this.mapSignal = null;
debug('window mapped');
window.child._onWindowMap(window);
}
});

View File

@@ -0,0 +1,69 @@
/* Clapper Application
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <glib/gi18n.h>
#include <gst/gst.h>
#include <adwaita.h>
#include "clapper-app-about-window.h"
GtkWidget *
clapper_app_about_window_new (GtkApplication *gtk_app)
{
AdwAboutWindow *about;
GtkWindow *window;
GString *string;
gchar *gst_ver, *debug_info;
about = ADW_ABOUT_WINDOW (adw_about_window_new_from_appdata (
CLAPPER_APP_RESOURCE_PREFIX "/data/metainfo/" CLAPPER_APP_ID ".metainfo.xml",
NULL));
window = gtk_application_get_active_window (gtk_app);
gtk_window_set_modal (GTK_WINDOW (about), TRUE);
gtk_window_set_transient_for (GTK_WINDOW (about), window);
/* TRANSLATORS: Put your name(s) here for credits or leave untranslated */
adw_about_window_set_translator_credits (about, _("translator-credits"));
string = g_string_new (NULL);
g_string_append_printf (string, "GLib %u.%u.%u\n",
glib_major_version,
glib_minor_version,
glib_micro_version);
g_string_append_printf (string, "GTK %u.%u.%u\n",
gtk_get_major_version (),
gtk_get_minor_version (),
gtk_get_micro_version ());
g_string_append_printf (string, "Adwaita %u.%u.%u\n",
adw_get_major_version (),
adw_get_minor_version (),
adw_get_micro_version ());
gst_ver = gst_version_string ();
g_string_append (string, gst_ver);
g_free (gst_ver);
debug_info = g_string_free_and_steal (string);
adw_about_window_set_debug_info (about, debug_info);
g_free (debug_info);
return GTK_WIDGET (about);
}

View File

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

View File

@@ -0,0 +1,585 @@
/* Clapper Application
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <math.h>
#include <gst/gst.h>
#include <clapper-gtk/clapper-gtk.h>
#include "clapper-app-application.h"
#include "clapper-app-window.h"
#include "clapper-app-file-dialog.h"
#include "clapper-app-uri-dialog.h"
#include "clapper-app-info-window.h"
#include "clapper-app-preferences-window.h"
#include "clapper-app-about-window.h"
#include "clapper-app-utils.h"
#define PERCENTAGE_ROUND(a) (round ((gdouble) a / 0.01) * 0.01)
#define GST_CAT_DEFAULT clapper_app_application_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperAppApplication
{
GtkApplication parent;
GSettings *settings;
gboolean need_init_state;
};
struct ClapperPluginFeatureData
{
const gchar *name;
GstRank rank;
};
struct ClapperPluginData
{
const gchar *name;
guint skip_version[3];
struct ClapperPluginFeatureData features[10];
};
typedef struct
{
const gchar *action;
const gchar *accels[3];
} ClapperAppShortcut;
#define parent_class clapper_app_application_parent_class
G_DEFINE_TYPE (ClapperAppApplication, clapper_app_application, GTK_TYPE_APPLICATION);
static inline void
_set_initial_plugin_feature_ranks (void)
{
GstRegistry *registry = gst_registry_get ();
guint i;
const struct ClapperPluginData plugins_data[] = {
{
.name = "va",
.skip_version = { 1, 24, 0 },
.features = {
{ "vah264dec", GST_RANK_PRIMARY + 24 },
{ "vah265dec", GST_RANK_PRIMARY + 24 },
{ "vavp8dec", GST_RANK_PRIMARY + 24 },
{ "vavp9dec", GST_RANK_PRIMARY + 24 },
{ "vaav1dec", GST_RANK_PRIMARY + 24 },
{ NULL, 0 }
}
},
{
.name = "nvcodec",
.skip_version = { 1, 24, 0 },
.features = {
{ "nvh264dec", GST_RANK_PRIMARY + 28 },
{ "nvh265dec", GST_RANK_PRIMARY + 28 },
{ "nvvp8dec", GST_RANK_PRIMARY + 28 },
{ "nvvp9dec", GST_RANK_PRIMARY + 28 },
{ "nvav1dec", GST_RANK_PRIMARY + 28 },
{ NULL, 0 }
}
}
};
for (i = 0; i < G_N_ELEMENTS (plugins_data); ++i) {
GList *features;
if (!(features = gst_registry_get_feature_list_by_plugin (
registry, plugins_data[i].name)))
continue;
if (g_list_length (features) > 0) {
guint j;
for (j = 0; G_N_ELEMENTS (plugins_data[i].features); ++j) {
GstPluginFeature *feature;
if (!plugins_data[i].features[j].name)
break;
if (!(feature = gst_registry_lookup_feature (registry,
plugins_data[i].features[j].name)))
continue;
if (!gst_plugin_feature_check_version (feature,
plugins_data[i].skip_version[0],
plugins_data[i].skip_version[1],
plugins_data[i].skip_version[2])) {
gst_plugin_feature_set_rank (feature,
plugins_data[i].features[j].rank);
GST_DEBUG ("Initially set \"%s\" rank to: %i",
plugins_data[i].features[j].name,
plugins_data[i].features[j].rank);
}
gst_object_unref (feature);
}
}
gst_plugin_feature_list_free (features);
}
}
static void
_iter_ranks_func (const gchar *feature_name, GstRank rank,
gboolean from_env, gpointer user_data G_GNUC_UNUSED)
{
GstPluginFeature *feature;
if ((feature = gst_registry_find_feature (gst_registry_get (),
feature_name, GST_TYPE_ELEMENT_FACTORY))) {
gst_plugin_feature_set_rank (feature, rank);
GST_INFO ("Set \"%s\" rank to: %i", feature_name, rank);
gst_object_unref (feature);
}
}
static void
plugin_feature_ranks_settings_changed_cb (GSettings *settings,
gchar *key G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED)
{
clapper_app_utils_iterate_plugin_feature_ranks (settings,
(ClapperAppUtilsIterRanks) _iter_ranks_func, NULL);
}
static void
_assemble_initial_state (GtkWindow *window)
{
GtkWidget *stack = gtk_window_get_child (window);
GtkBuilder *builder = gtk_builder_new_from_resource (
CLAPPER_APP_RESOURCE_PREFIX "/ui/clapper-app-initial-state.ui");
GtkWidget *initial_state = GTK_WIDGET (gtk_builder_get_object (builder, "initial_state"));
gtk_stack_add_named (GTK_STACK (stack), initial_state, "initial_state");
gtk_stack_set_visible_child (GTK_STACK (stack), initial_state);
g_object_unref (builder);
}
static void
add_files (GSimpleAction *action, GVariant *param, gpointer user_data)
{
GtkApplication *gtk_app = GTK_APPLICATION (user_data);
clapper_app_file_dialog_open_files (gtk_app);
}
static void
add_uri (GSimpleAction *action, GVariant *param, gpointer user_data)
{
GtkApplication *gtk_app = GTK_APPLICATION (user_data);
clapper_app_uri_dialog_open_uri (gtk_app);
}
static void
show_preferences (GSimpleAction *action, GVariant *param, gpointer user_data)
{
GtkApplication *gtk_app = GTK_APPLICATION (user_data);
GtkWidget *preferences_window;
preferences_window = clapper_app_preferences_window_new (gtk_app);
gtk_window_present (GTK_WINDOW (preferences_window));
}
static void
show_info (GSimpleAction *action, GVariant *param, gpointer user_data)
{
GtkApplication *gtk_app = GTK_APPLICATION (user_data);
GtkWidget *info_window;
GtkWindow *window;
ClapperPlayer *player;
window = gtk_application_get_active_window (gtk_app);
player = clapper_app_window_get_player (CLAPPER_APP_WINDOW (window));
info_window = clapper_app_info_window_new (gtk_app, player);
gtk_window_present (GTK_WINDOW (info_window));
}
static void
show_about (GSimpleAction *action, GVariant *param, gpointer user_data)
{
GtkApplication *gtk_app = GTK_APPLICATION (user_data);
GtkWidget *about_window;
about_window = clapper_app_about_window_new (gtk_app);
gtk_window_present (GTK_WINDOW (about_window));
}
static inline void
_restore_settings_to_window (ClapperAppApplication *self, ClapperAppWindow *app_window)
{
ClapperPlayer *player = clapper_app_window_get_player (app_window);
ClapperQueue *queue = clapper_player_get_queue (player);
GST_DEBUG ("Restoring saved GSettings values to: %" GST_PTR_FORMAT, app_window);
clapper_player_set_volume (player, PERCENTAGE_ROUND (g_settings_get_double (self->settings, "volume")));
clapper_player_set_mute (player, g_settings_get_boolean (self->settings, "mute"));
clapper_player_set_speed (player, PERCENTAGE_ROUND (g_settings_get_double (self->settings, "speed")));
clapper_player_set_subtitles_enabled (player, g_settings_get_boolean (self->settings, "subtitles-enabled"));
clapper_queue_set_progression_mode (queue, g_settings_get_int (self->settings, "progression-mode"));
if (g_settings_get_boolean (self->settings, "fullscreened"))
gtk_window_fullscreen (GTK_WINDOW (app_window));
else if (g_settings_get_boolean (self->settings, "maximized"))
gtk_window_maximize (GTK_WINDOW (app_window));
GST_DEBUG ("Configuration restored");
}
static inline void
_store_settings_from_window (ClapperAppApplication *self, ClapperAppWindow *app_window)
{
ClapperPlayer *player = clapper_app_window_get_player (app_window);
ClapperQueue *queue = clapper_player_get_queue (player);
GtkWindow *window = GTK_WINDOW (app_window);
GST_DEBUG ("Storing current configuration to GSettings");
g_settings_set_double (self->settings, "volume", clapper_player_get_volume (player));
g_settings_set_boolean (self->settings, "mute", clapper_player_get_mute (player));
g_settings_set_double (self->settings, "speed", clapper_player_get_speed (player));
g_settings_set_boolean (self->settings, "subtitles-enabled", clapper_player_get_subtitles_enabled (player));
g_settings_set_int (self->settings, "progression-mode", clapper_queue_get_progression_mode (queue));
g_settings_set_boolean (self->settings, "maximized", gtk_window_is_maximized (window));
g_settings_set_boolean (self->settings, "fullscreened", gtk_window_is_fullscreen (window));
GST_DEBUG ("Configuration stored");
}
GApplication *
clapper_app_application_new (void)
{
return g_object_new (CLAPPER_APP_TYPE_APPLICATION,
"application-id", CLAPPER_APP_ID,
"flags", G_APPLICATION_HANDLES_OPEN,
NULL);
}
static void
clapper_app_application_window_removed (GtkApplication *gtk_app, GtkWindow *window)
{
ClapperAppApplication *self = CLAPPER_APP_APPLICATION_CAST (gtk_app);
if (CLAPPER_APP_IS_WINDOW (window)) {
GList *win, *windows = gtk_application_get_windows (gtk_app);
gboolean has_player_windows = FALSE;
for (win = windows; win != NULL; win = win->next) {
GtkWindow *rem_window = GTK_WINDOW (win->data);
if ((has_player_windows = (rem_window != window
&& CLAPPER_APP_IS_WINDOW (rem_window))))
break;
}
/* Last player window is closing, time to store settings */
if (!has_player_windows)
_store_settings_from_window (self, CLAPPER_APP_WINDOW_CAST (window));
}
GTK_APPLICATION_CLASS (parent_class)->window_removed (gtk_app, window);
}
static void
clapper_app_application_activate (GApplication *app)
{
ClapperAppApplication *self = CLAPPER_APP_APPLICATION_CAST (app);
GtkWindow *window;
GST_INFO ("Activate");
G_APPLICATION_CLASS (parent_class)->activate (app);
if (!(window = gtk_application_get_active_window (GTK_APPLICATION (app)))) {
window = GTK_WINDOW (clapper_app_window_new (GTK_APPLICATION (app)));
_restore_settings_to_window (self, CLAPPER_APP_WINDOW_CAST (window));
}
if (self->need_init_state) {
_assemble_initial_state (window);
self->need_init_state = FALSE;
}
gtk_window_present (window);
}
static gboolean
clapper_app_application_local_command_line (GApplication *app,
gchar ***arguments, gint *exit_status)
{
gchar **argv = *arguments;
guint i;
/* NOTE: argv is never NULL, so no need to check */
for (i = 0; argv[i]; ++i) {
/* Handle "-" special case as URI */
if (strlen (argv[i]) == 1 && argv[i][0] == '-') {
g_free (argv[i]);
argv[i] = g_strdup ("fd://0");
}
}
return G_APPLICATION_CLASS (parent_class)->local_command_line (app, arguments, exit_status);
}
static gboolean
_is_claps_file (GFile *file)
{
gchar *basename = g_file_get_basename (file);
gboolean is_claps;
is_claps = (basename && g_str_has_suffix (basename, ".claps"));
g_free (basename);
return is_claps;
}
static void
add_item_from_file (GFile *file, ClapperQueue *queue)
{
ClapperMediaItem *item = clapper_media_item_new_from_file (file);
GST_DEBUG ("Adding media item with URI: %s",
clapper_media_item_get_uri (item));
clapper_queue_add_item (queue, item);
gst_object_unref (item);
}
static void
add_items_from_claps_file (GFile *file, ClapperQueue *queue)
{
GDataInputStream *dstream = NULL;
GFileInputStream *stream;
GError *error = NULL;
gchar *line;
if (!(stream = g_file_read (file, NULL, &error)))
goto finish;
dstream = g_data_input_stream_new (G_INPUT_STREAM (stream));
while ((line = g_data_input_stream_read_line (
dstream, NULL, NULL, &error))) {
g_strstrip (line);
if (strlen (line) > 0) {
GFile *tmp_file = gst_uri_is_valid (line)
? g_file_new_for_uri (line)
: g_file_new_for_path (line);
if (_is_claps_file (tmp_file))
add_items_from_claps_file (tmp_file, queue);
else
add_item_from_file (tmp_file, queue);
g_object_unref (tmp_file);
}
g_free (line);
}
finish:
if (error) {
GST_ERROR ("Could not read \".claps\" file, reason: %s", error->message);
g_error_free (error);
}
if (stream) {
g_input_stream_close (G_INPUT_STREAM (stream), NULL, NULL);
g_object_unref (stream);
}
g_clear_object (&dstream);
}
static void
add_item_with_subtitles (GFile *media_file,
GFile *subs_file, ClapperQueue *queue)
{
ClapperMediaItem *item = clapper_media_item_new_from_file (media_file);
gchar *suburi = g_file_get_uri (subs_file);
GST_DEBUG ("Adding media item with URI: %s, SUBURI: %s",
clapper_media_item_get_uri (item), GST_STR_NULL (suburi));
clapper_media_item_set_suburi (item, suburi);
clapper_queue_add_item (queue, item);
gst_object_unref (item);
g_free (suburi);
}
static void
clapper_app_application_open (GApplication *app,
GFile **files, gint n_files, const gchar *hint)
{
ClapperAppApplication *self = CLAPPER_APP_APPLICATION_CAST (app);
GtkWindow *window;
ClapperPlayer *player;
ClapperQueue *queue;
guint n_before;
gboolean add_only, handled = FALSE;
GST_INFO ("Open");
/* Since we startup with media,
* no need to show initial state */
self->need_init_state = FALSE;
g_application_activate (app);
g_application_mark_busy (app);
window = gtk_application_get_active_window (GTK_APPLICATION (app));
while (window && !CLAPPER_APP_IS_WINDOW (window))
window = gtk_window_get_transient_for (window);
clapper_app_window_ensure_no_initial_state (CLAPPER_APP_WINDOW (window));
player = clapper_app_window_get_player (CLAPPER_APP_WINDOW (window));
queue = clapper_player_get_queue (player);
n_before = clapper_queue_get_n_items (queue);
/* Special path for opening video with subtitles at once */
if (n_files == 2) {
gboolean first_subs, second_subs;
first_subs = clapper_app_utils_is_subtitles_file (files[0]);
second_subs = clapper_app_utils_is_subtitles_file (files[1]);
if ((handled = first_subs != second_subs)) {
guint media_index, subs_index;
media_index = (second_subs) ? 0 : 1;
subs_index = (media_index + 1) % 2;
add_item_with_subtitles (
files[media_index], files[subs_index], queue);
}
}
if (!handled) {
gint i;
for (i = 0; i < n_files; ++i) {
if (_is_claps_file (files[i]))
add_items_from_claps_file (files[i], queue);
else
add_item_from_file (files[i], queue);
}
}
add_only = (g_strcmp0 (hint, "add-only") == 0);
/* Select first thing from added item to play (behave like "open" should),
* when queue was empty first item is automatically selected */
if (!add_only && n_before > 0)
clapper_queue_select_index (queue, n_before);
g_application_unmark_busy (app);
}
static void
clapper_app_application_init (ClapperAppApplication *self)
{
self->need_init_state = TRUE;
}
static void
clapper_app_application_constructed (GObject *object)
{
ClapperAppApplication *self = CLAPPER_APP_APPLICATION_CAST (object);
GApplication *app = G_APPLICATION (self);
guint i;
static const GActionEntry app_entries[] = {
{ "add-files", add_files, NULL, NULL, NULL },
{ "add-uri", add_uri, NULL, NULL, NULL },
{ "info", show_info, NULL, NULL, NULL },
{ "preferences", show_preferences, NULL, NULL, NULL },
{ "about", show_about, NULL, NULL, NULL },
};
static const ClapperAppShortcut app_shortcuts[] = {
{ "app.add-files", { "<Control>o", NULL, NULL }},
{ "app.add-uri", { "<Control>u", NULL, NULL }},
{ "app.info", { "<Control>i", NULL, NULL }},
{ "app.preferences", { "<Control>comma", NULL, NULL }},
{ "app.about", { "F1", NULL, NULL }},
{ "win.toggle-fullscreen", { "F11", "f", NULL }},
{ "win.show-help-overlay", { "<Control>question", NULL, NULL }},
{ "window.close", { "<Control>q", NULL, NULL }},
};
/* Override initial ranks, they will be updated
* from both stored settings and env below */
_set_initial_plugin_feature_ranks ();
self->settings = g_settings_new (CLAPPER_APP_ID);
g_signal_connect (self->settings,
"changed::plugin-feature-ranks",
G_CALLBACK (plugin_feature_ranks_settings_changed_cb), self);
plugin_feature_ranks_settings_changed_cb (self->settings, NULL, NULL);
g_action_map_add_action_entries (G_ACTION_MAP (app),
app_entries, G_N_ELEMENTS (app_entries), app);
for (i = 0; i < G_N_ELEMENTS (app_shortcuts); ++i)
gtk_application_set_accels_for_action (GTK_APPLICATION (app), app_shortcuts[i].action, app_shortcuts[i].accels);
g_application_add_option_group (app, gst_init_get_option_group ());
G_OBJECT_CLASS (parent_class)->constructed (object);
}
static void
clapper_app_application_finalize (GObject *object)
{
ClapperAppApplication *self = CLAPPER_APP_APPLICATION_CAST (object);
GST_TRACE ("Finalize");
g_object_unref (self->settings);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
clapper_app_application_class_init (ClapperAppApplicationClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GApplicationClass *application_class = (GApplicationClass *) klass;
GtkApplicationClass *gtk_application_class = (GtkApplicationClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperappapplication", 0,
"Clapper App Application");
gobject_class->constructed = clapper_app_application_constructed;
gobject_class->finalize = clapper_app_application_finalize;
gtk_application_class->window_removed = clapper_app_application_window_removed;
application_class->activate = clapper_app_application_activate;
application_class->local_command_line = clapper_app_application_local_command_line;
application_class->open = clapper_app_application_open;
}

View File

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

View File

@@ -0,0 +1,131 @@
/* Clapper Application
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <gio/gio.h>
#include "clapper-app-file-dialog.h"
#include "clapper-app-utils.h"
static inline void
_open_files_from_model (GtkApplication *gtk_app, GListModel *files_model)
{
GFile **files = NULL;
gint n_files = 0;
if (clapper_app_utils_files_from_list_model (files_model, &files, &n_files)) {
g_application_open (G_APPLICATION (gtk_app), files, n_files, "add-only");
clapper_app_utils_files_free (files);
}
}
static void
_open_files_cb (GtkFileDialog *dialog, GAsyncResult *result, GtkApplication *gtk_app)
{
GError *error = NULL;
GListModel *files_model = gtk_file_dialog_open_multiple_finish (dialog, result, &error);
if (G_LIKELY (error == NULL)) {
_open_files_from_model (gtk_app, files_model);
} else {
if (error->domain != GTK_DIALOG_ERROR || error->code != GTK_DIALOG_ERROR_DISMISSED) {
g_printerr ("Error: %s\n",
(error->message) ? error->message : "Could not open file dialog");
}
g_error_free (error);
}
g_clear_object (&files_model);
}
static void
_open_subtitles_cb (GtkFileDialog *dialog, GAsyncResult *result, ClapperMediaItem *item)
{
GError *error = NULL;
GFile *file = gtk_file_dialog_open_finish (dialog, result, &error);
if (G_LIKELY (error == NULL)) {
gchar *suburi = g_file_get_uri (file);
clapper_media_item_set_suburi (item, suburi);
g_free (suburi);
} else {
if (error->domain != GTK_DIALOG_ERROR || error->code != GTK_DIALOG_ERROR_DISMISSED) {
g_printerr ("Error: %s\n",
(error->message) ? error->message : "Could not open file dialog");
}
g_error_free (error);
}
g_clear_object (&file);
gst_object_unref (item); // Borrowed reference
}
static void
_dialog_add_mime_types (GtkFileDialog *dialog, const gchar *filter_name,
const gchar *const *mime_types)
{
GListStore *filters = g_list_store_new (GTK_TYPE_FILE_FILTER);
GtkFileFilter *filter = gtk_file_filter_new ();
guint i;
for (i = 0; mime_types[i]; ++i)
gtk_file_filter_add_mime_type (filter, mime_types[i]);
gtk_file_filter_set_name (filter, filter_name);
g_list_store_append (filters, filter);
gtk_file_dialog_set_filters (dialog, G_LIST_MODEL (filters));
g_object_unref (filters);
g_object_unref (filter);
}
void
clapper_app_file_dialog_open_files (GtkApplication *gtk_app)
{
GtkWindow *window = gtk_application_get_active_window (gtk_app);
GtkFileDialog *dialog = gtk_file_dialog_new ();
_dialog_add_mime_types (dialog, "Media Files",
clapper_app_utils_get_mime_types ());
gtk_file_dialog_set_modal (dialog, TRUE);
gtk_file_dialog_set_title (dialog, "Add Files");
gtk_file_dialog_open_multiple (dialog, window, NULL,
(GAsyncReadyCallback) _open_files_cb,
gtk_app);
g_object_unref (dialog);
}
void
clapper_app_file_dialog_open_subtitles (GtkApplication *gtk_app, ClapperMediaItem *item)
{
GtkWindow *window = gtk_application_get_active_window (gtk_app);
GtkFileDialog *dialog = gtk_file_dialog_new ();
_dialog_add_mime_types (dialog, "Subtitles",
clapper_app_utils_get_subtitles_mime_types ());
gtk_file_dialog_set_modal (dialog, TRUE);
gtk_file_dialog_set_title (dialog, "Open Subtitles");
gtk_file_dialog_open (dialog, window, NULL,
(GAsyncReadyCallback) _open_subtitles_cb,
gst_object_ref (item));
g_object_unref (dialog);
}

View File

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

View File

@@ -0,0 +1,185 @@
/* Clapper Application
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include "clapper-app-headerbar.h"
#include "clapper-app-utils.h"
#define GST_CAT_DEFAULT clapper_app_headerbar_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperAppHeaderbar
{
ClapperGtkContainer parent;
GtkWidget *queue_revealer;
GtkWidget *previous_item_revealer;
GtkWidget *next_item_revealer;
GtkWidget *win_buttons_revealer;
GtkDropTarget *drop_target;
gboolean adapt;
};
#define parent_class clapper_app_headerbar_parent_class
G_DEFINE_TYPE (ClapperAppHeaderbar, clapper_app_headerbar, CLAPPER_GTK_TYPE_CONTAINER);
static void
_determine_win_buttons_reveal (ClapperAppHeaderbar *self)
{
gboolean queue_reveal = gtk_revealer_get_reveal_child (GTK_REVEALER (self->queue_revealer));
gtk_revealer_set_reveal_child (GTK_REVEALER (self->win_buttons_revealer),
(queue_reveal) ? !self->adapt : TRUE);
}
static void
container_adapt_cb (ClapperGtkContainer *container, gboolean adapt,
ClapperAppHeaderbar *self)
{
GST_DEBUG_OBJECT (self, "Width adapted: %s", (adapt) ? "yes" : "no");
self->adapt = adapt;
gtk_revealer_set_reveal_child (GTK_REVEALER (self->previous_item_revealer), !adapt);
gtk_revealer_set_reveal_child (GTK_REVEALER (self->next_item_revealer), !adapt);
_determine_win_buttons_reveal (self);
}
static void
queue_reveal_cb (GtkRevealer *revealer,
GParamSpec *pspec G_GNUC_UNUSED, ClapperAppHeaderbar *self)
{
_determine_win_buttons_reveal (self);
}
static void
reveal_queue_button_clicked_cb (GtkButton *button, ClapperAppHeaderbar *self)
{
gboolean reveal;
GST_INFO_OBJECT (self, "Reveal queue button clicked");
reveal = gtk_revealer_get_reveal_child (GTK_REVEALER (self->queue_revealer));
gtk_revealer_set_reveal_child (GTK_REVEALER (self->queue_revealer), !reveal);
}
static void
drop_value_notify_cb (GtkDropTarget *drop_target,
GParamSpec *pspec G_GNUC_UNUSED, ClapperAppHeaderbar *self)
{
const GValue *value = gtk_drop_target_get_value (drop_target);
if (value && !clapper_app_utils_value_for_item_is_valid (value))
gtk_drop_target_reject (drop_target);
}
static gboolean
drop_cb (GtkDropTarget *drop_target, const GValue *value,
gdouble x, gdouble y, ClapperAppHeaderbar *self)
{
GFile **files = NULL;
gint n_files = 0;
gboolean success = FALSE;
if (clapper_app_utils_files_from_value (value, &files, &n_files)) {
ClapperPlayer *player;
if ((player = clapper_gtk_get_player_from_ancestor (GTK_WIDGET (self)))) {
ClapperQueue *queue = clapper_player_get_queue (player);
gint i;
for (i = 0; i < n_files; ++i) {
ClapperMediaItem *item = clapper_media_item_new_from_file (files[i]);
clapper_queue_add_item (queue, item);
if (i == 0) // Select first added item for playback
clapper_queue_select_item (queue, item);
gst_object_unref (item);
}
success = TRUE;
}
clapper_app_utils_files_free (files);
}
return success;
}
static void
clapper_app_headerbar_init (ClapperAppHeaderbar *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
gtk_drop_target_set_gtypes (self->drop_target,
(GType[3]) { GDK_TYPE_FILE_LIST, G_TYPE_FILE, G_TYPE_STRING }, 3);
}
static void
clapper_app_headerbar_dispose (GObject *object)
{
gtk_widget_dispose_template (GTK_WIDGET (object), CLAPPER_APP_TYPE_HEADERBAR);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
clapper_app_headerbar_finalize (GObject *object)
{
ClapperAppHeaderbar *self = CLAPPER_APP_HEADERBAR_CAST (object);
GST_TRACE_OBJECT (self, "Finalize");
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
clapper_app_headerbar_class_init (ClapperAppHeaderbarClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperappheaderbar", 0,
"Clapper App Headerbar");
gobject_class->dispose = clapper_app_headerbar_dispose;
gobject_class->finalize = clapper_app_headerbar_finalize;
gtk_widget_class_set_template_from_resource (widget_class,
CLAPPER_APP_RESOURCE_PREFIX "/ui/clapper-app-headerbar.ui");
gtk_widget_class_bind_template_child (widget_class, ClapperAppHeaderbar, queue_revealer);
gtk_widget_class_bind_template_child (widget_class, ClapperAppHeaderbar, previous_item_revealer);
gtk_widget_class_bind_template_child (widget_class, ClapperAppHeaderbar, next_item_revealer);
gtk_widget_class_bind_template_child (widget_class, ClapperAppHeaderbar, win_buttons_revealer);
gtk_widget_class_bind_template_child (widget_class, ClapperAppHeaderbar, drop_target);
gtk_widget_class_bind_template_callback (widget_class, container_adapt_cb);
gtk_widget_class_bind_template_callback (widget_class, reveal_queue_button_clicked_cb);
gtk_widget_class_bind_template_callback (widget_class, queue_reveal_cb);
gtk_widget_class_bind_template_callback (widget_class, drop_value_notify_cb);
gtk_widget_class_bind_template_callback (widget_class, drop_cb);
gtk_widget_class_set_css_name (widget_class, "clapper-app-headerbar");
}

View File

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

View File

@@ -0,0 +1,212 @@
/* Clapper Application
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <glib/gi18n.h>
#include <gst/gst.h>
#include <clapper-gtk/clapper-gtk.h>
#include "clapper-app-info-window.h"
#include "clapper-app-property-row.h"
#include "clapper-app-list-item-utils.h"
#define GST_CAT_DEFAULT clapper_app_info_window_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperAppInfoWindow
{
AdwWindow parent;
GtkWidget *vstreams_list;
GtkWidget *astreams_list;
GtkWidget *sstreams_list;
ClapperPlayer *player;
};
#define parent_class clapper_app_info_window_parent_class
G_DEFINE_TYPE (ClapperAppInfoWindow, clapper_app_info_window, ADW_TYPE_WINDOW);
enum
{
PROP_0,
PROP_PLAYER,
PROP_LAST
};
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
static gchar *
media_duration_closure (ClapperAppInfoWindow *self, gdouble duration)
{
return g_strdup_printf ("%" CLAPPER_TIME_MS_FORMAT, CLAPPER_TIME_MS_ARGS (duration));
}
static gchar *
playback_element_name_closure (ClapperAppInfoWindow *self, GstElement *element)
{
GstElementFactory *factory;
if (!element || !(factory = gst_element_get_factory (element)))
return NULL;
return gst_object_get_name (GST_OBJECT_CAST (factory));
}
static gchar *
playback_decoder_closure (ClapperAppInfoWindow *self, GstElement *decoder)
{
GstElementFactory *factory;
gchar *el_name, *text;
gboolean is_hardware;
if (!decoder || !(factory = gst_element_get_factory (decoder)))
return NULL;
el_name = gst_object_get_name (GST_OBJECT_CAST (factory));
is_hardware = gst_element_factory_list_is_type (factory,
GST_ELEMENT_FACTORY_TYPE_HARDWARE);
text = g_strdup_printf ("%s [%s]", el_name,
(is_hardware) ? _("Hardware") : _("Software"));
g_free (el_name);
return text;
}
static GtkSelectionModel *
create_no_selection_closure (ClapperAppInfoWindow *self, ClapperStreamList *stream_list)
{
return GTK_SELECTION_MODEL (gtk_no_selection_new (gst_object_ref (stream_list)));
}
static gboolean
has_streams_closure (ClapperAppInfoWindow *self, guint n_streams)
{
return (n_streams > 0);
}
GtkWidget *
clapper_app_info_window_new (GtkApplication *gtk_app, ClapperPlayer *player)
{
ClapperAppInfoWindow *window;
window = g_object_new (CLAPPER_APP_TYPE_INFO_WINDOW,
"application", gtk_app,
"transient-for", gtk_application_get_active_window (gtk_app),
"player", player,
NULL);
return GTK_WIDGET (window);
}
static void
clapper_app_info_window_init (ClapperAppInfoWindow *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
gtk_widget_remove_css_class (self->vstreams_list, "view");
gtk_widget_remove_css_class (self->astreams_list, "view");
gtk_widget_remove_css_class (self->sstreams_list, "view");
}
static void
clapper_app_info_window_dispose (GObject *object)
{
gtk_widget_dispose_template (GTK_WIDGET (object), CLAPPER_APP_TYPE_INFO_WINDOW);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
clapper_app_info_window_finalize (GObject *object)
{
ClapperAppInfoWindow *self = CLAPPER_APP_INFO_WINDOW_CAST (object);
GST_TRACE_OBJECT (self, "Finalize");
gst_clear_object (&self->player);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
clapper_app_info_window_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
ClapperAppInfoWindow *self = CLAPPER_APP_INFO_WINDOW_CAST (object);
switch (prop_id) {
case PROP_PLAYER:
g_value_set_object (value, self->player);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_app_info_window_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
ClapperAppInfoWindow *self = CLAPPER_APP_INFO_WINDOW_CAST (object);
switch (prop_id) {
case PROP_PLAYER:
self->player = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_app_info_window_class_init (ClapperAppInfoWindowClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperappinfowindow", 0,
"Clapper App Info Window");
gobject_class->get_property = clapper_app_info_window_get_property;
gobject_class->set_property = clapper_app_info_window_set_property;
gobject_class->dispose = clapper_app_info_window_dispose;
gobject_class->finalize = clapper_app_info_window_finalize;
gtk_widget_class_set_template_from_resource (widget_class,
CLAPPER_APP_RESOURCE_PREFIX "/ui/clapper-app-info-window.ui");
param_specs[PROP_PLAYER] = g_param_spec_object ("player",
NULL, NULL, CLAPPER_TYPE_PLAYER,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
gtk_widget_class_bind_template_child (widget_class, ClapperAppInfoWindow, vstreams_list);
gtk_widget_class_bind_template_child (widget_class, ClapperAppInfoWindow, astreams_list);
gtk_widget_class_bind_template_child (widget_class, ClapperAppInfoWindow, sstreams_list);
gtk_widget_class_bind_template_callback (widget_class, media_duration_closure);
gtk_widget_class_bind_template_callback (widget_class, playback_element_name_closure);
gtk_widget_class_bind_template_callback (widget_class, playback_decoder_closure);
gtk_widget_class_bind_template_callback (widget_class, create_no_selection_closure);
gtk_widget_class_bind_template_callback (widget_class, has_streams_closure);
}

View File

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

View File

@@ -0,0 +1,83 @@
/* Clapper Application
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <glib/gi18n.h>
#include "clapper-app-list-item-utils.h"
gchar *
clapper_app_list_item_make_stream_group_title (GtkListItem *list_item, ClapperStream *stream)
{
ClapperStreamType stream_type = CLAPPER_STREAM_TYPE_UNKNOWN;
guint position = gtk_list_item_get_position (list_item);
gchar *title = NULL;
if (stream)
stream_type = clapper_stream_get_stream_type (stream);
switch (stream_type) {
case CLAPPER_STREAM_TYPE_VIDEO:
title = g_strdup_printf ("%s #%u", _("Video"), position);
break;
case CLAPPER_STREAM_TYPE_AUDIO:
title = g_strdup_printf ("%s #%u", _("Audio"), position);
break;
case CLAPPER_STREAM_TYPE_SUBTITLE:
title = g_strdup_printf ("%s #%u", _("Subtitles"), position);
break;
default:
break;
}
return title;
}
gchar *
clapper_app_list_item_make_resolution (GtkListItem *list_item,
gint width, gint height)
{
return g_strdup_printf ("%ix%i", width, height);
}
gchar *
clapper_app_list_item_make_bitrate (GtkListItem *list_item, guint bitrate)
{
if (bitrate >= 1000000)
return g_strdup_printf ("%.3lf Mbps", (gdouble) bitrate / 1000000);
return g_strdup_printf ("%u kbps", bitrate / 1000);
}
gchar *
clapper_app_list_item_convert_int (GtkListItem *list_item, gint value)
{
return g_strdup_printf ("%i", value);
}
gchar *
clapper_app_list_item_convert_uint (GtkListItem *list_item, guint value)
{
return g_strdup_printf ("%u", value);
}
gchar *
clapper_app_list_item_convert_double (GtkListItem *list_item, gdouble value)
{
return g_strdup_printf ("%.3lf", value);
}

View File

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

View File

@@ -0,0 +1,106 @@
/* Clapper Application
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "clapper-app-media-item-box.h"
struct _ClapperAppMediaItemBox
{
GtkBox parent;
ClapperMediaItem *media_item;
};
enum
{
PROP_0,
PROP_MEDIA_ITEM,
PROP_LAST
};
#define parent_class clapper_app_media_item_box_parent_class
G_DEFINE_TYPE (ClapperAppMediaItemBox, clapper_app_media_item_box, GTK_TYPE_BOX);
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
ClapperMediaItem *
clapper_app_media_item_box_get_media_item (ClapperAppMediaItemBox *self)
{
return self->media_item;
}
static void
clapper_app_media_item_box_init (ClapperAppMediaItemBox *self)
{
}
static void
clapper_app_media_item_box_finalize (GObject *object)
{
ClapperAppMediaItemBox *self = CLAPPER_APP_MEDIA_ITEM_BOX_CAST (object);
gst_clear_object (&self->media_item);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
clapper_app_media_item_box_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
ClapperAppMediaItemBox *self = CLAPPER_APP_MEDIA_ITEM_BOX_CAST (object);
switch (prop_id) {
case PROP_MEDIA_ITEM:
g_value_set_object (value, clapper_app_media_item_box_get_media_item (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_app_media_item_box_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
ClapperAppMediaItemBox *self = CLAPPER_APP_MEDIA_ITEM_BOX_CAST (object);
switch (prop_id) {
case PROP_MEDIA_ITEM:
gst_object_replace ((GstObject **) &self->media_item, GST_OBJECT_CAST (g_value_get_object (value)));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_app_media_item_box_class_init (ClapperAppMediaItemBoxClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
gobject_class->get_property = clapper_app_media_item_box_get_property;
gobject_class->set_property = clapper_app_media_item_box_set_property;
gobject_class->finalize = clapper_app_media_item_box_finalize;
param_specs[PROP_MEDIA_ITEM] = g_param_spec_object ("media-item",
NULL, NULL, CLAPPER_TYPE_MEDIA_ITEM,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
}

View File

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

View File

@@ -0,0 +1,614 @@
/* Clapper Application
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <glib/gi18n.h>
#include <gst/gst.h>
#include <clapper/clapper.h>
#include "clapper-app-preferences-window.h"
#include "clapper-app-application.h"
#include "clapper-app-utils.h"
#define GST_CAT_DEFAULT clapper_app_preferences_window_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperAppPreferencesWindow
{
AdwPreferencesWindow parent;
AdwComboRow *seek_method_combo_row;
AdwComboRow *seek_unit_combo_row;
AdwSpinRow *seek_value_spin_row;
AdwSwitchRow *server_switch_row;
AdwSpinRow *audio_offset_spin_row;
AdwSpinRow *subtitle_offset_spin_row;
GtkFontDialogButton *font_dialog_button;
AdwNavigationPage *plugins_subpage;
AdwComboRow *plugins_combo_row;
AdwComboRow *features_combo_row;
AdwPreferencesGroup *overrides_group;
GSettings *settings;
GList *features;
GtkStringList *plugins_list;
GPtrArray *rank_rows;
gulong ranks_setting_changed_id;
gboolean ranking_has_plugins_model;
};
#define parent_class clapper_app_preferences_window_parent_class
G_DEFINE_TYPE (ClapperAppPreferencesWindow, clapper_app_preferences_window, ADW_TYPE_PREFERENCES_WINDOW);
typedef struct
{
ClapperAppPreferencesWindow *prefs;
GHashTable *parsed_overrides;
gboolean updated;
} ClapperAppPreferencesIterRanksData;
enum
{
PROP_0,
PROP_RANK_ROWS,
PROP_LAST
};
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
/* Sort by plugin name and if the same, sort by element name */
static gint
_compare_plugins_cb (gconstpointer ptr_a, gconstpointer ptr_b)
{
GstPluginFeature *feature_a = GST_PLUGIN_FEATURE_CAST (ptr_a);
GstPluginFeature *feature_b = GST_PLUGIN_FEATURE_CAST (ptr_b);
gint result;
result = strcmp (
gst_plugin_feature_get_plugin_name (feature_a),
gst_plugin_feature_get_plugin_name (feature_b));
if (result == 0) {
result = strcmp (
gst_plugin_feature_get_name (feature_a),
gst_plugin_feature_get_name (feature_b));
}
return result;
}
static gint
_compare_names_cb (gconstpointer ptr_a, gconstpointer ptr_b)
{
GstPluginFeature *feature = GST_PLUGIN_FEATURE_CAST (ptr_a);
const gchar *plugin_name = (const gchar *) ptr_b;
return strcmp (gst_plugin_feature_get_plugin_name (feature), plugin_name);
}
static gboolean
_prefs_rows_compare_func (gconstpointer ptr_a, gconstpointer ptr_b)
{
AdwPreferencesRow *row = (AdwPreferencesRow *) ptr_a;
const gchar *name = (const gchar *) ptr_b;
return (strcmp (adw_preferences_row_get_title (row), name) == 0);
}
static gboolean
_find_rank_overide_for_name (ClapperAppPreferencesWindow *self,
const gchar *plugin_feature, guint *index)
{
return g_ptr_array_find_with_equal_func (self->rank_rows,
plugin_feature, (GEqualFunc) _prefs_rows_compare_func, index);
}
static gboolean
_plugin_feature_filter_cb (GstPluginFeature *feature, gpointer user_data G_GNUC_UNUSED)
{
return GST_IS_ELEMENT_FACTORY (feature);
}
static void
remove_rank_override_button_clicked_cb (GtkButton *button, ClapperAppPreferencesWindow *self)
{
GtkWidget *spin_row;
const gchar *feature_name;
spin_row = gtk_widget_get_ancestor (GTK_WIDGET (button), ADW_TYPE_SPIN_ROW);
feature_name = adw_preferences_row_get_title (ADW_PREFERENCES_ROW (spin_row));
GST_DEBUG ("Removing rank override for: %s", feature_name);
g_ptr_array_remove (self->rank_rows, spin_row);
adw_preferences_group_remove (self->overrides_group, GTK_WIDGET (spin_row));
gtk_widget_set_visible (GTK_WIDGET (self->overrides_group), self->rank_rows->len > 0);
g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_RANK_ROWS]);
}
static void
_add_rank_override (ClapperAppPreferencesWindow *self,
const gchar *feature_name, GstRank rank, gboolean from_env)
{
GtkWidget *spin_row, *remove_button;
spin_row = adw_spin_row_new_with_range (0, G_MAXINT, 1);
remove_button = gtk_button_new_from_icon_name ("user-trash-symbolic");
gtk_widget_set_halign (remove_button, GTK_ALIGN_CENTER);
gtk_widget_set_valign (remove_button, GTK_ALIGN_CENTER);
gtk_widget_add_css_class (remove_button, "circular");
adw_preferences_row_set_title (ADW_PREFERENCES_ROW (spin_row), feature_name);
adw_action_row_add_prefix (ADW_ACTION_ROW (spin_row), remove_button);
adw_spin_row_set_numeric (ADW_SPIN_ROW (spin_row), TRUE);
adw_spin_row_set_value (ADW_SPIN_ROW (spin_row), rank);
gtk_widget_set_sensitive (spin_row, !from_env);
if (!from_env) {
g_signal_connect (remove_button, "clicked",
G_CALLBACK (remove_rank_override_button_clicked_cb), self);
}
adw_preferences_group_add (self->overrides_group, spin_row);
g_ptr_array_add (self->rank_rows, spin_row);
}
static void
_iter_ranks_func (const gchar *feature_name, GstRank rank,
gboolean from_env, ClapperAppPreferencesIterRanksData *data)
{
ClapperAppPreferencesWindow *self = data->prefs;
guint index = 0;
if (_find_rank_overide_for_name (self, feature_name, &index)) {
GtkWidget *spin_row = g_ptr_array_index (self->rank_rows, index);
if (rank != adw_spin_row_get_value (ADW_SPIN_ROW (spin_row))) {
adw_spin_row_set_value (ADW_SPIN_ROW (spin_row), rank);
data->updated = TRUE;
}
if (from_env == gtk_widget_get_sensitive (spin_row)) {
gtk_widget_set_sensitive (spin_row, !from_env);
data->updated = TRUE;
}
} else {
_add_rank_override (self, feature_name, rank, from_env);
data->updated = TRUE;
}
g_hash_table_insert (data->parsed_overrides,
g_strdup (feature_name), GINT_TO_POINTER (rank));
}
static void
_update_rank_overrides (ClapperAppPreferencesWindow *self)
{
ClapperAppPreferencesIterRanksData *data;
gint i;
data = g_new (ClapperAppPreferencesIterRanksData, 1);
data->prefs = self;
data->parsed_overrides = g_hash_table_new (g_str_hash, g_str_equal);
data->updated = FALSE;
GST_DEBUG ("Updating rank overrides");
clapper_app_utils_iterate_plugin_feature_ranks (self->settings,
(ClapperAppUtilsIterRanks) _iter_ranks_func, data);
for (i = self->rank_rows->len - 1; i >= 0; --i) {
AdwPreferencesRow *prefs_row = ADW_PREFERENCES_ROW (g_ptr_array_index (self->rank_rows, i));
const gchar *feature_name = adw_preferences_row_get_title (prefs_row);
if (!g_hash_table_contains (data->parsed_overrides, feature_name)) {
g_ptr_array_remove_index (self->rank_rows, i);
adw_preferences_group_remove (self->overrides_group, GTK_WIDGET (prefs_row));
data->updated = TRUE;
}
}
if (data->updated) {
gtk_widget_set_visible (GTK_WIDGET (self->overrides_group), self->rank_rows->len > 0);
g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_RANK_ROWS]);
}
g_hash_table_unref (data->parsed_overrides);
g_free (data);
}
static void
add_override_button_clicked_cb (GtkButton *button, ClapperAppPreferencesWindow *self)
{
GstPluginFeature *plugin_feature;
GstRank rank;
GtkStringObject *string_obj;
const gchar *feature_name;
string_obj = GTK_STRING_OBJECT (adw_combo_row_get_selected_item (self->features_combo_row));
/* Should never happen, as button is insensitive when no selection */
if (G_UNLIKELY (string_obj == NULL))
return;
feature_name = gtk_string_object_get_string (string_obj);
GST_DEBUG ("Adding rank override for: %s", feature_name);
plugin_feature = gst_registry_lookup_feature (gst_registry_get (), feature_name);
rank = gst_plugin_feature_get_rank (plugin_feature);
_add_rank_override (self, feature_name, rank, FALSE);
gtk_widget_set_visible (GTK_WIDGET (self->overrides_group), self->rank_rows->len > 0);
g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_RANK_ROWS]);
gst_object_unref (plugin_feature);
}
static GtkStringList *
_make_plugin_features_string_list (ClapperAppPreferencesWindow *self, const gchar *plugin_name)
{
GList *features, *feat;
GtkStringList *features_list;
GStrvBuilder *builder;
gchar **features_names;
GST_DEBUG ("Reading plugin features for plugin: %s", plugin_name);
features = g_list_find_custom (self->features, plugin_name, (GCompareFunc) _compare_names_cb);
builder = g_strv_builder_new ();
for (feat = features; feat != NULL; feat = feat->next) {
GstPluginFeature *feature = GST_PLUGIN_FEATURE_CAST (feat->data);
const gchar *feature_name = gst_plugin_feature_get_name (feature);
if (strcmp (gst_plugin_feature_get_plugin_name (feature), plugin_name) != 0)
break;
g_strv_builder_add (builder, feature_name);
}
features_names = g_strv_builder_end (builder);
g_strv_builder_unref (builder);
features_list = gtk_string_list_new ((const gchar *const *) features_names);
g_strfreev (features_names);
GST_DEBUG ("Found plugin features: %u", g_list_model_get_n_items (G_LIST_MODEL (features_list)));
return features_list;
}
static GtkStringList *
ranking_features_model_closure (ClapperAppPreferencesWindow *self, GtkStringObject *string_obj)
{
if (!string_obj || !self->ranking_has_plugins_model)
return NULL;
return _make_plugin_features_string_list (self, gtk_string_object_get_string (string_obj));
}
static gboolean
add_override_button_sensitive_closure (ClapperAppPreferencesWindow *self,
GtkStringObject *string_obj, GPtrArray *rank_rows)
{
return (string_obj && !_find_rank_overide_for_name (self,
gtk_string_object_get_string (string_obj), NULL));
}
static void
plugin_feature_ranks_settings_changed_cb (GSettings *settings,
gchar *key G_GNUC_UNUSED, ClapperAppPreferencesWindow *self)
{
GST_DEBUG ("Plugin feature ranks stored setting changed");
_update_rank_overrides (self);
}
static void
_ensure_plugins_and_features_lists (ClapperAppPreferencesWindow *self)
{
GList *feat;
GStrvBuilder *builder;
gchar **plugin_names;
const gchar *last_plugin_name = NULL;
/* Return if we were here already. Features can be NULL
* when no plugins are found at specified directory */
if (self->features || self->plugins_list)
return;
GST_DEBUG ("Reading available plugin features...");
self->features = gst_registry_feature_filter (gst_registry_get (),
(GstPluginFeatureFilter) _plugin_feature_filter_cb,
FALSE, NULL);
self->features = g_list_sort (self->features, (GCompareFunc) _compare_plugins_cb);
builder = g_strv_builder_new ();
for (feat = self->features; feat != NULL; feat = feat->next) {
GstPluginFeature *feature = GST_PLUGIN_FEATURE_CAST (feat->data);
const gchar *plugin_name = gst_plugin_feature_get_plugin_name (feature);
if (g_strcmp0 (plugin_name, last_plugin_name) != 0) {
g_strv_builder_add (builder, plugin_name);
last_plugin_name = plugin_name;
}
}
plugin_names = g_strv_builder_end (builder);
g_strv_builder_unref (builder);
GST_DEBUG ("Read all available plugin features");
self->plugins_list = gtk_string_list_new ((const gchar *const *) plugin_names);
g_strfreev (plugin_names);
}
static void
plugin_ranking_activated_cb (AdwActionRow *action_row, ClapperAppPreferencesWindow *self)
{
_ensure_plugins_and_features_lists (self);
if (!self->ranking_has_plugins_model) {
adw_combo_row_set_model (self->plugins_combo_row, G_LIST_MODEL (self->plugins_list));
adw_combo_row_set_selected (self->plugins_combo_row, GTK_INVALID_LIST_POSITION);
GST_DEBUG ("Populated plugins combo row in ranking subpage");
/* This is needed here so we will not populate plugin features row after setting
* model and unset it again after changing back to GTK_INVALID_LIST_POSITION */
self->ranking_has_plugins_model = TRUE;
}
if (self->ranks_setting_changed_id == 0) {
self->ranks_setting_changed_id = g_signal_connect (self->settings,
"changed::plugin-feature-ranks",
G_CALLBACK (plugin_feature_ranks_settings_changed_cb), self);
}
_update_rank_overrides (self);
adw_preferences_window_push_subpage (ADW_PREFERENCES_WINDOW (self), self->plugins_subpage);
}
static void
plugin_ranking_unrealize_cb (GtkWidget *widget, ClapperAppPreferencesWindow *self)
{
GString *string;
gchar *ranks_str;
guint i;
/* Since we are closing ranking subpage, disconnect this
* signal as we do not need to update widgets immediately */
if (self->ranks_setting_changed_id != 0) {
g_signal_handler_disconnect (self->settings, self->ranks_setting_changed_id);
self->ranks_setting_changed_id = 0;
}
GST_DEBUG ("Saving current rank overrides");
string = g_string_new (NULL);
for (i = 0; i < self->rank_rows->len; ++i) {
GtkWidget *spin_row = g_ptr_array_index (self->rank_rows, i);
GstRank rank;
const gchar *feature_name;
/* Insensitive are from env, we do not want to save these */
if (!gtk_widget_get_sensitive (spin_row))
continue;
rank = adw_spin_row_get_value (ADW_SPIN_ROW (spin_row));
feature_name = adw_preferences_row_get_title (ADW_PREFERENCES_ROW (spin_row));
if (string->len == 0)
g_string_append_printf (string, "%s:%i", feature_name, rank);
else
g_string_append_printf (string, ",%s:%i", feature_name, rank);
}
ranks_str = g_string_free_and_steal (string);
g_settings_set_string (self->settings, "plugin-feature-ranks", ranks_str);
g_free (ranks_str);
}
static gchar *
seek_method_name_closure (AdwEnumListItem *list_item, gpointer *user_data G_GNUC_UNUSED)
{
switch (adw_enum_list_item_get_value (list_item)) {
case CLAPPER_PLAYER_SEEK_METHOD_ACCURATE:
return g_strdup (_("Accurate"));
case CLAPPER_PLAYER_SEEK_METHOD_NORMAL:
return g_strdup (_("Normal"));
case CLAPPER_PLAYER_SEEK_METHOD_FAST:
return g_strdup (_("Fast"));
default:
return NULL;
}
}
static gboolean
_get_font_mapping (GValue *value,
GVariant *variant, gpointer user_data G_GNUC_UNUSED)
{
PangoFontDescription *desc;
const gchar *desc_str = g_variant_get_string (variant, NULL);
desc = pango_font_description_from_string (desc_str);
g_value_set_boxed (value, desc);
pango_font_description_free (desc);
return TRUE;
}
static GVariant *
_set_font_mapping (const GValue *value,
const GVariantType *expected_type, gpointer user_data G_GNUC_UNUSED)
{
PangoFontDescription *desc;
gchar *desc_str;
desc = (PangoFontDescription *) g_value_get_boxed (value);
desc_str = pango_font_description_to_string (desc);
return g_variant_new_take_string (desc_str);
}
GtkWidget *
clapper_app_preferences_window_new (GtkApplication *gtk_app)
{
ClapperAppPreferencesWindow *window;
window = g_object_new (CLAPPER_APP_TYPE_PREFERENCES_WINDOW,
"application", gtk_app,
"transient-for", gtk_application_get_active_window (gtk_app),
NULL);
return GTK_WIDGET (window);
}
static void
clapper_app_preferences_window_init (ClapperAppPreferencesWindow *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
self->rank_rows = g_ptr_array_new ();
self->settings = g_settings_new (CLAPPER_APP_ID);
g_settings_bind (self->settings, "seek-method",
self->seek_method_combo_row, "selected", G_SETTINGS_BIND_DEFAULT);
g_settings_bind (self->settings, "seek-unit",
self->seek_unit_combo_row, "selected", G_SETTINGS_BIND_DEFAULT);
g_settings_bind (self->settings, "seek-value",
self->seek_value_spin_row, "value", G_SETTINGS_BIND_DEFAULT);
#if CLAPPER_HAVE_SERVER
g_settings_bind (self->settings, "server-enabled",
self->server_switch_row, "active", G_SETTINGS_BIND_DEFAULT);
#else
gtk_widget_set_sensitive (GTK_WIDGET (self->server_switch_row), FALSE);
#endif
g_settings_bind (self->settings, "audio-offset",
self->audio_offset_spin_row, "value", G_SETTINGS_BIND_DEFAULT);
g_settings_bind (self->settings, "subtitle-offset",
self->subtitle_offset_spin_row, "value", G_SETTINGS_BIND_DEFAULT);
g_settings_bind_with_mapping (self->settings, "subtitle-font-desc",
self->font_dialog_button, "font-desc", G_SETTINGS_BIND_DEFAULT,
(GSettingsBindGetMapping) _get_font_mapping,
(GSettingsBindSetMapping) _set_font_mapping,
NULL, NULL);
}
static void
clapper_app_preferences_window_dispose (GObject *object)
{
ClapperAppPreferencesWindow *self = CLAPPER_APP_PREFERENCES_WINDOW_CAST (object);
g_clear_pointer (&self->rank_rows, g_ptr_array_unref);
gtk_widget_dispose_template (GTK_WIDGET (self), CLAPPER_APP_TYPE_PREFERENCES_WINDOW);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
clapper_app_preferences_window_finalize (GObject *object)
{
ClapperAppPreferencesWindow *self = CLAPPER_APP_PREFERENCES_WINDOW_CAST (object);
GST_TRACE_OBJECT (self, "Finalize");
g_object_unref (self->settings);
g_clear_object (&self->plugins_list);
if (self->features)
gst_plugin_feature_list_free (self->features);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
clapper_app_preferences_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
ClapperAppPreferencesWindow *self = CLAPPER_APP_PREFERENCES_WINDOW_CAST (object);
switch (prop_id) {
case PROP_RANK_ROWS:
g_value_set_boxed (value, self->rank_rows);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_app_preferences_window_class_init (ClapperAppPreferencesWindowClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperapppreferenceswindow", 0,
"Clapper App Preferences Window");
gobject_class->get_property = clapper_app_preferences_get_property;
gobject_class->dispose = clapper_app_preferences_window_dispose;
gobject_class->finalize = clapper_app_preferences_window_finalize;
gtk_widget_class_set_template_from_resource (widget_class,
CLAPPER_APP_RESOURCE_PREFIX "/ui/clapper-app-preferences-window.ui");
param_specs[PROP_RANK_ROWS] = g_param_spec_boxed ("rank-rows",
NULL, NULL, G_TYPE_PTR_ARRAY,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, seek_method_combo_row);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, seek_unit_combo_row);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, seek_value_spin_row);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, server_switch_row);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, audio_offset_spin_row);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, subtitle_offset_spin_row);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, font_dialog_button);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, plugins_subpage);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, plugins_combo_row);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, features_combo_row);
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, overrides_group);
gtk_widget_class_bind_template_callback (widget_class, seek_method_name_closure);
gtk_widget_class_bind_template_callback (widget_class, plugin_ranking_activated_cb);
gtk_widget_class_bind_template_callback (widget_class, plugin_ranking_unrealize_cb);
gtk_widget_class_bind_template_callback (widget_class, ranking_features_model_closure);
gtk_widget_class_bind_template_callback (widget_class, add_override_button_sensitive_closure);
gtk_widget_class_bind_template_callback (widget_class, add_override_button_clicked_cb);
}

View File

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

View File

@@ -0,0 +1,77 @@
/* Clapper Application
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "clapper-app-property-row.h"
struct _ClapperAppPropertyRow
{
AdwActionRow parent;
};
#define parent_class clapper_app_property_row_parent_class
G_DEFINE_TYPE (ClapperAppPropertyRow, clapper_app_property_row, ADW_TYPE_ACTION_ROW);
static inline void
_ensure_subtitle (AdwActionRow *action_row)
{
const gchar *subtitle = adw_action_row_get_subtitle (action_row);
if (!subtitle || strlen (subtitle) == 0)
adw_action_row_set_subtitle (action_row, "-");
}
static void
_subtitle_changed_cb (AdwActionRow *action_row,
GParamSpec *pspec G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED)
{
_ensure_subtitle (action_row);
}
static void
clapper_app_property_row_realize (GtkWidget *widget)
{
_ensure_subtitle (ADW_ACTION_ROW (widget));
g_signal_connect (widget, "notify::subtitle",
G_CALLBACK (_subtitle_changed_cb), NULL);
GTK_WIDGET_CLASS (parent_class)->realize (widget);
}
static void
clapper_app_property_row_unrealize (GtkWidget *widget)
{
g_signal_handlers_disconnect_by_func (widget,
_subtitle_changed_cb, NULL);
GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
}
static void
clapper_app_property_row_init (ClapperAppPropertyRow *self)
{
gtk_widget_add_css_class (GTK_WIDGET (self), "property");
}
static void
clapper_app_property_row_class_init (ClapperAppPropertyRowClass *klass)
{
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
widget_class->realize = clapper_app_property_row_realize;
widget_class->unrealize = clapper_app_property_row_unrealize;
}

View File

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

View File

@@ -0,0 +1,472 @@
/* Clapper Application
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gdk/gdk.h>
#include "clapper-app-queue-list.h"
#include "clapper-app-queue-selection.h"
#include "clapper-app-media-item-box.h"
#include "clapper-app-utils.h"
#define GST_CAT_DEFAULT clapper_app_queue_list_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperAppQueueList
{
GtkBox parent;
GtkWidget *progression_drop_down;
GtkWidget *list_view;
GtkWidget *stack;
GtkWidget *stack_default_page;
GtkWidget *stack_trash_page;
GtkDropTarget *trash_drop_target;
GtkDropTarget *drop_target;
GBinding *queue_progression_binding;
GtkWidget *list_target; // store last target
gboolean drop_after; // if should drop below list_target
};
#define parent_class clapper_app_queue_list_parent_class
G_DEFINE_TYPE (ClapperAppQueueList, clapper_app_queue_list, GTK_TYPE_BOX);
typedef struct
{
ClapperMediaItem *item;
GtkWidget *widget;
GdkPaintable *paintable;
gdouble x, y;
} ClapperAppQueueListDragData;
static GdkContentProvider *
drag_item_prepare_cb (GtkDragSource *drag_source, gdouble x, gdouble y, ClapperAppQueueList *self)
{
GtkWidget *list_view, *pickup, *list_widget;
GdkPaintable *paintable;
ClapperMediaItem *item;
ClapperAppQueueListDragData *drag_data;
graphene_point_t p;
/* Ensure no target yet */
self->list_target = NULL;
list_view = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (drag_source));
pickup = gtk_widget_pick (list_view, x, y, GTK_PICK_DEFAULT);
if (G_UNLIKELY (pickup == NULL) || !CLAPPER_APP_IS_MEDIA_ITEM_BOX (pickup))
return NULL;
list_widget = gtk_widget_get_parent (pickup);
item = clapper_app_media_item_box_get_media_item (CLAPPER_APP_MEDIA_ITEM_BOX_CAST (pickup));
if (G_UNLIKELY (item == NULL || list_widget == NULL))
return NULL;
GST_DEBUG_OBJECT (self, "Preparing drag for: %" GST_PTR_FORMAT, item);
if (!gtk_widget_compute_point (list_view, list_widget, &GRAPHENE_POINT_INIT (x, y), &p))
graphene_point_init (&p, x, y);
paintable = gtk_widget_paintable_new (list_widget);
drag_data = g_new0 (ClapperAppQueueListDragData, 1);
drag_data->item = gst_object_ref (item);
drag_data->widget = g_object_ref_sink (pickup);
drag_data->paintable = gdk_paintable_get_current_image (paintable);
drag_data->x = p.x;
drag_data->y = p.y;
g_object_set_data (G_OBJECT (self), "drag-data", drag_data);
g_object_unref (paintable);
return gdk_content_provider_new_typed (GTK_TYPE_WIDGET, pickup);
}
static void
drag_item_drag_begin_cb (GtkDragSource *drag_source, GdkDrag *drag, ClapperAppQueueList *self)
{
ClapperAppQueueListDragData *drag_data;
GtkWidget *list_view;
drag_data = (ClapperAppQueueListDragData *) g_object_get_data (G_OBJECT (self), "drag-data");
gtk_drag_source_set_icon (drag_source, drag_data->paintable, drag_data->x, drag_data->y);
gtk_widget_set_opacity (drag_data->widget, 0.3);
list_view = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (drag_source));
gtk_widget_add_css_class (list_view, "dnd");
gtk_stack_set_visible_child (GTK_STACK (self->stack), self->stack_trash_page);
}
static void
drag_item_drag_end_cb (GtkDragSource *drag_source, GdkDrag *drag,
gboolean delete_data, ClapperAppQueueList *self)
{
ClapperAppQueueListDragData *drag_data;
GtkWidget *list_view;
drag_data = (ClapperAppQueueListDragData *) g_object_get_data (G_OBJECT (self), "drag-data");
g_object_set_data (G_OBJECT (self), "drag-data", NULL);
list_view = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (drag_source));
gtk_widget_remove_css_class (list_view, "dnd");
gtk_widget_set_opacity (drag_data->widget, 1.0);
gtk_stack_set_visible_child (GTK_STACK (self->stack), self->stack_default_page);
gst_object_unref (drag_data->item);
g_object_unref (drag_data->widget);
g_object_unref (drag_data->paintable);
g_free (drag_data);
gtk_event_controller_reset (GTK_EVENT_CONTROLLER (drag_source));
}
static void
queue_drop_value_notify_cb (GtkDropTarget *drop_target,
GParamSpec *pspec G_GNUC_UNUSED, ClapperAppQueueList *self)
{
const GValue *value = gtk_drop_target_get_value (drop_target);
if (value && !clapper_app_utils_value_for_item_is_valid (value))
gtk_drop_target_reject (drop_target);
}
static GdkDragAction
queue_drop_motion_cb (GtkDropTarget *drop_target,
gdouble x, gdouble y, ClapperAppQueueList *self)
{
GtkWidget *list_view, *pickup;
GdkDrop *drop;
list_view = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (drop_target));
pickup = gtk_widget_pick (list_view, x, y, GTK_PICK_DEFAULT);
if (pickup && CLAPPER_APP_IS_MEDIA_ITEM_BOX (pickup)) {
ClapperAppQueueListDragData *drag_data;
GtkWidget *list_widget;
graphene_point_t point;
gint height, margin_top = 0, margin_bottom = 0;
drag_data = (ClapperAppQueueListDragData *) g_object_get_data (G_OBJECT (self), "drag-data");
list_widget = gtk_widget_get_parent (pickup);
height = gtk_widget_get_height (list_widget);
if ((!drag_data || pickup != drag_data->widget)
&& gtk_widget_compute_point (list_view, list_widget, &GRAPHENE_POINT_INIT (x, y), &point)) {
GtkWidget *sibling = NULL;
if (point.y < (gfloat) height / 2) {
if (drag_data)
sibling = gtk_widget_get_prev_sibling (list_widget);
if (!sibling || gtk_widget_get_parent (drag_data->widget) != sibling)
margin_top = height;
} else {
if (drag_data)
sibling = gtk_widget_get_next_sibling (list_widget);
if (!sibling || gtk_widget_get_parent (drag_data->widget) != sibling)
margin_bottom = height;
}
}
if (self->list_target && self->list_target != list_widget) {
gtk_widget_set_margin_top (self->list_target, 0);
gtk_widget_set_margin_bottom (self->list_target, 0);
}
gtk_widget_set_margin_top (list_widget, margin_top);
gtk_widget_set_margin_bottom (list_widget, margin_bottom);
self->list_target = list_widget;
self->drop_after = (margin_bottom > margin_top);
}
if ((drop = gtk_drop_target_get_current_drop (drop_target))) {
GdkContentFormats *formats = gdk_drop_get_formats (drop);
/* If it is a widget we move it from one place to another */
if (gdk_content_formats_contain_gtype (formats, GTK_TYPE_WIDGET))
return GDK_ACTION_MOVE;
}
return GDK_ACTION_COPY;
}
static void
queue_drop_leave_cb (GtkDropTarget *drop_target, ClapperAppQueueList *self)
{
if (self->list_target) {
gtk_widget_set_margin_top (self->list_target, 0);
gtk_widget_set_margin_bottom (self->list_target, 0);
}
}
static gboolean
queue_drop_cb (GtkDropTarget *drop_target, const GValue *value,
gdouble x, gdouble y, ClapperAppQueueList *self)
{
ClapperQueue *queue;
ClapperMediaItem *item;
GtkWidget *pickup;
guint drop_index = 0;
gboolean success = FALSE;
if (G_UNLIKELY (self->list_target == NULL))
return FALSE;
pickup = gtk_widget_get_first_child (self->list_target);
/* Reset margins on drop */
gtk_widget_set_margin_top (self->list_target, 0);
gtk_widget_set_margin_bottom (self->list_target, 0);
self->list_target = NULL;
if (G_UNLIKELY (pickup == NULL) || !CLAPPER_APP_IS_MEDIA_ITEM_BOX (pickup))
return FALSE;
item = clapper_app_media_item_box_get_media_item (CLAPPER_APP_MEDIA_ITEM_BOX_CAST (pickup));
queue = CLAPPER_QUEUE (gst_object_get_parent (GST_OBJECT (item)));
if (G_UNLIKELY (queue == NULL))
return FALSE;
if (!clapper_queue_find_item (queue, item, &drop_index)) {
gst_object_unref (queue);
return FALSE;
}
if (self->drop_after)
drop_index++;
/* Moving item with widget */
if (G_VALUE_HOLDS (value, GTK_TYPE_WIDGET)) {
ClapperAppQueueListDragData *drag_data;
drag_data = (ClapperAppQueueListDragData *) g_object_get_data (G_OBJECT (self), "drag-data");
/* Insert at different place */
if (item != drag_data->item) {
guint index = 0;
if (clapper_queue_find_item (queue, drag_data->item, &index)) {
if (drop_index > index)
drop_index--;
clapper_queue_reposition_item (queue, drag_data->item, drop_index);
success = TRUE;
}
}
} else {
GFile **files = NULL;
gint n_files = 0;
if (clapper_app_utils_files_from_value (value, &files, &n_files)) {
gint i;
for (i = 0; i < n_files; ++i) {
ClapperMediaItem *new_item = clapper_media_item_new_from_file (files[i]);
clapper_queue_insert_item (queue, new_item, drop_index + i);
gst_object_unref (new_item);
}
clapper_app_utils_files_free (files);
success = TRUE;
}
}
gst_object_unref (queue);
return success;
}
static gboolean
trash_drop_cb (GtkDropTarget *drop_target, const GValue *value,
gdouble x, gdouble y, ClapperAppQueueList *self)
{
ClapperAppQueueListDragData *drag_data;
ClapperQueue *queue;
drag_data = (ClapperAppQueueListDragData *) g_object_get_data (G_OBJECT (self), "drag-data");
if ((queue = CLAPPER_QUEUE (gst_object_get_parent (GST_OBJECT (drag_data->item))))) {
clapper_queue_remove_item (queue, drag_data->item);
gst_object_unref (queue);
}
return TRUE;
}
static void
_item_selected_cb (ClapperAppQueueSelection *selection, guint index, ClapperAppQueueList *self)
{
GtkWidget *list_revealer;
/* Auto hide queue list after selection */
list_revealer = gtk_widget_get_ancestor (self->list_view, GTK_TYPE_REVEALER);
if (G_LIKELY (list_revealer != NULL))
gtk_revealer_set_reveal_child (GTK_REVEALER (list_revealer), FALSE);
}
static gboolean
_queue_progression_mode_transform_to_func (GBinding *binding, const GValue *from_value,
GValue *to_value, ClapperAppQueueList *self)
{
ClapperQueueProgressionMode mode = g_value_get_enum (from_value);
g_value_set_uint (to_value, (guint) mode);
return TRUE;
}
static gboolean
_queue_progression_mode_transform_from_func (GBinding *binding, const GValue *from_value,
GValue *to_value, ClapperAppQueueList *self)
{
guint mode = g_value_get_uint (from_value);
if (mode == GTK_INVALID_LIST_POSITION)
return FALSE;
g_value_set_enum (to_value, (ClapperQueueProgressionMode) mode);
return TRUE;
}
static void
clapper_app_queue_list_realize (GtkWidget *widget)
{
ClapperAppQueueList *self = CLAPPER_APP_QUEUE_LIST_CAST (widget);
ClapperPlayer *player;
GTK_WIDGET_CLASS (parent_class)->realize (widget);
GST_TRACE_OBJECT (self, "Realize");
if ((player = clapper_gtk_get_player_from_ancestor (widget))) {
ClapperQueue *queue = clapper_player_get_queue (player);
ClapperAppQueueSelection *selection = clapper_app_queue_selection_new (queue);
g_signal_connect (selection, "item-selected",
G_CALLBACK (_item_selected_cb), self);
self->queue_progression_binding = g_object_bind_property_full (queue, "progression-mode",
self->progression_drop_down, "selected", G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
(GBindingTransformFunc) _queue_progression_mode_transform_to_func,
(GBindingTransformFunc) _queue_progression_mode_transform_from_func,
self, NULL);
gtk_list_view_set_model (GTK_LIST_VIEW (self->list_view),
GTK_SELECTION_MODEL (selection));
g_object_unref (selection);
}
}
static void
clapper_app_queue_list_unrealize (GtkWidget *widget)
{
ClapperAppQueueList *self = CLAPPER_APP_QUEUE_LIST_CAST (widget);
GST_TRACE_OBJECT (self, "Unrealize");
g_clear_pointer (&self->queue_progression_binding, g_binding_unbind);
gtk_list_view_set_model (GTK_LIST_VIEW (self->list_view), NULL);
GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
}
static void
clapper_app_queue_list_init (ClapperAppQueueList *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
/* Does not work correctly with OSD */
gtk_widget_remove_css_class (self->list_view, "view");
gtk_drop_target_set_gtypes (self->trash_drop_target,
(GType[1]) { GTK_TYPE_WIDGET }, 1);
gtk_drop_target_set_gtypes (self->drop_target,
(GType[4]) { GTK_TYPE_WIDGET, GDK_TYPE_FILE_LIST, G_TYPE_FILE, G_TYPE_STRING }, 4);
}
static void
clapper_app_queue_list_dispose (GObject *object)
{
gtk_widget_dispose_template (GTK_WIDGET (object), CLAPPER_APP_TYPE_QUEUE_LIST);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
clapper_app_queue_list_finalize (GObject *object)
{
ClapperAppQueueList *self = CLAPPER_APP_QUEUE_LIST_CAST (object);
GST_TRACE_OBJECT (self, "Finalize");
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
clapper_app_queue_list_class_init (ClapperAppQueueListClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperappqueuelist", 0,
"Clapper App Queue List");
gobject_class->dispose = clapper_app_queue_list_dispose;
gobject_class->finalize = clapper_app_queue_list_finalize;
widget_class->realize = clapper_app_queue_list_realize;
widget_class->unrealize = clapper_app_queue_list_unrealize;
gtk_widget_class_set_template_from_resource (widget_class,
CLAPPER_APP_RESOURCE_PREFIX "/ui/clapper-app-queue-list.ui");
gtk_widget_class_bind_template_child (widget_class, ClapperAppQueueList, progression_drop_down);
gtk_widget_class_bind_template_child (widget_class, ClapperAppQueueList, list_view);
gtk_widget_class_bind_template_child (widget_class, ClapperAppQueueList, stack);
gtk_widget_class_bind_template_child (widget_class, ClapperAppQueueList, stack_default_page);
gtk_widget_class_bind_template_child (widget_class, ClapperAppQueueList, stack_trash_page);
gtk_widget_class_bind_template_child (widget_class, ClapperAppQueueList, trash_drop_target);
gtk_widget_class_bind_template_child (widget_class, ClapperAppQueueList, drop_target);
gtk_widget_class_bind_template_callback (widget_class, drag_item_prepare_cb);
gtk_widget_class_bind_template_callback (widget_class, drag_item_drag_begin_cb);
gtk_widget_class_bind_template_callback (widget_class, drag_item_drag_end_cb);
gtk_widget_class_bind_template_callback (widget_class, queue_drop_value_notify_cb);
gtk_widget_class_bind_template_callback (widget_class, queue_drop_motion_cb);
gtk_widget_class_bind_template_callback (widget_class, queue_drop_leave_cb);
gtk_widget_class_bind_template_callback (widget_class, queue_drop_cb);
gtk_widget_class_bind_template_callback (widget_class, trash_drop_cb);
gtk_widget_class_set_css_name (widget_class, "clapper-app-queue-list");
}

View File

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

View File

@@ -0,0 +1,107 @@
/* Clapper Application
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <glib.h>
#include "clapper-app-queue-progression-item.h"
struct _ClapperAppQueueProgressionItem
{
GObject parent;
gchar *icon_name;
gchar *label;
};
enum
{
PROP_0,
PROP_ICON_NAME,
PROP_LABEL,
PROP_LAST
};
#define parent_class clapper_app_queue_progression_item_parent_class
G_DEFINE_TYPE (ClapperAppQueueProgressionItem, clapper_app_queue_progression_item, G_TYPE_OBJECT);
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
ClapperAppQueueProgressionItem *
clapper_app_queue_progression_item_new (const gchar *icon_name, const gchar *label)
{
ClapperAppQueueProgressionItem *item;
item = g_object_new (CLAPPER_APP_TYPE_QUEUE_PROGRESSION_ITEM, NULL);
item->icon_name = g_strdup (icon_name);
item->label = g_strdup (label);
return item;
}
static void
clapper_app_queue_progression_item_init (ClapperAppQueueProgressionItem *self)
{
}
static void
clapper_app_queue_progression_item_finalize (GObject *object)
{
ClapperAppQueueProgressionItem *self = CLAPPER_APP_QUEUE_PROGRESSION_ITEM_CAST (object);
g_free (self->icon_name);
g_free (self->label);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
clapper_app_queue_progression_item_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
ClapperAppQueueProgressionItem *self = CLAPPER_APP_QUEUE_PROGRESSION_ITEM_CAST (object);
switch (prop_id) {
case PROP_ICON_NAME:
g_value_set_string (value, self->icon_name);
break;
case PROP_LABEL:
g_value_set_string (value, self->label);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_app_queue_progression_item_class_init (ClapperAppQueueProgressionItemClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
gobject_class->get_property = clapper_app_queue_progression_item_get_property;
gobject_class->finalize = clapper_app_queue_progression_item_finalize;
param_specs[PROP_ICON_NAME] = g_param_spec_string ("icon-name",
NULL, NULL, NULL,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
param_specs[PROP_LABEL] = g_param_spec_string ("label",
NULL, NULL, NULL,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
}

View File

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

View File

@@ -0,0 +1,109 @@
/* Clapper Application
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "clapper-app-queue-progression-model.h"
#include "clapper-app-queue-progression-item.h"
#include "clapper-app-utils.h"
#define N_PROGRESSION_MODES 5
struct _ClapperAppQueueProgressionModel
{
GObject parent;
GListStore *store;
};
static GType
clapper_app_queue_progression_model_get_item_type (GListModel *model)
{
return CLAPPER_APP_TYPE_QUEUE_PROGRESSION_ITEM;
}
static guint
clapper_app_queue_progression_model_get_n_items (GListModel *model)
{
ClapperAppQueueProgressionModel *self = CLAPPER_APP_QUEUE_PROGRESSION_MODEL_CAST (model);
return g_list_model_get_n_items (G_LIST_MODEL (self->store));
}
static gpointer
clapper_app_queue_progression_model_get_item (GListModel *model, guint index)
{
ClapperAppQueueProgressionModel *self = CLAPPER_APP_QUEUE_PROGRESSION_MODEL_CAST (model);
return g_list_model_get_item (G_LIST_MODEL (self->store), index);
}
static void
_list_model_iface_init (GListModelInterface *iface)
{
iface->get_item_type = clapper_app_queue_progression_model_get_item_type;
iface->get_n_items = clapper_app_queue_progression_model_get_n_items;
iface->get_item = clapper_app_queue_progression_model_get_item;
}
#define parent_class clapper_app_queue_progression_model_parent_class
G_DEFINE_TYPE_WITH_CODE (ClapperAppQueueProgressionModel, clapper_app_queue_progression_model, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, _list_model_iface_init));
static void
clapper_app_queue_progression_model_init (ClapperAppQueueProgressionModel *self)
{
self->store = g_list_store_new (CLAPPER_APP_TYPE_QUEUE_PROGRESSION_ITEM);
}
static void
clapper_app_queue_progression_model_constructed (GObject *object)
{
ClapperAppQueueProgressionModel *self = CLAPPER_APP_QUEUE_PROGRESSION_MODEL_CAST (object);
guint i;
for (i = 0; i < N_PROGRESSION_MODES; ++i) {
ClapperAppQueueProgressionItem *item;
const gchar *icon = NULL, *label = NULL;
clapper_app_utils_parse_progression (i, &icon, &label);
item = clapper_app_queue_progression_item_new (icon, label);
g_list_store_append (self->store, item);
g_object_unref (item);
}
G_OBJECT_CLASS (parent_class)->constructed (object);
}
static void
clapper_app_queue_progression_model_finalize (GObject *object)
{
ClapperAppQueueProgressionModel *self = CLAPPER_APP_QUEUE_PROGRESSION_MODEL_CAST (object);
g_object_unref (self->store);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
clapper_app_queue_progression_model_class_init (ClapperAppQueueProgressionModelClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
gobject_class->constructed = clapper_app_queue_progression_model_constructed;
gobject_class->finalize = clapper_app_queue_progression_model_finalize;
}

View File

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

View File

@@ -0,0 +1,364 @@
/* Clapper Application
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <gst/gst.h>
#include <gtk/gtk.h>
#include "clapper-app-queue-selection.h"
#define GST_CAT_DEFAULT clapper_app_queue_selection_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperAppQueueSelection
{
GObject parent;
ClapperQueue *queue;
ClapperMediaItem *current_item;
guint current_position;
};
enum
{
PROP_0,
PROP_QUEUE,
PROP_LAST
};
enum
{
SIGNAL_ITEM_SELECTED,
SIGNAL_LAST
};
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
static guint signals[SIGNAL_LAST] = { 0, };
static GType
clapper_app_queue_selection_get_item_type (GListModel *model)
{
return CLAPPER_TYPE_MEDIA_ITEM;
}
static guint
clapper_app_queue_selection_get_n_items (GListModel *model)
{
ClapperAppQueueSelection *self = CLAPPER_APP_QUEUE_SELECTION_CAST (model);
return (self->queue) ? clapper_queue_get_n_items (self->queue) : 0;
}
static gpointer
clapper_app_queue_selection_get_item (GListModel *model, guint index)
{
ClapperAppQueueSelection *self = CLAPPER_APP_QUEUE_SELECTION_CAST (model);
return (self->queue) ? clapper_queue_get_item (self->queue, index) : NULL;
}
static void
_list_model_iface_init (GListModelInterface *iface)
{
iface->get_item_type = clapper_app_queue_selection_get_item_type;
iface->get_n_items = clapper_app_queue_selection_get_n_items;
iface->get_item = clapper_app_queue_selection_get_item;
}
static inline void
_refresh_current_selection (ClapperAppQueueSelection *self)
{
guint position, old_position, index, n_changed;
position = clapper_queue_get_current_index (self->queue);
/* Clapper -> GTK expected value change.
* Should be the same, but better be safe. */
if (position == CLAPPER_QUEUE_INVALID_POSITION)
position = GTK_INVALID_LIST_POSITION;
/* No change */
if (position == self->current_position)
return;
old_position = self->current_position;
self->current_position = position;
if (old_position == GTK_INVALID_LIST_POSITION) {
index = position;
n_changed = 1;
} else if (position == GTK_INVALID_LIST_POSITION) {
index = old_position;
n_changed = 1;
} else if (position < old_position) {
index = position;
n_changed = old_position - position + 1;
} else {
index = old_position;
n_changed = position - old_position + 1;
}
GST_DEBUG ("Selection changed, index: %u, n_changed: %u", index, n_changed);
gtk_selection_model_selection_changed (GTK_SELECTION_MODEL (self), index, n_changed);
}
static gboolean
clapper_app_queue_selection_is_selected (GtkSelectionModel *model, guint position)
{
ClapperAppQueueSelection *self = CLAPPER_APP_QUEUE_SELECTION_CAST (model);
return (position == self->current_position);
}
static GtkBitset *
clapper_app_queue_selection_get_selection_in_range (GtkSelectionModel *model, guint position, guint n_items)
{
ClapperAppQueueSelection *self = CLAPPER_APP_QUEUE_SELECTION_CAST (model);
GtkBitset *bitset = gtk_bitset_new_empty ();
if (self->current_position != GTK_INVALID_LIST_POSITION
&& position <= self->current_position
&& position + n_items > self->current_position)
gtk_bitset_add (bitset, self->current_position);
return bitset;
}
static gboolean
clapper_app_queue_selection_select_item (GtkSelectionModel *model, guint position, gboolean exclusive)
{
ClapperAppQueueSelection *self = CLAPPER_APP_QUEUE_SELECTION_CAST (model);
gboolean res = TRUE;
if (G_UNLIKELY (self->queue == NULL))
return FALSE;
/* Disallow reselecting of the same item */
if (self->current_position != position)
res = clapper_queue_select_index (self->queue, position);
/* Need to always emit this signal when select item succeeds */
if (G_LIKELY (res))
g_signal_emit (self, signals[SIGNAL_ITEM_SELECTED], 0, position);
return res;
}
static gboolean
clapper_app_queue_selection_unselect_item (GtkSelectionModel *model, guint position)
{
return FALSE;
}
static void
_selection_model_iface_init (GtkSelectionModelInterface *iface)
{
iface->is_selected = clapper_app_queue_selection_is_selected;
iface->get_selection_in_range = clapper_app_queue_selection_get_selection_in_range;
iface->select_item = clapper_app_queue_selection_select_item;
iface->unselect_item = clapper_app_queue_selection_unselect_item;
}
#define parent_class clapper_app_queue_selection_parent_class
G_DEFINE_TYPE_WITH_CODE (ClapperAppQueueSelection, clapper_app_queue_selection, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, _list_model_iface_init)
G_IMPLEMENT_INTERFACE (GTK_TYPE_SELECTION_MODEL, _selection_model_iface_init))
static void
_queue_model_items_changed_cb (GListModel *model, guint position, guint removed, guint added,
ClapperAppQueueSelection *self)
{
/* Forward event from internal model */
g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
}
static void
_queue_current_index_changed_cb (ClapperQueue *queue,
GParamSpec *pspec G_GNUC_UNUSED, ClapperAppQueueSelection *self)
{
_refresh_current_selection (self);
}
/*
* clapper_app_queue_selection_new:
* @queue: (nullable): a #ClapperQueue
*
* Creates a new #ClapperAppQueueSelection instance.
*
* Returns: (transfer full): a new #ClapperAppQueueSelection.
*/
ClapperAppQueueSelection *
clapper_app_queue_selection_new (ClapperQueue *queue)
{
return g_object_new (CLAPPER_APP_TYPE_QUEUE_SELECTION, "queue", queue, NULL);
}
/*
* clapper_app_queue_selection_set_queue:
* @selection: a #ClapperAppQueueSelection
* @queue: a #ClapperQueue
*
* Set #ClapperQueue to be managed by this selection model.
*/
void
clapper_app_queue_selection_set_queue (ClapperAppQueueSelection *self, ClapperQueue *queue)
{
guint n_before = 0, n_after = 0;
g_return_if_fail (CLAPPER_APP_IS_QUEUE_SELECTION (self));
g_return_if_fail (CLAPPER_IS_QUEUE (queue));
if (self->queue) {
g_signal_handlers_disconnect_by_func (G_LIST_MODEL (self->queue), _queue_model_items_changed_cb, self);
g_signal_handlers_disconnect_by_func (self->queue, _queue_current_index_changed_cb, self);
n_before = clapper_queue_get_n_items (self->queue);
}
gst_object_replace ((GstObject **) &self->queue, GST_OBJECT_CAST (queue));
g_signal_connect (G_LIST_MODEL (self->queue), "items-changed",
G_CALLBACK (_queue_model_items_changed_cb), self);
g_signal_connect (self->queue, "notify::current-index",
G_CALLBACK (_queue_current_index_changed_cb), self);
g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_QUEUE]);
n_after = clapper_queue_get_n_items (self->queue);
/* Refresh selected item after queue change */
self->current_position = GTK_INVALID_LIST_POSITION;
_queue_model_items_changed_cb (G_LIST_MODEL (self->queue), 0, n_before, n_after, self);
_refresh_current_selection (self);
}
/*
* clapper_app_queue_selection_get_queue:
* @selection: a #ClapperAppQueueSelection
*
* Get #ClapperQueue managed by this selection model.
*
* Returns: (transfer none): #ClapperQueue being managed.
*/
ClapperQueue *
clapper_app_queue_selection_get_queue (ClapperAppQueueSelection *self)
{
g_return_val_if_fail (CLAPPER_APP_IS_QUEUE_SELECTION (self), NULL);
return self->queue;
}
static void
clapper_app_queue_selection_init (ClapperAppQueueSelection *self)
{
self->current_position = GTK_INVALID_LIST_POSITION;
}
static void
clapper_app_queue_selection_finalize (GObject *object)
{
ClapperAppQueueSelection *self = CLAPPER_APP_QUEUE_SELECTION_CAST (object);
if (self->queue) {
g_signal_handlers_disconnect_by_func (G_LIST_MODEL (self->queue), _queue_model_items_changed_cb, self);
g_signal_handlers_disconnect_by_func (self->queue, _queue_current_index_changed_cb, self);
g_object_unref (self->queue);
}
g_clear_object (&self->current_item);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
clapper_app_queue_selection_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
ClapperAppQueueSelection *self = CLAPPER_APP_QUEUE_SELECTION_CAST (object);
switch (prop_id) {
case PROP_QUEUE:
g_value_set_object (value, clapper_app_queue_selection_get_queue (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_app_queue_selection_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
ClapperAppQueueSelection *self = CLAPPER_APP_QUEUE_SELECTION_CAST (object);
switch (prop_id) {
case PROP_QUEUE:
clapper_app_queue_selection_set_queue (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_app_queue_selection_class_init (ClapperAppQueueSelectionClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperappqueueselection", 0,
"Clapper App Queue Selection");
gobject_class->get_property = clapper_app_queue_selection_get_property;
gobject_class->set_property = clapper_app_queue_selection_set_property;
gobject_class->finalize = clapper_app_queue_selection_finalize;
/*
* ClapperAppQueueSelection:queue:
*
* The queue being managed.
*/
param_specs[PROP_QUEUE] = g_param_spec_object ("queue",
NULL, NULL, CLAPPER_TYPE_QUEUE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/*
* ClapperAppQueueSelection::item-selected:
* @selection: a #ClapperAppQueueSelection
* @index: an index of selected item
*
* Signals when user selected item within the [iface@Gtk.SelectionModel].
*
* Note that this signal is emitted only when item gets selected from
* the GTK side (also when the same item is reselected). If item was
* changed internally by e.g. progression of [class@Clapper.Queue],
* this signal will not be emitted.
*
* #ClapperAppQueueSelection automatically takes care of having its
* selection in sync with passed [class@Clapper.Queue], so you do not
* have to listen for the changes. This signal is useful if you need
* to differentiate what caused item selection, otherwise use either
* [signal@Gtk.SelectionModel::selection-changed] signal or listen for
* changes of [property@Clapper.Queue:current-item] property.
*/
signals[SIGNAL_ITEM_SELECTED] = g_signal_new ("item-selected",
G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_UINT);
g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
}

View File

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

View File

@@ -0,0 +1,120 @@
/* Clapper Application
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gio/gio.h>
#include <adwaita.h>
#include <gst/gst.h>
#include "clapper-app-uri-dialog.h"
#include "clapper-app-utils.h"
static void
_entry_text_changed_cb (GtkEntry *entry,
GParamSpec *pspec G_GNUC_UNUSED, AdwMessageDialog *dialog)
{
GtkEntryBuffer *buffer = gtk_entry_get_buffer (entry);
guint text_length = gtk_entry_buffer_get_length (buffer);
gboolean enabled = FALSE;
if (text_length > 0) {
const gchar *text = gtk_entry_buffer_get_text (buffer);
enabled = (text && gst_uri_is_valid (text));
}
adw_message_dialog_set_response_enabled (dialog, "add", enabled);
}
static void
_open_uri_cb (AdwMessageDialog *dialog, GAsyncResult *result, GtkApplication *gtk_app)
{
const gchar *response = adw_message_dialog_choose_finish (dialog, result);
if (strcmp (response, "add") == 0) {
GtkWidget *extra_child = adw_message_dialog_get_extra_child (dialog);
GtkEntryBuffer *buffer = gtk_entry_get_buffer (GTK_ENTRY (extra_child));
const gchar *text = gtk_entry_buffer_get_text (buffer);
GFile **files = NULL;
gint n_files = 0;
if (clapper_app_utils_files_from_string (text, &files, &n_files)) {
g_application_open (G_APPLICATION (gtk_app), files, n_files, "add-only");
clapper_app_utils_files_free (files);
}
}
}
static void
_read_text_cb (GdkClipboard *clipboard, GAsyncResult *result, GtkWidget *extra_child)
{
GtkEntry *entry = GTK_ENTRY (extra_child);
GError *error = NULL;
gchar *text = gdk_clipboard_read_text_finish (clipboard, result, &error);
if (G_LIKELY (error == NULL)) {
if (gst_uri_is_valid (text)) {
gtk_editable_set_text (GTK_EDITABLE (entry), text);
gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
}
} else {
/* Common error when clipboard is empty or has unsupported content.
* This we can safely ignore and not notify user. */
if (error->domain != G_IO_ERROR || error->code != G_IO_ERROR_NOT_SUPPORTED) {
g_printerr ("Error: %s\n",
(error->message) ? error->message : "Could not read clipboard");
}
g_error_free (error);
}
g_free (text);
}
void
clapper_app_uri_dialog_open_uri (GtkApplication *gtk_app)
{
GtkWindow *window = gtk_application_get_active_window (gtk_app);
GtkBuilder *builder;
AdwMessageDialog *dialog;
GtkWidget *extra_child;
GdkDisplay *display;
builder = gtk_builder_new_from_resource (
CLAPPER_APP_RESOURCE_PREFIX "/ui/clapper-app-uri-dialog.ui");
dialog = ADW_MESSAGE_DIALOG (gtk_builder_get_object (builder, "dialog"));
gtk_window_set_transient_for (GTK_WINDOW (dialog), window);
extra_child = adw_message_dialog_get_extra_child (dialog);
g_signal_connect (GTK_ENTRY (extra_child), "notify::text",
G_CALLBACK (_entry_text_changed_cb), dialog);
if ((display = gdk_display_get_default ())) {
GdkClipboard *clipboard = gdk_display_get_clipboard (display);
gdk_clipboard_read_text_async (clipboard, NULL,
(GAsyncReadyCallback) _read_text_cb, extra_child);
}
/* NOTE: Dialog will automatically unref itself after response */
adw_message_dialog_choose (dialog, NULL,
(GAsyncReadyCallback) _open_uri_cb,
gtk_app);
g_object_unref (builder);
}

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