1 Commits

Author SHA1 Message Date
Rafostar
f64f438f1e API: Add playlist support 2021-06-06 19:54:56 +02:00
29 changed files with 1018 additions and 429 deletions

View File

@@ -40,7 +40,7 @@
- [X] Remote playback controls via HTTP (VLC) + WebSockets - [X] Remote playback controls via HTTP (VLC) + WebSockets
- [ ] Expand available API - [ ] Expand available API
- [ ] API documentation - [ ] API documentation
- [X] Integration with the top bar - [ ] Integration with the top bar
- [X] MPRIS support - [ ] MPRIS support
- [X] Controls in the notifications panel - [ ] Controls in the notifications panel
- [ ] Progress bar in the notifications panel (maybe via extension) - [ ] Progress bar in the notifications panel (maybe via extension)

View File

@@ -1,115 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="16"
height="16"
viewBox="0 0 4.2333333 4.2333334"
version="1.1"
id="svg5"
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
sodipodi:docname="com.github.rafostar.Clapper-symbolic.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:object-paths="true"
inkscape:snap-intersection-paths="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-midpoints="true"
inkscape:snap-global="false"
units="px"
inkscape:zoom="32"
inkscape:cx="6.078125"
inkscape:cy="8.09375"
inkscape:window-width="1680"
inkscape:window-height="981"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2">
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect1853"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,1.8520833,0,1 @ F,0,0,1,0,1.8520833,0,1 @ F,0,0,1,0,1.8520833,0,1 @ F,0,0,1,0,1.8520833,0,1"
unit="px"
method="auto"
mode="F"
radius="7"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
<inkscape:path-effect
effect="fillet_chamfer"
id="path-effect1732"
is_visible="true"
lpeversion="1"
satellites_param="F,0,0,1,0,1.8520833,0,1 @ F,0,0,1,0,1.8520833,0,1 @ F,0,0,1,0,1.8520833,0,1 @ F,0,0,1,0,1.8520833,0,1"
unit="px"
method="auto"
mode="F"
radius="7"
chamfer_steps="1"
flexible="false"
use_knot_distance="true"
apply_no_radius="true"
apply_with_radius="true"
only_selected="false"
hide_knots="false" />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g2022"
transform="matrix(0.06169519,0,0,0.06168906,-4.7800087,-3.2713603)">
<path
id="rect973"
style="fill:#000000;stroke-width:1.30776;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke markers fill"
d="m 88.193064,81.795006 c -0.699254,0 -1.342327,0.227875 -1.864484,0.609782 h 51.32503 c -0.52216,-0.381907 -1.16471,-0.609782 -1.86397,-0.609782 z m -3.157945,10.475846 v 26.225278 c 0,1.74939 1.40856,3.15743 3.157945,3.15743 h 47.596576 c 1.74939,0 3.15795,-1.40804 3.15795,-3.15743 V 92.270852 Z m 20.323311,4.964038 15.40009,9.27283 -15.5205,9.56634 z" />
<path
style="fill:#000000;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 83.974394,69.464471 7.344033,-0.06509 a 2.7923103,2.7923103 33.047712 0 1 2.587466,1.683384 l 7.009937,16.201526 a 1.2163248,1.2163248 123.30899 0 1 -1.116623,1.699322 l -15.720141,-0.004 a 1.862525,1.862525 44.853691 0 1 -1.862019,-1.852534 l -0.08473,-15.79409 a 1.8585738,1.8585738 134.59241 0 1 1.842075,-1.868472 z"
id="path1422"
inkscape:path-effect="#path-effect1732"
inkscape:original-d="m 82.122383,69.480886 11.048055,-0.09792 8.480852,19.601124 -19.424307,-0.005 z" />
<rect
style="fill:#000000;stroke-width:1.3;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke markers fill"
id="rect1544"
width="59.366463"
height="9.8661175"
x="82"
y="79.292183"
ry="1.2306831" />
<path
id="rect1847"
style="fill:#000000;stroke-width:4.91339;stroke-linecap:round;stroke-linejoin:round;paint-order:stroke markers fill"
d="m 522.12695,200.42773 c -0.45798,3.8e-4 -0.92335,0.0696 -1.38476,0.21289 l -172.88672,53.70313 5.28515,-0.0469 a 10.55362,10.55362 0 0 1 9.7793,6.36328 l 10.69922,24.72656 158.18359,-49.13477 c 2.46089,-0.7644 3.82691,-3.36137 3.0625,-5.82226 l -8.30078,-26.72657 c -0.62108,-1.99947 -2.4529,-3.277 -4.4375,-3.27539 z m -203.69531,63.05469 -3.08398,0.95899 c -2.46089,0.7644 -3.82691,3.35942 -3.0625,5.82031 l 6.29101,20.2539 z"
transform="scale(0.26458333)" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.8 KiB

View File

@@ -14,10 +14,6 @@
<default>100</default> <default>100</default>
<summary>Custom initial volume value in percentage after startup</summary> <summary>Custom initial volume value in percentage after startup</summary>
</key> </key>
<key name="keep-last-frame" type="b">
<default>false</default>
<summary>Keep showing last video frame after playback finishes</summary>
</key>
<key name="close-auto" type="b"> <key name="close-auto" type="b">
<default>false</default> <default>false</default>
<summary>Automatically close the app after playback finishes</summary> <summary>Automatically close the app after playback finishes</summary>

View File

@@ -25,7 +25,7 @@
<p> <p>
For best stability Wayland session is recommended. Wayland users with AMD/Intel GPUs For best stability Wayland session is recommended. Wayland users with AMD/Intel GPUs
can try enabling HIGHLY EXPERIMENTAL "vah264dec" plugin inside player preferences can try enabling HIGHLY EXPERIMENTAL "vah264dec" plugin inside player preferences
for reduced CPU and GPU usage on H.264 videos. for reduced CPU and GPU usage on standard (8-bit) H.264 videos.
</p> </p>
</description> </description>
<developer_name>Rafał Dzięgiel</developer_name> <developer_name>Rafał Dzięgiel</developer_name>
@@ -52,26 +52,6 @@
</screenshot> </screenshot>
</screenshots> </screenshots>
<releases> <releases>
<release version="0.3.0" date="2021-06-18">
<description>
<p>Changes:</p>
<ul>
<li>Added MPRIS support</li>
<li>Added repeat modes: single video, whole playlist and shuffle</li>
<li>Support opening folders with media files</li>
<li>Append playlist items by holding Ctrl while doing Drag and Drop</li>
<li>Improved handling of keyboard shortcuts</li>
<li>Added more keyboard shortcuts</li>
<li>Added window that shows available keyboard shortcuts</li>
<li>Show black screen by default after playback (make showing last frame optional instead)</li>
<li>Added ability to export playlist to file</li>
<li>Improve handling of changing displays with different resolutions</li>
<li>Added support for EGL under x11 with GTK 4.3.1 or later</li>
<li>Added missing symbolic app icon</li>
<li>Some misc bug fixes and code cleanups</li>
</ul>
</description>
</release>
<release version="0.2.1" date="2021-04-19"> <release version="0.2.1" date="2021-04-19">
<description> <description>
<p>Player:</p> <p>Player:</p>

View File

