mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-27 06:00:22 +00:00
432e8664e1
The current definitions of the g_io_module_*() symbols do not build on Visual Studio when building against GLib earlier than 2.75.0 due to the way how these symbols are decorated in the GLib headers, as Visual Studio does not allow symbols that were previously marked with 'extern' (or so) to be marked with anything that is symantically different later. As a result, if we are using Visual Studio and glib-2.74.x or earlier, override _GLIB_EXTERN as appropriate in the modules/media sources before including the GIO headers. This sadly, means that we need a configure-time check as it would have been too late if we checked the GLib version using G_VERSION_CHECK macro, as the GIO headers would have been included already. There are similar items in the print backends, but we will not attempt to update these files as they are not meant to be built for Windows.
416 lines
13 KiB
C
416 lines
13 KiB
C
/*
|
|
* Copyright © 2018 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"
|
|
|
|
/*
|
|
* Sadly, we need this to build on Visual Studio against glib-2.74.x or earlier,
|
|
* otherwise the build will fail when building the g_io_module_*() bits with error C2375
|
|
* (redefinition; different linkage). This must be before including the Gio headers.
|
|
*/
|
|
#if defined (_MSC_VER) && defined (MODULES_OVERRIDE_GLIB_EXTERN)
|
|
# define _GLIB_EXTERN __declspec(dllexport) extern
|
|
#endif
|
|
|
|
#include "gtkgstmediafileprivate.h"
|
|
#include "gtkgstpaintableprivate.h"
|
|
|
|
#include <gst/player/gstplayer.h>
|
|
#include <gst/player/gstplayer-g-main-context-signal-dispatcher.h>
|
|
|
|
struct _GtkGstMediaFile
|
|
{
|
|
GtkMediaFile parent_instance;
|
|
|
|
GstPlayer *player;
|
|
GdkPaintable *paintable;
|
|
};
|
|
|
|
struct _GtkGstMediaFileClass
|
|
{
|
|
GtkMediaFileClass parent_class;
|
|
};
|
|
|
|
#define TO_GST_TIME(ts) ((ts) * (GST_SECOND / G_USEC_PER_SEC))
|
|
#define FROM_GST_TIME(ts) ((ts) / (GST_SECOND / G_USEC_PER_SEC))
|
|
|
|
static void
|
|
gtk_gst_media_file_paintable_snapshot (GdkPaintable *paintable,
|
|
GdkSnapshot *snapshot,
|
|
double width,
|
|
double height)
|
|
{
|
|
GtkGstMediaFile *self = GTK_GST_MEDIA_FILE (paintable);
|
|
|
|
gdk_paintable_snapshot (self->paintable, snapshot, width, height);
|
|
}
|
|
|
|
static GdkPaintable *
|
|
gtk_gst_media_file_paintable_get_current_image (GdkPaintable *paintable)
|
|
{
|
|
GtkGstMediaFile *self = GTK_GST_MEDIA_FILE (paintable);
|
|
|
|
return gdk_paintable_get_current_image (self->paintable);
|
|
}
|
|
|
|
static int
|
|
gtk_gst_media_file_paintable_get_intrinsic_width (GdkPaintable *paintable)
|
|
{
|
|
GtkGstMediaFile *self = GTK_GST_MEDIA_FILE (paintable);
|
|
|
|
return gdk_paintable_get_intrinsic_width (self->paintable);
|
|
}
|
|
|
|
static int
|
|
gtk_gst_media_file_paintable_get_intrinsic_height (GdkPaintable *paintable)
|
|
{
|
|
GtkGstMediaFile *self = GTK_GST_MEDIA_FILE (paintable);
|
|
|
|
return gdk_paintable_get_intrinsic_height (self->paintable);
|
|
}
|
|
|
|
static double gtk_gst_media_file_paintable_get_intrinsic_aspect_ratio (GdkPaintable *paintable)
|
|
{
|
|
GtkGstMediaFile *self = GTK_GST_MEDIA_FILE (paintable);
|
|
|
|
return gdk_paintable_get_intrinsic_aspect_ratio (self->paintable);
|
|
};
|
|
|
|
static void
|
|
gtk_gst_media_file_paintable_init (GdkPaintableInterface *iface)
|
|
{
|
|
iface->snapshot = gtk_gst_media_file_paintable_snapshot;
|
|
iface->get_current_image = gtk_gst_media_file_paintable_get_current_image;
|
|
iface->get_intrinsic_width = gtk_gst_media_file_paintable_get_intrinsic_width;
|
|
iface->get_intrinsic_height = gtk_gst_media_file_paintable_get_intrinsic_height;
|
|
iface->get_intrinsic_aspect_ratio = gtk_gst_media_file_paintable_get_intrinsic_aspect_ratio;
|
|
}
|
|
|
|
G_DEFINE_TYPE_EXTENDED (GtkGstMediaFile, gtk_gst_media_file, GTK_TYPE_MEDIA_FILE, 0,
|
|
G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
|
|
gtk_gst_media_file_paintable_init))
|
|
|
|
G_MODULE_EXPORT
|
|
void
|
|
g_io_module_load (GIOModule *module)
|
|
{
|
|
g_type_module_use (G_TYPE_MODULE (module));
|
|
|
|
g_io_extension_point_implement (GTK_MEDIA_FILE_EXTENSION_POINT_NAME,
|
|
GTK_TYPE_GST_MEDIA_FILE,
|
|
"gstreamer",
|
|
10);
|
|
}
|
|
|
|
G_MODULE_EXPORT
|
|
G_GNUC_NORETURN
|
|
void
|
|
g_io_module_unload (GIOModule *module)
|
|
{
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
G_MODULE_EXPORT
|
|
char **
|
|
g_io_module_query (void)
|
|
{
|
|
char *eps[] = {
|
|
(char *) GTK_MEDIA_FILE_EXTENSION_POINT_NAME,
|
|
NULL
|
|
};
|
|
|
|
return g_strdupv (eps);
|
|
}
|
|
|
|
static void
|
|
gtk_gst_media_file_ensure_prepared (GtkGstMediaFile *self)
|
|
{
|
|
GstPlayerMediaInfo *media_info;
|
|
|
|
if (gtk_media_stream_is_prepared (GTK_MEDIA_STREAM (self)))
|
|
return;
|
|
|
|
media_info = gst_player_get_media_info (self->player);
|
|
if (media_info)
|
|
{
|
|
GstClockTime duration = gst_player_media_info_get_duration (media_info);
|
|
|
|
gtk_media_stream_stream_prepared (GTK_MEDIA_STREAM (self),
|
|
gst_player_media_info_get_audio_streams (media_info) != NULL,
|
|
gst_player_media_info_get_video_streams (media_info) != NULL,
|
|
gst_player_media_info_is_seekable (media_info),
|
|
duration == GST_CLOCK_TIME_NONE ? 0 : FROM_GST_TIME (duration));
|
|
|
|
g_object_unref (media_info);
|
|
}
|
|
else
|
|
{
|
|
/* Assuming everything exists is better for the user than pretending it doesn't exist.
|
|
* Better to be able to control non-existing audio than not be able to control existing audio.
|
|
*
|
|
* Only for seeking we can't do a thing, because with 0 duration we can't seek anywhere.
|
|
*/
|
|
gtk_media_stream_stream_prepared (GTK_MEDIA_STREAM (self),
|
|
TRUE,
|
|
TRUE,
|
|
FALSE,
|
|
0);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_gst_media_file_position_updated_cb (GstPlayer *player,
|
|
GstClockTime time,
|
|
GtkGstMediaFile *self)
|
|
{
|
|
gtk_gst_media_file_ensure_prepared (self);
|
|
|
|
gtk_media_stream_update (GTK_MEDIA_STREAM (self), FROM_GST_TIME (time));
|
|
}
|
|
|
|
static void
|
|
gtk_gst_media_file_media_info_updated_cb (GstPlayer *player,
|
|
GstPlayerMediaInfo *media_info,
|
|
GtkGstMediaFile *self)
|
|
{
|
|
/* clock_time == 0: https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/issues/1588
|
|
* GstPlayer's first media-info-updated comes with 0 duration
|
|
*
|
|
* clock_time == -1: Seen on loading an audio-only ogg
|
|
*/
|
|
GstClockTime clock_time = gst_player_media_info_get_duration (media_info);
|
|
if (clock_time == 0 || clock_time == -1)
|
|
return;
|
|
|
|
gtk_gst_media_file_ensure_prepared (self);
|
|
}
|
|
|
|
static void
|
|
gtk_gst_media_file_seek_done_cb (GstPlayer *player,
|
|
GstClockTime time,
|
|
GtkGstMediaFile *self)
|
|
{
|
|
/* if we're not seeking, we're doing the loop seek-back after EOS */
|
|
if (gtk_media_stream_is_seeking (GTK_MEDIA_STREAM (self)))
|
|
gtk_media_stream_seek_success (GTK_MEDIA_STREAM (self));
|
|
gtk_media_stream_update (GTK_MEDIA_STREAM (self), FROM_GST_TIME (time));
|
|
}
|
|
|
|
static void
|
|
gtk_gst_media_file_error_cb (GstPlayer *player,
|
|
GError *error,
|
|
GtkGstMediaFile *self)
|
|
{
|
|
if (gtk_media_stream_get_error (GTK_MEDIA_STREAM (self)))
|
|
return;
|
|
|
|
gtk_media_stream_gerror (GTK_MEDIA_STREAM (self),
|
|
g_error_copy (error));
|
|
}
|
|
|
|
static void
|
|
gtk_gst_media_file_end_of_stream_cb (GstPlayer *player,
|
|
GtkGstMediaFile *self)
|
|
{
|
|
gtk_gst_media_file_ensure_prepared (self);
|
|
|
|
if (gtk_media_stream_get_ended (GTK_MEDIA_STREAM (self)))
|
|
return;
|
|
|
|
if (gtk_media_stream_get_loop (GTK_MEDIA_STREAM (self)))
|
|
{
|
|
gst_player_seek (self->player, 0);
|
|
return;
|
|
}
|
|
|
|
gtk_media_stream_stream_ended (GTK_MEDIA_STREAM (self));
|
|
}
|
|
|
|
static void
|
|
gtk_gst_media_file_destroy_player (GtkGstMediaFile *self)
|
|
{
|
|
if (self->player == NULL)
|
|
return;
|
|
|
|
g_signal_handlers_disconnect_by_func (self->player, gtk_gst_media_file_media_info_updated_cb, self);
|
|
g_signal_handlers_disconnect_by_func (self->player, gtk_gst_media_file_position_updated_cb, self);
|
|
g_signal_handlers_disconnect_by_func (self->player, gtk_gst_media_file_end_of_stream_cb, self);
|
|
g_signal_handlers_disconnect_by_func (self->player, gtk_gst_media_file_seek_done_cb, self);
|
|
g_signal_handlers_disconnect_by_func (self->player, gtk_gst_media_file_error_cb, self);
|
|
g_object_unref (self->player);
|
|
self->player = NULL;
|
|
}
|
|
|
|
static void
|
|
gtk_gst_media_file_create_player (GtkGstMediaFile *file)
|
|
{
|
|
GtkGstMediaFile *self = GTK_GST_MEDIA_FILE (file);
|
|
|
|
if (self->player != NULL)
|
|
return;
|
|
|
|
self->player = gst_player_new (GST_PLAYER_VIDEO_RENDERER (g_object_ref (self->paintable)),
|
|
gst_player_g_main_context_signal_dispatcher_new (NULL));
|
|
g_signal_connect (self->player, "media-info-updated", G_CALLBACK (gtk_gst_media_file_media_info_updated_cb), self);
|
|
g_signal_connect (self->player, "position-updated", G_CALLBACK (gtk_gst_media_file_position_updated_cb), self);
|
|
g_signal_connect (self->player, "end-of-stream", G_CALLBACK (gtk_gst_media_file_end_of_stream_cb), self);
|
|
g_signal_connect (self->player, "seek-done", G_CALLBACK (gtk_gst_media_file_seek_done_cb), self);
|
|
g_signal_connect (self->player, "error", G_CALLBACK (gtk_gst_media_file_error_cb), self);
|
|
}
|
|
|
|
static void
|
|
gtk_gst_media_file_open (GtkMediaFile *media_file)
|
|
{
|
|
GtkGstMediaFile *self = GTK_GST_MEDIA_FILE (media_file);
|
|
GFile *file;
|
|
|
|
gtk_gst_media_file_create_player (self);
|
|
|
|
file = gtk_media_file_get_file (media_file);
|
|
|
|
if (file)
|
|
{
|
|
/* XXX: This is technically incorrect because GFile uris aren't real uris */
|
|
char *uri = g_file_get_uri (file);
|
|
|
|
gst_player_set_uri (self->player, uri);
|
|
|
|
g_free (uri);
|
|
}
|
|
else
|
|
{
|
|
/* It's an input stream */
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
gst_player_pause (self->player);
|
|
}
|
|
|
|
static void
|
|
gtk_gst_media_file_close (GtkMediaFile *file)
|
|
{
|
|
GtkGstMediaFile *self = GTK_GST_MEDIA_FILE (file);
|
|
|
|
gtk_gst_media_file_destroy_player (self);
|
|
}
|
|
|
|
static gboolean
|
|
gtk_gst_media_file_play (GtkMediaStream *stream)
|
|
{
|
|
GtkGstMediaFile *self = GTK_GST_MEDIA_FILE (stream);
|
|
|
|
if (self->player == NULL)
|
|
return FALSE;
|
|
|
|
gst_player_play (self->player);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
gtk_gst_media_file_pause (GtkMediaStream *stream)
|
|
{
|
|
GtkGstMediaFile *self = GTK_GST_MEDIA_FILE (stream);
|
|
|
|
gst_player_pause (self->player);
|
|
}
|
|
|
|
static void
|
|
gtk_gst_media_file_seek (GtkMediaStream *stream,
|
|
gint64 timestamp)
|
|
{
|
|
GtkGstMediaFile *self = GTK_GST_MEDIA_FILE (stream);
|
|
|
|
gst_player_seek (self->player, TO_GST_TIME (timestamp));
|
|
}
|
|
|
|
static void
|
|
gtk_gst_media_file_update_audio (GtkMediaStream *stream,
|
|
gboolean muted,
|
|
double volume)
|
|
{
|
|
GtkGstMediaFile *self = GTK_GST_MEDIA_FILE (stream);
|
|
|
|
gst_player_set_mute (self->player, muted);
|
|
gst_player_set_volume (self->player, volume * volume * volume);
|
|
}
|
|
|
|
static void
|
|
gtk_gst_media_file_realize (GtkMediaStream *stream,
|
|
GdkSurface *surface)
|
|
{
|
|
GtkGstMediaFile *self = GTK_GST_MEDIA_FILE (stream);
|
|
|
|
gtk_gst_paintable_realize (GTK_GST_PAINTABLE (self->paintable), surface);
|
|
}
|
|
|
|
static void
|
|
gtk_gst_media_file_unrealize (GtkMediaStream *stream,
|
|
GdkSurface *surface)
|
|
{
|
|
GtkGstMediaFile *self = GTK_GST_MEDIA_FILE (stream);
|
|
|
|
gtk_gst_paintable_unrealize (GTK_GST_PAINTABLE (self->paintable), surface);
|
|
}
|
|
|
|
static void
|
|
gtk_gst_media_file_dispose (GObject *object)
|
|
{
|
|
GtkGstMediaFile *self = GTK_GST_MEDIA_FILE (object);
|
|
|
|
gtk_gst_media_file_destroy_player (self);
|
|
if (self->paintable)
|
|
{
|
|
g_signal_handlers_disconnect_by_func (self->paintable, gdk_paintable_invalidate_size, self);
|
|
g_signal_handlers_disconnect_by_func (self->paintable, gdk_paintable_invalidate_contents, self);
|
|
g_clear_object (&self->paintable);
|
|
}
|
|
|
|
G_OBJECT_CLASS (gtk_gst_media_file_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
gtk_gst_media_file_class_init (GtkGstMediaFileClass *klass)
|
|
{
|
|
GtkMediaFileClass *file_class = GTK_MEDIA_FILE_CLASS (klass);
|
|
GtkMediaStreamClass *stream_class = GTK_MEDIA_STREAM_CLASS (klass);
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
file_class->open = gtk_gst_media_file_open;
|
|
file_class->close = gtk_gst_media_file_close;
|
|
|
|
stream_class->play = gtk_gst_media_file_play;
|
|
stream_class->pause = gtk_gst_media_file_pause;
|
|
stream_class->seek = gtk_gst_media_file_seek;
|
|
stream_class->update_audio = gtk_gst_media_file_update_audio;
|
|
stream_class->realize = gtk_gst_media_file_realize;
|
|
stream_class->unrealize = gtk_gst_media_file_unrealize;
|
|
|
|
gobject_class->dispose = gtk_gst_media_file_dispose;
|
|
}
|
|
|
|
static void
|
|
gtk_gst_media_file_init (GtkGstMediaFile *self)
|
|
{
|
|
self->paintable = gtk_gst_paintable_new ();
|
|
g_signal_connect_swapped (self->paintable, "invalidate-size", G_CALLBACK (gdk_paintable_invalidate_size), self);
|
|
g_signal_connect_swapped (self->paintable, "invalidate-contents", G_CALLBACK (gdk_paintable_invalidate_contents), self);
|
|
}
|
|
|