Files
gtk/ottie/ottieplayer.c
Benjamin Otte c2ed71af7a Ottie: Add
2020-12-27 01:12:11 +01:00

491 lines
12 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "ottieplayer.h"
#include "ottiecreation.h"
#include "ottiepaintable.h"
#include <glib/gi18n.h>
struct _OttiePlayer
{
GObject parent_instance;
GFile *file;
OttieCreation *creation;
OttiePaintable *paintable;
gint64 time_offset;
guint timer_cb;
};
struct _OttiePlayerClass
{
GObjectClass parent_class;
};
enum {
PROP_0,
PROP_FILE,
N_PROPS,
};
static GParamSpec *properties[N_PROPS] = { NULL, };
static void
ottie_player_paintable_snapshot (GdkPaintable *paintable,
GdkSnapshot *snapshot,
double width,
double height)
{
OttiePlayer *self = OTTIE_PLAYER (paintable);
gdk_paintable_snapshot (GDK_PAINTABLE (self->paintable), snapshot, width, height);
}
static int
ottie_player_paintable_get_intrinsic_width (GdkPaintable *paintable)
{
OttiePlayer *self = OTTIE_PLAYER (paintable);
return gdk_paintable_get_intrinsic_width (GDK_PAINTABLE (self->paintable));
}
static int
ottie_player_paintable_get_intrinsic_height (GdkPaintable *paintable)
{
OttiePlayer *self = OTTIE_PLAYER (paintable);
return gdk_paintable_get_intrinsic_height (GDK_PAINTABLE (self->paintable));
}
static void
ottie_player_paintable_init (GdkPaintableInterface *iface)
{
iface->snapshot = ottie_player_paintable_snapshot;
iface->get_intrinsic_width = ottie_player_paintable_get_intrinsic_width;
iface->get_intrinsic_height = ottie_player_paintable_get_intrinsic_height;
}
G_DEFINE_TYPE_EXTENDED (OttiePlayer, ottie_player, GTK_TYPE_MEDIA_STREAM, 0,
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
ottie_player_paintable_init))
static gboolean
ottie_player_timer_cb (gpointer data)
{
OttiePlayer *self = OTTIE_PLAYER (data);
gint64 timestamp;
timestamp = g_get_monotonic_time () - self->time_offset;
if (timestamp > ottie_paintable_get_duration (self->paintable))
{
if (gtk_media_stream_get_loop (GTK_MEDIA_STREAM (self)))
{
timestamp %= ottie_paintable_get_duration (self->paintable);
}
else
{
timestamp = ottie_paintable_get_duration (self->paintable);
gtk_media_stream_ended (GTK_MEDIA_STREAM (self));
}
}
ottie_paintable_set_timestamp (self->paintable, timestamp);
gtk_media_stream_update (GTK_MEDIA_STREAM (self), timestamp);
return G_SOURCE_CONTINUE;
}
static gboolean
ottie_player_play (GtkMediaStream *stream)
{
OttiePlayer *self = OTTIE_PLAYER (stream);
double frame_rate;
frame_rate = ottie_creation_get_frame_rate (self->creation);
if (frame_rate <= 0)
return FALSE;
self->time_offset = g_get_monotonic_time () - ottie_paintable_get_timestamp (self->paintable);
self->timer_cb = g_timeout_add (1000 / frame_rate, ottie_player_timer_cb, self);
return TRUE;
}
static void
ottie_player_pause (GtkMediaStream *stream)
{
OttiePlayer *self = OTTIE_PLAYER (stream);
g_clear_handle_id (&self->timer_cb, g_source_remove);
}
static void
ottie_player_seek (GtkMediaStream *stream,
gint64 timestamp)
{
OttiePlayer *self = OTTIE_PLAYER (stream);
if (!ottie_creation_is_prepared (self->creation))
gtk_media_stream_seek_failed (stream);
ottie_paintable_set_timestamp (self->paintable, timestamp);
self->time_offset = g_get_monotonic_time () - timestamp;
gtk_media_stream_seek_success (stream);
gtk_media_stream_update (stream, timestamp);
}
static void
ottie_player_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
OttiePlayer *self = OTTIE_PLAYER (object);
switch (prop_id)
{
case PROP_FILE:
ottie_player_set_file (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
ottie_player_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
OttiePlayer *self = OTTIE_PLAYER (object);
switch (prop_id)
{
case PROP_FILE:
g_value_set_object (value, self->file);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
ottie_player_dispose (GObject *object)
{
OttiePlayer *self = OTTIE_PLAYER (object);
if (self->paintable)
{
g_signal_handlers_disconnect_by_func (self->paintable, gdk_paintable_invalidate_contents, self);
g_signal_handlers_disconnect_by_func (self->paintable, gdk_paintable_invalidate_size, self);
g_clear_object (&self->paintable);
}
g_clear_object (&self->creation);
G_OBJECT_CLASS (ottie_player_parent_class)->dispose (object);
}
static void
ottie_player_class_init (OttiePlayerClass *klass)
{
GtkMediaStreamClass *stream_class = GTK_MEDIA_STREAM_CLASS (klass);
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
stream_class->play = ottie_player_play;
stream_class->pause = ottie_player_pause;
stream_class->seek = ottie_player_seek;
gobject_class->get_property = ottie_player_get_property;
gobject_class->set_property = ottie_player_set_property;
gobject_class->dispose = ottie_player_dispose;
/**
* OttiePlayer:file
*
* The played file or %NULL.
*/
properties[PROP_FILE] =
g_param_spec_object ("file",
_("File"),
_("The played file"),
G_TYPE_FILE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, N_PROPS, properties);
}
static void
ottie_player_prepared_cb (OttieCreation *creation,
GParamSpec *pspec,
OttiePlayer *self)
{
if (ottie_creation_is_prepared (creation))
gtk_media_stream_prepared (GTK_MEDIA_STREAM (self),
FALSE,
TRUE,
TRUE,
ottie_paintable_get_duration (self->paintable));
else
gtk_media_stream_unprepared (GTK_MEDIA_STREAM (self));
ottie_paintable_set_timestamp (self->paintable, 0);
}
static void
ottie_player_init (OttiePlayer *self)
{
self->creation = ottie_creation_new ();
g_signal_connect (self->creation, "notify::prepared", G_CALLBACK (ottie_player_prepared_cb), self);
self->paintable = ottie_paintable_new (self->creation);
g_signal_connect_swapped (self->paintable, "invalidate-contents", G_CALLBACK (gdk_paintable_invalidate_contents), self);
g_signal_connect_swapped (self->paintable, "invalidate-size", G_CALLBACK (gdk_paintable_invalidate_size), self);
}
/**
* ottie_player_new:
*
* Creates a new Ottie player.
*
* Returns: (transfer full): a new #OttiePlayer
**/
OttiePlayer *
ottie_player_new (void)
{
return g_object_new (OTTIE_TYPE_PLAYER, NULL);
}
/**
* ottie_player_new_for_file:
* @file: (nullable): a #GFile
*
* Creates a new #OttiePlayer playing the given @file. If the file
* isnt found or cant be loaded, the resulting #OttiePlayer be empty.
*
* Returns: a new #OttiePlayer
**/
OttiePlayer*
ottie_player_new_for_file (GFile *file)
{
g_return_val_if_fail (file == NULL || G_IS_FILE (file), NULL);
return g_object_new (OTTIE_TYPE_PLAYER,
"file", file,
NULL);
}
/**
* ottie_player_new_for_filename:
* @filename: (type filename) (nullable): a filename
*
* Creates a new #OttiePlayer displaying the file @filename.
*
* This is a utility function that calls ottie_player_new_for_file().
* See that function for details.
*
* Returns: a new #OttiePlayer
**/
OttiePlayer*
ottie_player_new_for_filename (const char *filename)
{
OttiePlayer *result;
GFile *file;
if (filename)
file = g_file_new_for_path (filename);
else
file = NULL;
result = ottie_player_new_for_file (file);
if (file)
g_object_unref (file);
return result;
}
/**
* ottie_player_new_for_resource:
* @resource_path: (nullable): resource path to play back
*
* Creates a new #OttiePlayer displaying the file @resource_path.
*
* This is a utility function that calls ottie_player_new_for_file().
* See that function for details.
*
* Returns: a new #OttiePlayer
**/
OttiePlayer *
ottie_player_new_for_resource (const char *resource_path)
{
OttiePlayer *result;
GFile *file;
if (resource_path)
{
char *uri, *escaped;
escaped = g_uri_escape_string (resource_path,
G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE);
uri = g_strconcat ("resource://", escaped, NULL);
g_free (escaped);
file = g_file_new_for_uri (uri);
g_free (uri);
}
else
{
file = NULL;
}
result = ottie_player_new_for_file (file);
if (file)
g_object_unref (file);
return result;
}
/**
* ottie_player_set_file:
* @self: a #OttiePlayer
* @file: (nullable): a %GFile or %NULL
*
* Makes @self load and display @file.
*
* See ottie_player_new_for_file() for details.
**/
void
ottie_player_set_file (OttiePlayer *self,
GFile *file)
{
g_return_if_fail (OTTIE_IS_PLAYER (self));
g_return_if_fail (file == NULL || G_IS_FILE (file));
if (self->file == file)
return;
g_object_freeze_notify (G_OBJECT (self));
g_set_object (&self->file, file);
ottie_creation_load_file (self->creation, file);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FILE]);
g_object_thaw_notify (G_OBJECT (self));
}
/**
* ottie_player_get_file:
* @self: a #OttiePlayer
*
* Gets the #GFile currently displayed if @self is displaying a file.
* If @self is not displaying a file, then %NULL is returned.
*
* Returns: (nullable) (transfer none): The #GFile displayed by @self.
**/
GFile *
ottie_player_get_file (OttiePlayer *self)
{
g_return_val_if_fail (OTTIE_IS_PLAYER (self), FALSE);
return self->file;
}
/**
* ottie_player_set_filename:
* @self: a #OttiePlayer
* @filename: (nullable): the filename to play
*
* Makes @self load and display the given @filename.
*
* This is a utility function that calls ottie_player_set_file().
**/
void
ottie_player_set_filename (OttiePlayer *self,
const char *filename)
{
GFile *file;
g_return_if_fail (OTTIE_IS_PLAYER (self));
if (filename)
file = g_file_new_for_path (filename);
else
file = NULL;
ottie_player_set_file (self, file);
if (file)
g_object_unref (file);
}
/**
* ottie_player_set_resource:
* @self: a #OttiePlayer
* @resource_path: (nullable): the resource to set
*
* Makes @self load and display the resource at the given
* @resource_path.
*
* This is a utility function that calls ottie_player_set_file(),
**/
void
ottie_player_set_resource (OttiePlayer *self,
const char *resource_path)
{
GFile *file;
g_return_if_fail (OTTIE_IS_PLAYER (self));
if (resource_path)
{
char *uri, *escaped;
escaped = g_uri_escape_string (resource_path,
G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, FALSE);
uri = g_strconcat ("resource://", escaped, NULL);
g_free (escaped);
file = g_file_new_for_uri (uri);
g_free (uri);
}
else
{
file = NULL;
}
ottie_player_set_file (self, file);
if (file)
g_object_unref (file);
}