@@ -4,9 +4,6 @@ iconsdir = join_paths(sharedir, 'icons', 'hicolor')
install_data('com.github.rafostar.Clapper.svg', install_data('com.github.rafostar.Clapper.svg',
install_dir: join_paths(iconsdir, 'scalable', 'apps') 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_data('com.github.rafostar.Clapper.gschema.xml',
install_dir: join_paths(sharedir, 'glib-2.0', 'schemas') install_dir: join_paths(sharedir, 'glib-2.0', 'schemas')
) )

View File

@@ -28,6 +28,8 @@
#include <gst/clapper/gstclapper-g-main-context-signal-dispatcher.h> #include <gst/clapper/gstclapper-g-main-context-signal-dispatcher.h>
#include <gst/clapper/gstclapper-video-overlay-video-renderer.h> #include <gst/clapper/gstclapper-video-overlay-video-renderer.h>
#include <gst/clapper/gstclapper-visualization.h> #include <gst/clapper/gstclapper-visualization.h>
#include <gst/clapper/gstclapper-playlist.h>
#include <gst/clapper/gstclapper-playlist-item.h>
#include <gst/clapper/gstclapper-mpris.h> #include <gst/clapper/gstclapper-mpris.h>
#include <gst/clapper/gstclapper-gtk4-plugin.h> #include <gst/clapper/gstclapper-gtk4-plugin.h>

View File

@@ -392,11 +392,17 @@ handle_open_uri_cb (GstClapperMprisMediaPlayer2Player * player_skeleton,
gpointer user_data) gpointer user_data)
{ {
GstClapper *clapper = GST_CLAPPER (user_data); GstClapper *clapper = GST_CLAPPER (user_data);
GstClapperPlaylist *playlist;
GstClapperPlaylistItem *item;
GST_DEBUG ("Handle OpenUri"); GST_DEBUG ("Handle OpenUri");
/* FIXME: set one item playlist instead */ playlist = gst_clapper_playlist_new ();
gst_clapper_set_uri (clapper, uri); item = gst_clapper_playlist_item_new (uri);
gst_clapper_playlist_append (playlist, item);
gst_clapper_set_playlist (clapper, playlist);
gst_clapper_mpris_media_player2_player_complete_open_uri (player_skeleton, invocation); gst_clapper_mpris_media_player2_player_complete_open_uri (player_skeleton, invocation);
return TRUE; return TRUE;
@@ -771,8 +777,12 @@ gst_clapper_mpris_new (const gchar * own_name, const gchar * id_path,
const gchar * identity, const gchar * desktop_entry, const gchar * identity, const gchar * desktop_entry,
const gchar * default_art_url) const gchar * default_art_url)
{ {
return g_object_new (GST_TYPE_CLAPPER_MPRIS, GstClapperMpris *self;
self = g_object_new (GST_TYPE_CLAPPER,
"own-name", own_name, "id_path", id_path, "own-name", own_name, "id_path", id_path,
"identity", identity, "desktop-entry", desktop_entry, "identity", identity, "desktop-entry", desktop_entry,
"default-art-url", default_art_url, NULL); "default-art-url", default_art_url, NULL);
return self;
} }

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2021 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_CLAPPER_PLAYLIST_ITEM_PRIVATE_H__
#define __GST_CLAPPER_PLAYLIST_ITEM_PRIVATE_H__
#include "gstclapper-playlist.h"
struct _GstClapperPlaylistItem
{
GstObject parent;
/* ID of the playlist this item belongs to */
gchar *owner_uuid;
gint id;
gchar *uri;
gchar *suburi;
gchar *custom_title;
/* Signals */
gulong activated_signal_id;
};
struct _GstClapperPlaylistItemClass
{
GstObjectClass parent_class;
};
G_GNUC_INTERNAL
void gst_clapper_playlist_item_mark_added (GstClapperPlaylistItem *item, GstClapperPlaylist *playlist);
#endif /* __GST_CLAPPER_PLAYLIST_ITEM_PRIVATE_H__ */

View File

@@ -0,0 +1,281 @@
/*
* Copyright (C) 2021 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstclapper-playlist-item.h"
#include "gstclapper-playlist-item-private.h"
#include "gstclapper-playlist-private.h"
enum
{
PROP_0,
PROP_URI,
PROP_SUBURI,
PROP_CUSTOM_TITLE,
PROP_LAST
};
enum
{
SIGNAL_ACTIVATED,
SIGNAL_LAST
};
#define parent_class gst_clapper_playlist_item_parent_class
G_DEFINE_TYPE (GstClapperPlaylistItem, gst_clapper_playlist_item, GST_TYPE_OBJECT);
static guint signals[SIGNAL_LAST] = { 0, };
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
static void gst_clapper_playlist_item_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec);
static void gst_clapper_playlist_item_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec);
static void gst_clapper_playlist_item_dispose (GObject * object);
static void gst_clapper_playlist_item_finalize (GObject * object);
static void
gst_clapper_playlist_item_init (GstClapperPlaylistItem * self)
{
self->owner_uuid = NULL;
self->id = -1;
self->uri = NULL;
self->suburi = NULL;
self->custom_title = NULL;
}
static void
gst_clapper_playlist_item_class_init (GstClapperPlaylistItemClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
gobject_class->set_property = gst_clapper_playlist_item_set_property;
gobject_class->get_property = gst_clapper_playlist_item_get_property;
gobject_class->dispose = gst_clapper_playlist_item_dispose;
gobject_class->finalize = gst_clapper_playlist_item_finalize;
param_specs[PROP_URI] = g_param_spec_string ("uri",
"URI", "Playlist Item URI", NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
param_specs[PROP_SUBURI] = g_param_spec_string ("suburi",
"Subtitle URI", "Playlist Item Subtitle URI", NULL,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
param_specs[PROP_CUSTOM_TITLE] = g_param_spec_string ("custom-title",
"Custom Title", "Playlist Item Custom Title", NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
signals[SIGNAL_ACTIVATED] =
g_signal_new ("activated", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
NULL, NULL, G_TYPE_NONE, 0, G_TYPE_INVALID);
}
static void
gst_clapper_playlist_item_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)
{
GstClapperPlaylistItem *self = GST_CLAPPER_PLAYLIST_ITEM (object);
switch (prop_id) {
case PROP_URI:
self->uri = g_value_dup_string (value);
break;
case PROP_SUBURI:
g_free (self->suburi);
self->suburi = g_value_dup_string (value);
break;
case PROP_CUSTOM_TITLE:
self->custom_title = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_clapper_playlist_item_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec)
{
GstClapperPlaylistItem *self = GST_CLAPPER_PLAYLIST_ITEM (object);
switch (prop_id) {
case PROP_URI:
g_value_set_string (value, self->uri);
break;
case PROP_SUBURI:
g_value_set_string (value, self->suburi);
break;
case PROP_CUSTOM_TITLE:
g_value_set_string (value, self->custom_title);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_clapper_playlist_item_dispose (GObject * object)
{
GstClapperPlaylistItem *self = GST_CLAPPER_PLAYLIST_ITEM (object);
if (self->activated_signal_id) {
g_signal_handler_disconnect (self, self->activated_signal_id);
self->activated_signal_id = 0;
}
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
gst_clapper_playlist_item_finalize (GObject * object)
{
GstClapperPlaylistItem *self = GST_CLAPPER_PLAYLIST_ITEM (object);
g_free (self->owner_uuid);
g_free (self->uri);
g_free (self->suburi);
g_free (self->custom_title);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
item_activate_cb (GstClapperPlaylistItem * self, GParamSpec * pspec,
GstClapperPlaylist * playlist)
{
gst_clapper_playlist_emit_item_activated (playlist, self);
}
void
gst_clapper_playlist_item_mark_added (GstClapperPlaylistItem * self,
GstClapperPlaylist * playlist)
{
GST_OBJECT_LOCK (self);
self->owner_uuid = g_strdup (playlist->uuid);
self->id = playlist->id_count;
self->activated_signal_id = g_signal_connect (self, "activated",
G_CALLBACK (item_activate_cb), playlist);
GST_OBJECT_UNLOCK (self);
}
/**
* gst_clapper_playlist_item_new:
*
* Creates a new #GstClapperPlaylistItem.
*
* Returns: (transfer full): a new #GstClapperPlaylistItem object.
*/
GstClapperPlaylistItem *
gst_clapper_playlist_item_new (const gchar * uri)
{
return g_object_new (GST_TYPE_CLAPPER_PLAYLIST_ITEM, "uri", uri, NULL);
}
/**
* gst_clapper_playlist_item_new_titled:
* @uri: An URI pointing to media
* @custom_title: A custom title for this item
*
* Creates a new #GstClapperPlaylistItem with a custom title.
*
* Normally item title is obtained from media info or local filename,
* use this function for online sources where media title cannot be
* determined or if you want to override original title for some reason.
*
* Returns: (transfer full): a new #GstClapperPlaylistItem object.
*/
GstClapperPlaylistItem *
gst_clapper_playlist_item_new_titled (const gchar * uri,
const gchar * custom_title)
{
return g_object_new (GST_TYPE_CLAPPER_PLAYLIST_ITEM, "uri", uri,
"custom_title", custom_title, NULL);
}
/**
* gst_clapper_playlist_item_copy:
* @item: #GstClapperPlaylistItem
*
* Duplicates a #GstClapperPlaylistItem.
*
* Duplicated items do not belong to any playlist.
* Use this function if you want to append the same
* media into another #GstClapperPlaylist instance.
*
* Returns: (transfer full): a new #GstClapperPlaylistItem object.
*/
GstClapperPlaylistItem *
gst_clapper_playlist_item_copy (GstClapperPlaylistItem * source)
{
g_return_val_if_fail (GST_IS_CLAPPER_PLAYLIST_ITEM (source), NULL);
return g_object_new (GST_TYPE_CLAPPER_PLAYLIST_ITEM, "uri", source->uri,
"suburi", source->suburi, "custom-title", source->custom_title, NULL);
}
/**
* gst_clapper_playlist_item_set_suburi:
* @item: #GstClapperPlaylistItem
* @suburi: subtitle URI
*
* Sets the external subtitle URI.
*/
void
gst_clapper_playlist_item_set_suburi (GstClapperPlaylistItem * self,
const gchar * suburi)
{
/* TODO: When setting this property for an item that is currently active,
* it should be combined with a call to
* gst_clapper_set_subtitle_track_enabled(Clapper, TRUE),
* so the subtitles are actually rendered.
*/
g_return_if_fail (GST_IS_CLAPPER_PLAYLIST_ITEM (self));
g_object_set (self, "suburi", suburi, NULL);
}
/**
* gst_clapper_playlist_item_activate:
* @item: #GstClapperPlaylistItem
*
* Activates the #GstClapperPlaylistItem.
*/
void
gst_clapper_playlist_item_activate (GstClapperPlaylistItem * self)
{
g_return_if_fail (GST_IS_CLAPPER_PLAYLIST_ITEM (self));
g_signal_emit (self, signals[SIGNAL_ACTIVATED], 0);
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) 2021 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_CLAPPER_PLAYLIST_ITEM_H__
#define __GST_CLAPPER_PLAYLIST_ITEM_H__
#include <gst/clapper/clapper-prelude.h>
G_BEGIN_DECLS
typedef struct _GstClapperPlaylistItem GstClapperPlaylistItem;
typedef struct _GstClapperPlaylistItemClass GstClapperPlaylistItemClass;
#define GST_TYPE_CLAPPER_PLAYLIST_ITEM (gst_clapper_playlist_item_get_type ())
#define GST_IS_CLAPPER_PLAYLIST_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CLAPPER_PLAYLIST_ITEM))
#define GST_IS_CLAPPER_PLAYLIST_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_CLAPPER_PLAYLIST_ITEM))
#define GST_CLAPPER_PLAYLIST_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CLAPPER_PLAYLIST_ITEM, GstClapperPlaylistItemClass))
#define GST_CLAPPER_PLAYLIST_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_CLAPPER_PLAYLIST_ITEM, GstClapperPlaylistItem))
#define GST_CLAPPER_PLAYLIST_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_CLAPPER_PLAYLIST_ITEM, GstClapperPlaylistItemClass))
#define GST_CLAPPER_PLAYLIST_ITEM_CAST(obj) ((GstClapperPlaylistItem*)(obj))
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstClapperPlaylistItem, gst_object_unref)
#endif
GST_CLAPPER_API
GType gst_clapper_playlist_item_get_type (void);
GST_CLAPPER_API
GstClapperPlaylistItem * gst_clapper_playlist_item_new (const gchar *uri);
GST_CLAPPER_API
GstClapperPlaylistItem * gst_clapper_playlist_item_new_titled (const gchar *uri, const gchar *custom_title);
GST_CLAPPER_API
GstClapperPlaylistItem * gst_clapper_playlist_item_copy (GstClapperPlaylistItem *item);
GST_CLAPPER_API
void gst_clapper_playlist_item_set_suburi (GstClapperPlaylistItem *item, const gchar *suburi);
GST_CLAPPER_API
void gst_clapper_playlist_item_activate (GstClapperPlaylistItem *item);
G_END_DECLS
#endif /* __GST_CLAPPER_PLAYLIST_ITEM_H__ */

View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2021 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_CLAPPER_PLAYLIST_PRIVATE_H__
#define __GST_CLAPPER_PLAYLIST_PRIVATE_H__
#include "gstclapper-playlist.h"
struct _GstClapperPlaylist
{
GstObject parent;
gchar *uuid;
gint id_count;
GArray *items;
gint active_index;
};
struct _GstClapperPlaylistClass
{
GstObjectClass parent_class;
};
G_GNUC_INTERNAL
void gst_clapper_playlist_emit_item_activated (GstClapperPlaylist *playlist, GstClapperPlaylistItem *item);
#endif /* __GST_CLAPPER_PLAYLIST_PRIVATE_H__ */

275
lib/gst/clapper/gstclapper-playlist.c vendored Normal file
View File

@@ -0,0 +1,275 @@
/*
* Copyright (C) 2021 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstclapper-playlist.h"
#include "gstclapper-playlist-private.h"
#include "gstclapper-playlist-item.h"
#include "gstclapper-playlist-item-private.h"
enum
{
SIGNAL_ITEM_ACTIVATED,
SIGNAL_LAST
};
#define parent_class gst_clapper_playlist_parent_class
G_DEFINE_TYPE (GstClapperPlaylist, gst_clapper_playlist, GST_TYPE_OBJECT);
static guint signals[SIGNAL_LAST] = { 0, };
static void gst_clapper_playlist_dispose (GObject * object);
static void gst_clapper_playlist_finalize (GObject * object);
static void
gst_clapper_playlist_init (GstClapperPlaylist * self)
{
self->uuid = g_uuid_string_random ();
self->id_count = 0;
self->items = g_array_new (FALSE, FALSE, sizeof (GstClapperPlaylistItem));
self->active_index = -1;
g_array_set_clear_func (self->items, (GDestroyNotify) gst_object_unref);
}
static void
gst_clapper_playlist_class_init (GstClapperPlaylistClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
gobject_class->dispose = gst_clapper_playlist_dispose;
gobject_class->finalize = gst_clapper_playlist_finalize;
signals[SIGNAL_ITEM_ACTIVATED] =
g_signal_new ("item-activated", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_CLAPPER_PLAYLIST_ITEM);
}
static void
gst_clapper_playlist_dispose (GObject * object)
{
GstClapperPlaylist *self = GST_CLAPPER_PLAYLIST (object);
/* FIXME: Need this for something? */
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
gst_clapper_playlist_finalize (GObject * object)
{
GstClapperPlaylist *self = GST_CLAPPER_PLAYLIST (object);
g_free (self->uuid);
g_array_unref (self->items);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
void
gst_clapper_playlist_emit_item_activated (GstClapperPlaylist * self,
GstClapperPlaylistItem * item)
{
g_signal_emit (self, signals[SIGNAL_ITEM_ACTIVATED], 0, item);
}
/**
* gst_clapper_playlist_new:
*
* Creates a new #GstClapperPlaylist.
*
* Returns: (transfer full): a new #GstClapperPlaylist instance.
*/
GstClapperPlaylist *
gst_clapper_playlist_new (void)
{
return g_object_new (GST_TYPE_CLAPPER_PLAYLIST, NULL);
}
/**
* gst_clapper_playlist_append:
* @playlist: #GstClapperPlaylist
* @item: #GstClapperPlaylistItem to append
*
* Adds a new #GstClapperPlaylistItem to the end of playlist.
*
* Returns: %TRUE if the item was added successfully.
*/
gboolean
gst_clapper_playlist_append (GstClapperPlaylist * self, GstClapperPlaylistItem * item)
{
gboolean added = FALSE;
g_return_val_if_fail (GST_IS_CLAPPER_PLAYLIST (self), FALSE);
g_return_val_if_fail (GST_IS_CLAPPER_PLAYLIST_ITEM (item), FALSE);
g_return_val_if_fail (item->owner_uuid == NULL, FALSE);
GST_OBJECT_LOCK (self);
added = g_array_append_val (self->items, item) != NULL;
if (added) {
gst_clapper_playlist_item_mark_added (item, self);
self->id_count++;
}
GST_OBJECT_UNLOCK (self);
return added;
}
/**
* gst_clapper_playlist_get_length:
* @playlist: #GstClapperPlaylist
*
* Returns: Amount of items in playlist.
*/
guint
gst_clapper_playlist_get_length (GstClapperPlaylist * self)
{
guint len;
g_return_val_if_fail (GST_IS_CLAPPER_PLAYLIST (self), 0);
GST_OBJECT_LOCK (self);
len = self->items->len;
GST_OBJECT_UNLOCK (self);
return len;
}
/**
* gst_clapper_playlist_get_item_at_index:
* @playlist: #GstClapperPlaylist
*
* Returns: (transfer none): A #GstClapperPlaylistItem at given index.
*/
GstClapperPlaylistItem *
gst_clapper_playlist_get_item_at_index (GstClapperPlaylist * self, gint index)
{
GstClapperPlaylistItem *item = NULL;
g_return_val_if_fail (GST_IS_CLAPPER_PLAYLIST (self), NULL);
GST_OBJECT_LOCK (self);
if (index < self->items->len)
goto out;
item = &g_array_index (self->items, GstClapperPlaylistItem, index);
out:
GST_OBJECT_UNLOCK (self);
return item;
}
/**
* gst_clapper_playlist_get_active_item:
* @playlist: #GstClapperPlaylist
*
* Returns: (transfer none): A #GstClapperPlaylistItem that is
* currently playing.
*/
GstClapperPlaylistItem *
gst_clapper_playlist_get_active_item (GstClapperPlaylist * self)
{
gint active_index;
GST_OBJECT_LOCK (self);
active_index = self->active_index;
GST_OBJECT_UNLOCK (self);
return gst_clapper_playlist_get_item_at_index (self, active_index);
}
/**
* gst_clapper_playlist_remove_item_at_index:
* @playlist: #GstClapperPlaylist
* @index: Index of #GstClapperPlaylistItem to remove
*
* Removes item at given index from playlist.
*
* Returns: %TRUE if the item was removed successfully.
*/
gboolean
gst_clapper_playlist_remove_item_at_index (GstClapperPlaylist * self, guint index)
{
gboolean removed = FALSE;
g_return_val_if_fail (GST_IS_CLAPPER_PLAYLIST (self), FALSE);
GST_OBJECT_LOCK (self);
if (index >= self->items->len || index == self->active_index)
goto out;
removed = g_array_remove_index (self->items, index) != NULL;
out:
GST_OBJECT_UNLOCK (self);
return removed;
}
/**
* gst_clapper_playlist_remove_item:
* @playlist: #GstClapperPlaylist
* @item: #GstClapperPlaylistItem object to remove
*
* Removes given playlist item from playlist.
*
* Returns: %TRUE if the item was removed successfully.
*/
gboolean
gst_clapper_playlist_remove_item (GstClapperPlaylist * self,
GstClapperPlaylistItem * item)
{
gint i;
gboolean removed = FALSE;
g_return_val_if_fail (GST_IS_CLAPPER_PLAYLIST (self), FALSE);
g_return_val_if_fail (GST_IS_CLAPPER_PLAYLIST_ITEM (item), FALSE);
GST_OBJECT_LOCK (self);
if (strcmp (self->uuid, item->owner_uuid) != 0)
goto out;
for (i = 0; i < self->items->len; i++) {
GstClapperPlaylistItem *curr_item;
curr_item = &g_array_index (self->items, GstClapperPlaylistItem, i);
if (!curr_item)
goto out;
if (item->id == curr_item->id) {
removed = g_array_remove_index (self->items, i) != NULL;
break;
}
}
out:
GST_OBJECT_UNLOCK (self);
return removed;
}

71
lib/gst/clapper/gstclapper-playlist.h vendored Normal file
View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2021 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GST_CLAPPER_PLAYLIST_H__
#define __GST_CLAPPER_PLAYLIST_H__
#include <gst/clapper/clapper-prelude.h>
#include <gst/clapper/gstclapper-playlist-item.h>
G_BEGIN_DECLS
typedef struct _GstClapperPlaylist GstClapperPlaylist;
typedef struct _GstClapperPlaylistClass GstClapperPlaylistClass;
#define GST_TYPE_CLAPPER_PLAYLIST (gst_clapper_playlist_get_type ())
#define GST_IS_CLAPPER_PLAYLIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_CLAPPER_PLAYLIST))
#define GST_IS_CLAPPER_PLAYLIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_CLAPPER_PLAYLIST))
#define GST_CLAPPER_PLAYLIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_CLAPPER_PLAYLIST, GstClapperPlaylistClass))
#define GST_CLAPPER_PLAYLIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_CLAPPER_PLAYLIST, GstClapperPlaylist))
#define GST_CLAPPER_PLAYLIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_CLAPPER_PLAYLIST, GstClapperPlaylistClass))
#define GST_CLAPPER_PLAYLIST_CAST(obj) ((GstClapperPlaylist*)(obj))
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstClapperPlaylist, g_object_unref)
#endif
GST_CLAPPER_API
GType gst_clapper_playlist_get_type (void);
GST_CLAPPER_API
GstClapperPlaylist * gst_clapper_playlist_new (void);
GST_CLAPPER_API
gboolean gst_clapper_playlist_append (GstClapperPlaylist *playlist, GstClapperPlaylistItem *item);
GST_CLAPPER_API
guint gst_clapper_playlist_get_length (GstClapperPlaylist *playlist);
GST_CLAPPER_API
GstClapperPlaylistItem *
gst_clapper_playlist_get_item_at_index (GstClapperPlaylist *playlist, gint index);
GST_CLAPPER_API
GstClapperPlaylistItem *
gst_clapper_playlist_get_active_item (GstClapperPlaylist *playlist);
GST_CLAPPER_API
gboolean gst_clapper_playlist_remove_item_at_index (GstClapperPlaylist *playlist, guint index);
GST_CLAPPER_API
gboolean gst_clapper_playlist_remove_item (GstClapperPlaylist *playlist, GstClapperPlaylistItem *item);
G_END_DECLS
#endif /* __GST_CLAPPER_PLAYLIST_H__ */

View File

@@ -46,6 +46,7 @@
#include "gstclapper-signal-dispatcher-private.h" #include "gstclapper-signal-dispatcher-private.h"
#include "gstclapper-video-renderer-private.h" #include "gstclapper-video-renderer-private.h"
#include "gstclapper-media-info-private.h" #include "gstclapper-media-info-private.h"
#include "gstclapper-playlist-item-private.h"
#include "gstclapper-mpris-private.h" #include "gstclapper-mpris-private.h"
GST_DEBUG_CATEGORY_STATIC (gst_clapper_debug); GST_DEBUG_CATEGORY_STATIC (gst_clapper_debug);
@@ -79,8 +80,7 @@ enum
PROP_SIGNAL_DISPATCHER, PROP_SIGNAL_DISPATCHER,
PROP_MPRIS, PROP_MPRIS,
PROP_STATE, PROP_STATE,
PROP_URI, PROP_PLAYLIST,
PROP_SUBURI,
PROP_POSITION, PROP_POSITION,
PROP_DURATION, PROP_DURATION,
PROP_MEDIA_INFO, PROP_MEDIA_INFO,
@@ -134,6 +134,7 @@ struct _GstClapper
gchar *uri; gchar *uri;
gchar *redirect_uri; gchar *redirect_uri;
gchar *suburi; gchar *suburi;
gchar *custom_title;
GThread *thread; GThread *thread;
GMutex lock; GMutex lock;
@@ -188,6 +189,12 @@ struct _GstClapper
gchar *audio_sid; gchar *audio_sid;
gchar *subtitle_sid; gchar *subtitle_sid;
gulong stream_notify_id; gulong stream_notify_id;
/* For playlist */
GstClapperPlaylist *playlist;
GstClapperPlaylistItem *active_item;
gulong item_activated_id;
gulong suburi_notify_id;
}; };
struct _GstClapperClass struct _GstClapperClass
@@ -320,12 +327,8 @@ gst_clapper_class_init (GstClapperClass * klass)
GST_TYPE_CLAPPER_STATE, DEFAULT_STATE, G_PARAM_READABLE | GST_TYPE_CLAPPER_STATE, DEFAULT_STATE, G_PARAM_READABLE |
G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
param_specs[PROP_URI] = g_param_spec_string ("uri", "URI", "Current URI", param_specs[PROP_PLAYLIST] = g_param_spec_string ("playlist", "Playlist",
DEFAULT_URI, G_PARAM_READWRITE | "Current Playlist", NULL, G_PARAM_READWRITE |
G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
param_specs[PROP_SUBURI] = g_param_spec_string ("suburi", "Subtitle URI",
"Current Subtitle URI", NULL, G_PARAM_READWRITE |
G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
param_specs[PROP_POSITION] = param_specs[PROP_POSITION] =
@@ -519,6 +522,8 @@ gst_clapper_finalize (GObject * object)
g_object_unref (self->signal_dispatcher); g_object_unref (self->signal_dispatcher);
if (self->mpris) if (self->mpris)
g_object_unref (self->mpris); g_object_unref (self->mpris);
if (self->playlist)
gst_object_unref (self->playlist);
if (self->current_vis_element) if (self->current_vis_element)
gst_object_unref (self->current_vis_element); gst_object_unref (self->current_vis_element);
if (self->collection) if (self->collection)
@@ -567,35 +572,6 @@ uri_loaded_signal_data_free (UriLoadedSignalData * data)
g_free (data); g_free (data);
} }
static gboolean
gst_clapper_set_uri_internal (gpointer user_data)
{
GstClapper *self = user_data;
gst_clapper_stop_internal (self, FALSE);
g_mutex_lock (&self->lock);
GST_DEBUG_OBJECT (self, "Changing URI to '%s'", GST_STR_NULL (self->uri));
g_object_set (self->playbin, "uri", self->uri, NULL);
g_object_set (self->playbin, "suburi", NULL, NULL);
self->can_start = TRUE;
if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
signals[SIGNAL_URI_LOADED], 0, NULL, NULL, NULL) != 0) {
UriLoadedSignalData *data = g_new (UriLoadedSignalData, 1);
data->clapper = g_object_ref (self);
data->uri = g_strdup (self->uri);
gst_clapper_signal_dispatcher_dispatch (self->signal_dispatcher, self,
uri_loaded_dispatch, data,
(GDestroyNotify) uri_loaded_signal_data_free);
}
g_mutex_unlock (&self->lock);
return G_SOURCE_REMOVE;
}
static gboolean static gboolean
gst_clapper_set_suburi_internal (gpointer user_data) gst_clapper_set_suburi_internal (gpointer user_data)
{ {
@@ -628,6 +604,100 @@ gst_clapper_set_suburi_internal (gpointer user_data)
return G_SOURCE_REMOVE; return G_SOURCE_REMOVE;
} }
static void
suburi_notify_cb (GstClapperPlaylistItem * item, GParamSpec * pspec,
GstClapper * self)
{
g_mutex_lock (&self->lock);
g_free (self->suburi);
self->suburi = g_strdup (item->suburi);
GST_DEBUG_OBJECT (self, "Set suburi: %s", self->suburi);
g_mutex_unlock (&self->lock);
g_main_context_invoke_full (self->context, G_PRIORITY_DEFAULT + 1,
gst_clapper_set_suburi_internal, self, NULL);
}
static void
gst_clapper_set_playlist_item_locked (GstClapper * self,
GstClapperPlaylistItem * item)
{
if (self->suburi_notify_id)
g_signal_handler_disconnect (self->active_item, self->suburi_notify_id);
gst_object_replace ((GstObject **) & self->active_item,
(GstObject *) item);
g_free (self->uri);
self->uri = g_strdup (self->active_item->uri);
g_free (self->redirect_uri);
self->redirect_uri = NULL;
g_free (self->suburi);
self->suburi = g_strdup (self->active_item->suburi);
g_free (self->custom_title);
self->custom_title = g_strdup (self->active_item->custom_title);
GST_DEBUG_OBJECT (self, "Changing URI to '%s'",
GST_STR_NULL (self->uri));
g_object_set (self->playbin, "uri", self->uri, NULL);
GST_DEBUG_OBJECT (self, "Changing SUBURI to '%s'",
GST_STR_NULL (self->suburi));
g_object_set (self->playbin, "suburi", self->suburi, NULL);
self->suburi_notify_id = g_signal_connect (self->active_item,
"notify::suburi", G_CALLBACK (suburi_notify_cb), self);
self->can_start = TRUE;
if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
signals[SIGNAL_URI_LOADED], 0, NULL, NULL, NULL) != 0) {
UriLoadedSignalData *data = g_new (UriLoadedSignalData, 1);
data->clapper = g_object_ref (self);
data->uri = g_strdup (self->uri);
gst_clapper_signal_dispatcher_dispatch (self->signal_dispatcher, self,
uri_loaded_dispatch, data,
(GDestroyNotify) uri_loaded_signal_data_free);
}
}
static gboolean
gst_clapper_set_playlist_internal (gpointer user_data)
{
GstClapper *self = user_data;
GstClapperPlaylistItem *item;
gst_clapper_stop_internal (self, FALSE);
g_mutex_lock (&self->lock);
item = gst_clapper_playlist_get_item_at_index (self->playlist, 0);
if (!item) {
GST_DEBUG_OBJECT (self, "Set empty playlist");
goto out;
}
gst_clapper_set_playlist_item_locked (self, item);
out:
g_mutex_unlock (&self->lock);
return G_SOURCE_REMOVE;
}
static void
item_activated_cb (G_GNUC_UNUSED GstClapperPlaylist * playlist,
GstClapperPlaylistItem * item, gpointer user_data)
{
GstClapper *self = GST_CLAPPER (user_data);
g_mutex_lock (&self->lock);
gst_clapper_set_playlist_item_locked (self, item);
g_mutex_unlock (&self->lock);
}
static void static void
gst_clapper_set_rate_internal (GstClapper * self) gst_clapper_set_rate_internal (GstClapper * self)
{ {
@@ -664,35 +734,21 @@ gst_clapper_set_property (GObject * object, guint prop_id,
case PROP_MPRIS: case PROP_MPRIS:
self->mpris = g_value_dup_object (value); self->mpris = g_value_dup_object (value);
break; break;
case PROP_URI:{ case PROP_PLAYLIST:
g_mutex_lock (&self->lock); g_mutex_lock (&self->lock);
g_free (self->uri); if (self->playlist) {
g_free (self->redirect_uri); if (self->item_activated_id)
self->redirect_uri = NULL; g_signal_handler_disconnect (self->playlist, self->item_activated_id);
gst_object_unref (self->playlist);
g_free (self->suburi); }
self->suburi = NULL; self->playlist = g_value_dup_object (value);
self->item_activated_id = g_signal_connect (self->playlist, "item-activated",
self->uri = g_value_dup_string (value); G_CALLBACK (item_activated_cb), self);
GST_DEBUG_OBJECT (self, "Set uri=%s", self->uri);
g_mutex_unlock (&self->lock); g_mutex_unlock (&self->lock);
g_main_context_invoke_full (self->context, G_PRIORITY_DEFAULT, g_main_context_invoke_full (self->context, G_PRIORITY_DEFAULT,
gst_clapper_set_uri_internal, self, NULL); gst_clapper_set_playlist_internal, self, NULL);
break; break;
}
case PROP_SUBURI:{
g_mutex_lock (&self->lock);
g_free (self->suburi);
self->suburi = g_value_dup_string (value);
GST_DEBUG_OBJECT (self, "Set suburi=%s", self->suburi);
g_mutex_unlock (&self->lock);
g_main_context_invoke_full (self->context, G_PRIORITY_DEFAULT,
gst_clapper_set_suburi_internal, self, NULL);
break;
}
case PROP_VOLUME: { case PROP_VOLUME: {
GValue volume_linear = G_VALUE_INIT; GValue volume_linear = G_VALUE_INIT;
gdouble volume = g_value_get_double (value); gdouble volume = g_value_get_double (value);
@@ -757,27 +813,13 @@ gst_clapper_get_property (GObject * object, guint prop_id,
switch (prop_id) { switch (prop_id) {
case PROP_MPRIS: case PROP_MPRIS:
g_mutex_lock (&self->lock);
g_value_set_object (value, self->mpris); g_value_set_object (value, self->mpris);
g_mutex_unlock (&self->lock);
break; break;
case PROP_STATE: case PROP_STATE:
g_mutex_lock (&self->lock); g_mutex_lock (&self->lock);
g_value_set_enum (value, self->app_state); g_value_set_enum (value, self->app_state);
g_mutex_unlock (&self->lock); g_mutex_unlock (&self->lock);
break; break;
case PROP_URI:
g_mutex_lock (&self->lock);
g_value_set_string (value, self->uri);
g_mutex_unlock (&self->lock);
break;
case PROP_SUBURI:
g_mutex_lock (&self->lock);
g_value_set_string (value, self->suburi);
g_mutex_unlock (&self->lock);
GST_DEBUG_OBJECT (self, "Returning suburi=%s",
g_value_get_string (value));
break;
case PROP_POSITION:{ case PROP_POSITION:{
gint64 position = GST_CLOCK_TIME_NONE; gint64 position = GST_CLOCK_TIME_NONE;
@@ -787,12 +829,11 @@ gst_clapper_get_property (GObject * object, guint prop_id,
GST_TIME_ARGS (g_value_get_uint64 (value))); GST_TIME_ARGS (g_value_get_uint64 (value)));
break; break;
} }
case PROP_DURATION:{ case PROP_DURATION:
g_value_set_uint64 (value, self->cached_duration); g_value_set_uint64 (value, self->cached_duration);
GST_TRACE_OBJECT (self, "Returning duration=%" GST_TIME_FORMAT, GST_TRACE_OBJECT (self, "Returning duration=%" GST_TIME_FORMAT,
GST_TIME_ARGS (g_value_get_uint64 (value))); GST_TIME_ARGS (g_value_get_uint64 (value)));
break; break;
}
case PROP_MEDIA_INFO:{ case PROP_MEDIA_INFO:{
GstClapperMediaInfo *media_info = gst_clapper_get_media_info (self); GstClapperMediaInfo *media_info = gst_clapper_get_media_info (self);
g_value_take_object (value, media_info); g_value_take_object (value, media_info);
@@ -839,20 +880,18 @@ gst_clapper_get_property (GObject * object, guint prop_id,
case PROP_PIPELINE: case PROP_PIPELINE:
g_value_set_object (value, self->playbin); g_value_set_object (value, self->playbin);
break; break;
case PROP_VIDEO_MULTIVIEW_MODE:{ case PROP_VIDEO_MULTIVIEW_MODE:
g_object_get_property (G_OBJECT (self->playbin), "video-multiview-mode", g_object_get_property (G_OBJECT (self->playbin), "video-multiview-mode",
value); value);
GST_TRACE_OBJECT (self, "Return multiview mode=%d", GST_TRACE_OBJECT (self, "Return multiview mode=%d",
g_value_get_enum (value)); g_value_get_enum (value));
break; break;
} case PROP_VIDEO_MULTIVIEW_FLAGS:
case PROP_VIDEO_MULTIVIEW_FLAGS:{
g_object_get_property (G_OBJECT (self->playbin), "video-multiview-flags", g_object_get_property (G_OBJECT (self->playbin), "video-multiview-flags",
value); value);
GST_TRACE_OBJECT (self, "Return multiview flags=%x", GST_TRACE_OBJECT (self, "Return multiview flags=%x",
g_value_get_flags (value)); g_value_get_flags (value));
break; break;
}
case PROP_AUDIO_VIDEO_OFFSET: case PROP_AUDIO_VIDEO_OFFSET:
g_object_get_property (G_OBJECT (self->playbin), "av-offset", value); g_object_get_property (G_OBJECT (self->playbin), "av-offset", value);
break; break;
@@ -1618,7 +1657,7 @@ static void
emit_duration_changed (GstClapper * self, GstClockTime duration) emit_duration_changed (GstClapper * self, GstClockTime duration)
{ {
if (self->cached_duration == duration if (self->cached_duration == duration
|| self->cached_duration / (250 * GST_MSECOND) == duration / (250 * GST_MSECOND)) || self->cached_duration / GST_MSECOND == duration / GST_MSECOND)
return; return;
GST_DEBUG_OBJECT (self, "Duration changed %" GST_TIME_FORMAT, GST_DEBUG_OBJECT (self, "Duration changed %" GST_TIME_FORMAT,
@@ -1806,12 +1845,14 @@ request_state_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg,
static void static void
media_info_update (GstClapper * self, GstClapperMediaInfo * info) media_info_update (GstClapper * self, GstClapperMediaInfo * info)
{ {
if (!self->custom_title) {
/* Update title from new tags or leave the title from URI */ /* Update title from new tags or leave the title from URI */
gchar *tags_title = get_from_tags (self, info, get_title); gchar *tags_title = get_from_tags (self, info, get_title);
if (tags_title) { if (tags_title) {
g_free (info->title); g_free (info->title);
info->title = tags_title; info->title = tags_title;
} }
}
g_free (info->container); g_free (info->container);
info->container = get_from_tags (self, info, get_container_format); info->container = get_from_tags (self, info, get_container_format);
@@ -2819,6 +2860,9 @@ gst_clapper_media_info_create (GstClapper * self)
GST_TYPE_CLAPPER_SUBTITLE_INFO); GST_TYPE_CLAPPER_SUBTITLE_INFO);
} }
if (self->custom_title)
media_info->title = g_strdup (self->custom_title);
if (!media_info->title)
media_info->title = get_from_tags (self, media_info, get_title); media_info->title = get_from_tags (self, media_info, get_title);
if (!media_info->title) if (!media_info->title)
media_info->title = get_title_from_uri (self->uri); media_info->title = get_title_from_uri (self->uri);
@@ -3632,77 +3676,19 @@ gst_clapper_get_state (GstClapper * self)
} }
/** /**
* gst_clapper_get_uri: * gst_clapper_set_playlist:
* @clapper: #GstClapper instance * @clapper: #GstClapper instance
* @playlist: #GstClapperPlaylist instance
* *
* Gets the URI of the currently-playing stream. * Sets the new #GstClapperPlaylist
*
* Returns: (transfer full): a string containing the URI of the
* currently-playing stream. g_free() after usage.
*/
gchar *
gst_clapper_get_uri (GstClapper * self)
{
gchar *val;
g_return_val_if_fail (GST_IS_CLAPPER (self), DEFAULT_URI);
g_object_get (self, "uri", &val, NULL);
return val;
}
/**
* gst_clapper_set_uri:
* @clapper: #GstClapper instance
* @uri: next URI to play.
*
* Sets the next URI to play.
*/ */
void void
gst_clapper_set_uri (GstClapper * self, const gchar * val) gst_clapper_set_playlist (GstClapper * self, GstClapperPlaylist * val)
{ {
g_return_if_fail (GST_IS_CLAPPER (self)); g_return_if_fail (GST_IS_CLAPPER (self));
g_return_if_fail (GST_IS_CLAPPER_PLAYLIST (val));
g_object_set (self, "uri", val, NULL); g_object_set (self, "playlist", val, NULL);
}
/**
* gst_clapper_set_subtitle_uri:
* @clapper: #GstClapper instance
* @uri: subtitle URI
*
* Sets the external subtitle URI. This should be combined with a call to
* gst_clapper_set_subtitle_track_enabled(@clapper, TRUE) so the subtitles are actually
* rendered.
*/
void
gst_clapper_set_subtitle_uri (GstClapper * self, const gchar * suburi)
{
g_return_if_fail (GST_IS_CLAPPER (self));
g_object_set (self, "suburi", suburi, NULL);
}
/**
* gst_clapper_get_subtitle_uri:
* @clapper: #GstClapper instance
*
* current subtitle URI
*
* Returns: (transfer full): URI of the current external subtitle.
* g_free() after usage.
*/
gchar *
gst_clapper_get_subtitle_uri (GstClapper * self)
{
gchar *val = NULL;
g_return_val_if_fail (GST_IS_CLAPPER (self), NULL);
g_object_get (self, "suburi", &val, NULL);
return val;
} }
/** /**

View File

@@ -30,6 +30,8 @@
#include <gst/clapper/gstclapper-signal-dispatcher.h> #include <gst/clapper/gstclapper-signal-dispatcher.h>
#include <gst/clapper/gstclapper-video-renderer.h> #include <gst/clapper/gstclapper-video-renderer.h>
#include <gst/clapper/gstclapper-media-info.h> #include <gst/clapper/gstclapper-media-info.h>
#include <gst/clapper/gstclapper-playlist.h>
#include <gst/clapper/gstclapper-playlist-item.h>
#include <gst/clapper/gstclapper-mpris.h> #include <gst/clapper/gstclapper-mpris.h>
G_BEGIN_DECLS G_BEGIN_DECLS
@@ -193,16 +195,7 @@ GST_CLAPPER_API
gdouble gst_clapper_get_rate (GstClapper *clapper); gdouble gst_clapper_get_rate (GstClapper *clapper);
GST_CLAPPER_API GST_CLAPPER_API
gchar * gst_clapper_get_uri (GstClapper *clapper); void gst_clapper_set_playlist (GstClapper *clapper, GstClapperPlaylist *playlist);
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 GST_CLAPPER_API
GstClockTime gst_clapper_get_position (GstClapper *clapper); GstClockTime gst_clapper_get_position (GstClapper *clapper);

View File

@@ -37,6 +37,10 @@
GST_DEBUG_CATEGORY (gst_debug_clapper_gl_sink); GST_DEBUG_CATEGORY (gst_debug_clapper_gl_sink);
#define GST_CAT_DEFAULT gst_debug_clapper_gl_sink #define GST_CAT_DEFAULT gst_debug_clapper_gl_sink
#define DEFAULT_FORCE_ASPECT_RATIO TRUE
#define DEFAULT_PAR_N 0
#define DEFAULT_PAR_D 1
static GstStaticPadTemplate gst_clapper_gl_sink_template = static GstStaticPadTemplate gst_clapper_gl_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink", GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK, GST_PAD_SINK,
@@ -58,7 +62,6 @@ static gboolean gst_clapper_gl_sink_propose_allocation (GstBaseSink * bsink,
static gboolean gst_clapper_gl_sink_query (GstBaseSink * bsink, GstQuery * query); static gboolean gst_clapper_gl_sink_query (GstBaseSink * bsink, GstQuery * query);
static gboolean gst_clapper_gl_sink_start (GstBaseSink * bsink); static gboolean gst_clapper_gl_sink_start (GstBaseSink * bsink);
static gboolean gst_clapper_gl_sink_stop (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 static GstStateChangeReturn
gst_clapper_gl_sink_change_state (GstElement * element, gst_clapper_gl_sink_change_state (GstElement * element,
@@ -76,6 +79,14 @@ static GstFlowReturn gst_clapper_gl_sink_show_frame (GstVideoSink * bsink,
static void static void
gst_clapper_gl_sink_navigation_interface_init (GstNavigationInterface * iface); gst_clapper_gl_sink_navigation_interface_init (GstNavigationInterface * iface);
enum
{
PROP_0,
PROP_WIDGET,
PROP_FORCE_ASPECT_RATIO,
PROP_PIXEL_ASPECT_RATIO,
};
#define gst_clapper_gl_sink_parent_class parent_class #define gst_clapper_gl_sink_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstClapperGLSink, gst_clapper_gl_sink, G_DEFINE_TYPE_WITH_CODE (GstClapperGLSink, gst_clapper_gl_sink,
GST_TYPE_VIDEO_SINK, GST_TYPE_VIDEO_SINK,
@@ -101,7 +112,6 @@ gst_clapper_gl_sink_class_init (GstClapperGLSinkClass * klass)
gobject_class->set_property = gst_clapper_gl_sink_set_property; gobject_class->set_property = gst_clapper_gl_sink_set_property;
gobject_class->get_property = gst_clapper_gl_sink_get_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_object_class_install_property (gobject_class, PROP_WIDGET,
g_param_spec_object ("widget", "GTK Widget", g_param_spec_object ("widget", "GTK Widget",
@@ -109,7 +119,19 @@ gst_clapper_gl_sink_class_init (GstClapperGLSinkClass * klass)
"(must only be get from the GTK main thread)", "(must only be get from the GTK main thread)",
GTK_TYPE_WIDGET, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); GTK_TYPE_WIDGET, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
gst_gtk_install_shared_properties (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));
gobject_class->finalize = gst_clapper_gl_sink_finalize;
gstelement_class->change_state = gst_clapper_gl_sink_change_state; gstelement_class->change_state = gst_clapper_gl_sink_change_state;
@@ -120,7 +142,6 @@ gst_clapper_gl_sink_class_init (GstClapperGLSinkClass * klass)
gstbasesink_class->query = gst_clapper_gl_sink_query; gstbasesink_class->query = gst_clapper_gl_sink_query;
gstbasesink_class->start = gst_clapper_gl_sink_start; gstbasesink_class->start = gst_clapper_gl_sink_start;
gstbasesink_class->stop = gst_clapper_gl_sink_stop; 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; gstvideosink_class->show_frame = gst_clapper_gl_sink_show_frame;
@@ -145,9 +166,6 @@ gst_clapper_gl_sink_init (GstClapperGLSink * clapper_sink)
clapper_sink->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO; clapper_sink->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
clapper_sink->par_n = DEFAULT_PAR_N; clapper_sink->par_n = DEFAULT_PAR_N;
clapper_sink->par_d = DEFAULT_PAR_D; clapper_sink->par_d = DEFAULT_PAR_D;
clapper_sink->keep_last_frame = DEFAULT_KEEP_LAST_FRAME;
clapper_sink->had_eos = FALSE;
} }
static void static void
@@ -210,12 +228,12 @@ gst_clapper_gl_sink_get_widget (GstClapperGLSink * clapper_sink)
clapper_sink->widget = (GtkClapperGLWidget *) clapper_sink->widget = (GtkClapperGLWidget *)
GST_CLAPPER_GL_SINK_GET_CLASS (clapper_sink)->create_widget (); GST_CLAPPER_GL_SINK_GET_CLASS (clapper_sink)->create_widget ();
clapper_sink->bind_aspect_ratio =
g_object_bind_property (clapper_sink, "force-aspect-ratio", clapper_sink->widget, g_object_bind_property (clapper_sink, "force-aspect-ratio", clapper_sink->widget,
"force-aspect-ratio", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); "force-aspect-ratio", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
clapper_sink->bind_pixel_aspect_ratio =
g_object_bind_property (clapper_sink, "pixel-aspect-ratio", clapper_sink->widget, g_object_bind_property (clapper_sink, "pixel-aspect-ratio", clapper_sink->widget,
"pixel-aspect-ratio", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); "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 /* Take the floating ref, other wise the destruction of the container will
* make this widget disappear possibly before we are done. */ * make this widget disappear possibly before we are done. */
@@ -261,9 +279,6 @@ gst_clapper_gl_sink_get_property (GObject * object, guint prop_id,
case PROP_PIXEL_ASPECT_RATIO: case PROP_PIXEL_ASPECT_RATIO:
gst_value_set_fraction (value, clapper_sink->par_n, clapper_sink->par_d); gst_value_set_fraction (value, clapper_sink->par_n, clapper_sink->par_d);
break; break;
case PROP_KEEP_LAST_FRAME:
g_value_set_boolean (value, clapper_sink->keep_last_frame);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@@ -284,9 +299,6 @@ gst_clapper_gl_sink_set_property (GObject * object, guint prop_id,
clapper_sink->par_n = gst_value_get_fraction_numerator (value); clapper_sink->par_n = gst_value_get_fraction_numerator (value);
clapper_sink->par_d = gst_value_get_fraction_denominator (value); clapper_sink->par_d = gst_value_get_fraction_denominator (value);
break; break;
case PROP_KEEP_LAST_FRAME:
clapper_sink->keep_last_frame = g_value_get_boolean (value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@@ -577,7 +589,6 @@ gst_clapper_gl_sink_change_state (GstElement * element, GstStateChange transitio
switch (transition) { switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY: case GST_STATE_CHANGE_NULL_TO_READY:
GST_OBJECT_LOCK (clapper_sink); GST_OBJECT_LOCK (clapper_sink);
clapper_sink->had_eos = FALSE;
if (clapper_sink->widget) { if (clapper_sink->widget) {
GTK_CLAPPER_GL_WIDGET_LOCK (clapper_sink->widget); GTK_CLAPPER_GL_WIDGET_LOCK (clapper_sink->widget);
clapper_sink->widget->ignore_buffers = FALSE; clapper_sink->widget->ignore_buffers = FALSE;
@@ -603,8 +614,7 @@ gst_clapper_gl_sink_change_state (GstElement * element, GstStateChange transitio
GST_OBJECT_LOCK (clapper_sink); GST_OBJECT_LOCK (clapper_sink);
if (clapper_sink->widget) { if (clapper_sink->widget) {
GTK_CLAPPER_GL_WIDGET_LOCK (clapper_sink->widget); GTK_CLAPPER_GL_WIDGET_LOCK (clapper_sink->widget);
clapper_sink->widget->ignore_buffers = clapper_sink->widget->ignore_buffers = TRUE;
!clapper_sink->had_eos || !clapper_sink->keep_last_frame;
GTK_CLAPPER_GL_WIDGET_UNLOCK (clapper_sink->widget); GTK_CLAPPER_GL_WIDGET_UNLOCK (clapper_sink->widget);
} }
GST_OBJECT_UNLOCK (clapper_sink); GST_OBJECT_UNLOCK (clapper_sink);
@@ -696,29 +706,6 @@ gst_clapper_gl_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
return TRUE; return TRUE;
} }
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 static GstFlowReturn
gst_clapper_gl_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf) gst_clapper_gl_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
{ {

View File

@@ -58,14 +58,15 @@ struct _GstClapperGLSink
GtkClapperGLWidget *widget; GtkClapperGLWidget *widget;
gboolean had_eos;
/* properties */ /* properties */
gboolean force_aspect_ratio; gboolean force_aspect_ratio;
GBinding *bind_aspect_ratio;
gint par_n, par_d; gint par_n, par_d;
gboolean keep_last_frame; GBinding *bind_pixel_aspect_ratio;
gboolean ignore_textures; gboolean ignore_textures;
GBinding *bind_ignore_textures;
GtkWidget *window; GtkWidget *window;
gulong widget_destroy_id; gulong widget_destroy_id;

View File

@@ -19,7 +19,6 @@
* Boston, MA 02110-1301, USA. * Boston, MA 02110-1301, USA.
*/ */
#include <gst/gst.h>
#include "gstgtkutils.h" #include "gstgtkutils.h"
struct invoke_context struct invoke_context
@@ -70,26 +69,3 @@ gst_gtk_invoke_on_main (GThreadFunc func, gpointer data)
return info.res; return info.res;
} }
void
gst_gtk_install_shared_properties (GObjectClass *gobject_class)
{
g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
g_param_spec_boolean ("force-aspect-ratio",
"Force aspect ratio",
"When enabled, scaling will respect original aspect ratio",
DEFAULT_FORCE_ASPECT_RATIO,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio",
"The pixel aspect ratio of the device", DEFAULT_PAR_N, DEFAULT_PAR_D,
G_MAXINT, 1, 1, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_KEEP_LAST_FRAME,
g_param_spec_boolean ("keep-last-frame",
"Keep last frame",
"Keep showing last video frame after playback instead of black screen",
DEFAULT_KEEP_LAST_FRAME,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}

View File

@@ -22,25 +22,8 @@
#ifndef __GST_GTK_UTILS_H__ #ifndef __GST_GTK_UTILS_H__
#define __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.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); gpointer gst_gtk_invoke_on_main (GThreadFunc func, gpointer data);
void gst_gtk_install_shared_properties (GObjectClass *gobject_class);
#endif /* __GST_GTK_UTILS_H__ */ #endif /* __GST_GTK_UTILS_H__ */

View File

@@ -57,6 +57,10 @@
GST_DEBUG_CATEGORY (gst_debug_clapper_gl_widget); GST_DEBUG_CATEGORY (gst_debug_clapper_gl_widget);
#define GST_CAT_DEFAULT gst_debug_clapper_gl_widget #define GST_CAT_DEFAULT gst_debug_clapper_gl_widget
#define DEFAULT_FORCE_ASPECT_RATIO TRUE
#define DEFAULT_PAR_N 0
#define DEFAULT_PAR_D 1
struct _GtkClapperGLWidgetPrivate struct _GtkClapperGLWidgetPrivate
{ {
gboolean initiated; gboolean initiated;
@@ -90,6 +94,13 @@ G_DEFINE_TYPE_WITH_CODE (GtkClapperGLWidget, gtk_clapper_gl_widget, GTK_TYPE_GL_
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gtkclapperglwidget", 0, GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "gtkclapperglwidget", 0,
"GTK Clapper GL Widget")); "GTK Clapper GL Widget"));
enum
{
PROP_0,
PROP_FORCE_ASPECT_RATIO,
PROP_PIXEL_ASPECT_RATIO,
};
static void static void
gtk_clapper_gl_widget_get_preferred_width (GtkWidget * widget, gint * min, gtk_clapper_gl_widget_get_preferred_width (GtkWidget * widget, gint * min,
gint * natural) gint * natural)
@@ -163,9 +174,6 @@ gtk_clapper_gl_widget_set_property (GObject * object, guint prop_id,
clapper_widget->par_n = gst_value_get_fraction_numerator (value); clapper_widget->par_n = gst_value_get_fraction_numerator (value);
clapper_widget->par_d = gst_value_get_fraction_denominator (value); clapper_widget->par_d = gst_value_get_fraction_denominator (value);
break; break;
case PROP_KEEP_LAST_FRAME:
clapper_widget->keep_last_frame = g_value_get_boolean (value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@@ -185,9 +193,6 @@ gtk_clapper_gl_widget_get_property (GObject * object, guint prop_id,
case PROP_PIXEL_ASPECT_RATIO: case PROP_PIXEL_ASPECT_RATIO:
gst_value_set_fraction (value, clapper_widget->par_n, clapper_widget->par_d); gst_value_set_fraction (value, clapper_widget->par_n, clapper_widget->par_d);
break; break;
case PROP_KEEP_LAST_FRAME:
g_value_set_boolean (value, clapper_widget->keep_last_frame);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@@ -904,20 +909,30 @@ _get_gl_context (GtkClapperGLWidget * clapper_widget)
static void static void
gtk_clapper_gl_widget_class_init (GtkClapperGLWidgetClass * klass) gtk_clapper_gl_widget_class_init (GtkClapperGLWidgetClass * klass)
{ {
GObjectClass *gobject_class = (GObjectClass *) klass; GObjectClass *gobject_klass = (GObjectClass *) klass;
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass; GtkWidgetClass *widget_klass = (GtkWidgetClass *) klass;
GtkGLAreaClass *gl_area_class = (GtkGLAreaClass *) klass; GtkGLAreaClass *gl_area_klass = (GtkGLAreaClass *) klass;
gobject_class->set_property = gtk_clapper_gl_widget_set_property; gobject_klass->set_property = gtk_clapper_gl_widget_set_property;
gobject_class->get_property = gtk_clapper_gl_widget_get_property; gobject_klass->get_property = gtk_clapper_gl_widget_get_property;
gobject_class->finalize = gtk_clapper_gl_widget_finalize; gobject_klass->finalize = gtk_clapper_gl_widget_finalize;
gst_gtk_install_shared_properties (gobject_class); g_object_class_install_property (gobject_klass, PROP_FORCE_ASPECT_RATIO,
g_param_spec_boolean ("force-aspect-ratio",
"Force aspect ratio",
"When enabled, scaling will respect original aspect ratio",
DEFAULT_FORCE_ASPECT_RATIO,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
widget_class->measure = gtk_clapper_gl_widget_measure; g_object_class_install_property (gobject_klass, PROP_PIXEL_ASPECT_RATIO,
widget_class->size_allocate = gtk_clapper_gl_widget_size_allocate; 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));
gl_area_class->render = gtk_clapper_gl_widget_render; widget_klass->measure = gtk_clapper_gl_widget_measure;
widget_klass->size_allocate = gtk_clapper_gl_widget_size_allocate;
gl_area_klass->render = gtk_clapper_gl_widget_render;
} }
static void static void
@@ -930,7 +945,6 @@ gtk_clapper_gl_widget_init (GtkClapperGLWidget * clapper_widget)
clapper_widget->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO; clapper_widget->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
clapper_widget->par_n = DEFAULT_PAR_N; clapper_widget->par_n = DEFAULT_PAR_N;
clapper_widget->par_d = DEFAULT_PAR_D; clapper_widget->par_d = DEFAULT_PAR_D;
clapper_widget->keep_last_frame = DEFAULT_KEEP_LAST_FRAME;
clapper_widget->ignore_buffers = FALSE; clapper_widget->ignore_buffers = FALSE;
clapper_widget->last_pos_x = 0; clapper_widget->last_pos_x = 0;
clapper_widget->last_pos_y = 0; clapper_widget->last_pos_y = 0;
@@ -979,7 +993,7 @@ gtk_clapper_gl_widget_init (GtkClapperGLWidget * clapper_widget)
#if GST_GL_HAVE_WINDOW_X11 && defined (GDK_WINDOWING_X11) #if GST_GL_HAVE_WINDOW_X11 && defined (GDK_WINDOWING_X11)
if (GDK_IS_X11_DISPLAY (display)) { if (GDK_IS_X11_DISPLAY (display)) {
gpointer display_ptr; gpointer display_ptr;
#if GST_GL_HAVE_PLATFORM_EGL && GTK_CHECK_VERSION(4,3,1) #if GST_GL_HAVE_PLATFORM_EGL && GTK_CHECK_VERSION(4,4,0)
display_ptr = gdk_x11_display_get_egl_display (display); display_ptr = gdk_x11_display_get_egl_display (display);
if (display_ptr) if (display_ptr)
priv->display = (GstGLDisplay *) priv->display = (GstGLDisplay *)

View File

@@ -52,7 +52,6 @@ struct _GtkClapperGLWidget
/* properties */ /* properties */
gboolean force_aspect_ratio; gboolean force_aspect_ratio;
gint par_n, par_d; gint par_n, par_d;
gboolean keep_last_frame;
gint display_width; gint display_width;
gint display_height; gint display_height;

View File

@@ -8,6 +8,8 @@ gstclapper_sources = [
'gstclapper-g-main-context-signal-dispatcher.c', 'gstclapper-g-main-context-signal-dispatcher.c',
'gstclapper-video-overlay-video-renderer.c', 'gstclapper-video-overlay-video-renderer.c',
'gstclapper-visualization.c', 'gstclapper-visualization.c',
'gstclapper-playlist.c',
'gstclapper-playlist-item.c',
'gstclapper-mpris.c', 'gstclapper-mpris.c',
'gstclapper-gtk4-plugin.c', 'gstclapper-gtk4-plugin.c',
@@ -26,6 +28,8 @@ gstclapper_headers = [
'gstclapper-g-main-context-signal-dispatcher.h', 'gstclapper-g-main-context-signal-dispatcher.h',
'gstclapper-video-overlay-video-renderer.h', 'gstclapper-video-overlay-video-renderer.h',
'gstclapper-visualization.h', 'gstclapper-visualization.h',
'gstclapper-playlist.h',
'gstclapper-playlist-item.h',
'gstclapper-mpris.h', 'gstclapper-mpris.h',
'gstclapper-gtk4-plugin.h', 'gstclapper-gtk4-plugin.h',
] ]

View File

@@ -1,5 +1,5 @@
project('com.github.rafostar.Clapper', 'c', 'cpp', project('com.github.rafostar.Clapper', 'c', 'cpp',
version: '0.3.0', version: '0.2.1',
meson_version: '>= 0.50.0', meson_version: '>= 0.50.0',
license: 'GPL3', license: 'GPL3',
default_options: [ default_options: [

View File

@@ -2,7 +2,7 @@ Format: 3.0 (quilt)
Source: clapper Source: clapper
Binary: clapper Binary: clapper
Architecture: any Architecture: any
Version: 0.3.0 Version: 0.2.1
Maintainer: Rafostar <rafostar.github@gmail.com> Maintainer: Rafostar <rafostar.github@gmail.com>
Build-Depends: debhelper (>= 10), Build-Depends: debhelper (>= 10),
meson (>= 0.50), meson (>= 0.50),

View File

@@ -1,5 +1,5 @@
clapper (0.3.0) unstable; urgency=low clapper (0.2.1) unstable; urgency=low
* New version * New version
-- Rafostar <rafostar.github@gmail.com> Fri, 18 Jun 2021 09:39:00 +0100 -- Rafostar <rafostar.github@gmail.com> Mon, 19 Apr 2021 09:39:00 +0100

View File

@@ -26,7 +26,7 @@
%global glib2_version 2.56.0 %global glib2_version 2.56.0
Name: clapper Name: clapper
Version: 0.3.0 Version: 0.2.1
Release: 1%{?dist} Release: 1%{?dist}
Summary: Simple and modern GNOME media player Summary: Simple and modern GNOME media player
@@ -126,9 +126,6 @@ desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop
%{_libdir}/%{appname}/ %{_libdir}/%{appname}/
%changelog %changelog
* Fri Jun 18 2021 Rafostar <rafostar.github@gmail.com> - 0.3.0-1
- New version
* Mon Apr 19 2021 Rafostar <rafostar.github@gmail.com> - 0.2.1-1 * Mon Apr 19 2021 Rafostar <rafostar.github@gmail.com> - 0.2.1-1
- New version - New version

View File

@@ -81,8 +81,7 @@ class ClapperPlayer extends GstClapper.Clapper
this._onSettingsKeyChanged(settings, key); this._onSettingsKeyChanged(settings, key);
const flag = Gio.SettingsBindFlags.GET; const flag = Gio.SettingsBindFlags.GET;
settings.bind('keep-last-frame', this.widget, 'keep-last-frame', flag); settings.bind('subtitle-font', this.pipeline, 'subtitle_font_desc', flag);
settings.bind('subtitle-font', this.pipeline, 'subtitle-font-desc', flag);
} }
set_initial_config() set_initial_config()

View File

@@ -41,7 +41,6 @@ class ClapperGeneralPage extends PrefsBase.Grid
comboBox.connect('changed', this._onVolumeInitialChanged.bind(this, spinButton)); comboBox.connect('changed', this._onVolumeInitialChanged.bind(this, spinButton));
this.addTitle('Finish'); this.addTitle('Finish');
this.addCheckButton('Keep showing last frame', 'keep-last-frame');
this.addCheckButton('Close after playback', 'close-auto'); this.addCheckButton('Close after playback', 'close-auto');
} }

View File

@@ -38,7 +38,7 @@ var YouTubeClient = GObject.registerClass({
this.lastInfo = null; this.lastInfo = null;
this.postInfo = { this.postInfo = {
clientVersion: "2.20210605.09.00", clientVersion: null,
visitorData: "", visitorData: "",
}; };