mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-10-03 09:12:19 +00:00
Port auto/gconfsinks to 0.9. They actually appear to work here in
Original commit message from CVS: * configure.ac: * ext/Makefile.am: * ext/gconf/Makefile.am: * ext/gconf/gconf.c: (gst_bin_find_unconnected_pad), (gst_gconf_render_bin_from_description), (gst_gconf_get_default_video_sink): * ext/gconf/gstgconfaudiosink.c: (gst_gconf_audio_sink_base_init), (gst_gconf_audio_sink_class_init), (gst_gconf_audio_sink_dispose), (cb_toggle_element), (gst_gconf_audio_sink_change_state): * ext/gconf/gstgconfelements.h: * ext/gconf/gstgconfvideosink.c: (gst_gconf_video_sink_base_init), (gst_gconf_video_sink_class_init), (gst_gconf_video_sink_dispose), (cb_toggle_element), (gst_gconf_video_sink_change_state): * gst/autodetect/gstautoaudiosink.c: (gst_auto_audio_sink_base_init), (gst_auto_audio_sink_class_init), (gst_auto_audio_sink_detect), (gst_auto_audio_sink_change_state): * gst/autodetect/gstautovideosink.c: (gst_auto_video_sink_base_init), (gst_auto_video_sink_class_init), (gst_auto_video_sink_find_best), (gst_auto_video_sink_detect): Port auto/gconfsinks to 0.9. They actually appear to work here in Totem as well, making them actually useful.
This commit is contained in:
parent
bef986dba1
commit
961178077e
10 changed files with 135 additions and 218 deletions
24
ChangeLog
24
ChangeLog
|
@ -1,3 +1,27 @@
|
||||||
|
2005-07-20 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
|
||||||
|
|
||||||
|
* configure.ac:
|
||||||
|
* ext/Makefile.am:
|
||||||
|
* ext/gconf/Makefile.am:
|
||||||
|
* ext/gconf/gconf.c: (gst_bin_find_unconnected_pad),
|
||||||
|
(gst_gconf_render_bin_from_description),
|
||||||
|
(gst_gconf_get_default_video_sink):
|
||||||
|
* ext/gconf/gstgconfaudiosink.c: (gst_gconf_audio_sink_base_init),
|
||||||
|
(gst_gconf_audio_sink_class_init), (gst_gconf_audio_sink_dispose),
|
||||||
|
(cb_toggle_element), (gst_gconf_audio_sink_change_state):
|
||||||
|
* ext/gconf/gstgconfelements.h:
|
||||||
|
* ext/gconf/gstgconfvideosink.c: (gst_gconf_video_sink_base_init),
|
||||||
|
(gst_gconf_video_sink_class_init), (gst_gconf_video_sink_dispose),
|
||||||
|
(cb_toggle_element), (gst_gconf_video_sink_change_state):
|
||||||
|
* gst/autodetect/gstautoaudiosink.c:
|
||||||
|
(gst_auto_audio_sink_base_init), (gst_auto_audio_sink_class_init),
|
||||||
|
(gst_auto_audio_sink_detect), (gst_auto_audio_sink_change_state):
|
||||||
|
* gst/autodetect/gstautovideosink.c:
|
||||||
|
(gst_auto_video_sink_base_init), (gst_auto_video_sink_class_init),
|
||||||
|
(gst_auto_video_sink_find_best), (gst_auto_video_sink_detect):
|
||||||
|
Port auto/gconfsinks to 0.9. They actually appear to work here in
|
||||||
|
Totem as well, making them actually useful.
|
||||||
|
|
||||||
2005-07-20 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
|
2005-07-20 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
|
||||||
|
|
||||||
* ext/faad/Makefile.am:
|
* ext/faad/Makefile.am:
|
||||||
|
|
|
@ -302,6 +302,7 @@ dnl these are all the gst plug-ins, compilable without additional libs
|
||||||
GST_PLUGINS_ALL="\
|
GST_PLUGINS_ALL="\
|
||||||
videofilter \
|
videofilter \
|
||||||
alpha \
|
alpha \
|
||||||
|
autodetect \
|
||||||
avi \
|
avi \
|
||||||
effectv \
|
effectv \
|
||||||
fdsrc \
|
fdsrc \
|
||||||
|
@ -582,6 +583,7 @@ Makefile
|
||||||
gst-plugins.spec
|
gst-plugins.spec
|
||||||
gst/Makefile
|
gst/Makefile
|
||||||
gst/alpha/Makefile
|
gst/alpha/Makefile
|
||||||
|
gst/autodetect/Makefile
|
||||||
gst/avi/Makefile
|
gst/avi/Makefile
|
||||||
gst/effectv/Makefile
|
gst/effectv/Makefile
|
||||||
gst/fdsrc/Makefile
|
gst/fdsrc/Makefile
|
||||||
|
@ -602,6 +604,7 @@ sys/oss/Makefile
|
||||||
ext/Makefile
|
ext/Makefile
|
||||||
ext/aalib/Makefile
|
ext/aalib/Makefile
|
||||||
ext/dv/Makefile
|
ext/dv/Makefile
|
||||||
|
ext/gconf/Makefile
|
||||||
ext/libcaca/Makefile
|
ext/libcaca/Makefile
|
||||||
ext/mad/Makefile
|
ext/mad/Makefile
|
||||||
ext/raw1394/Makefile
|
ext/raw1394/Makefile
|
||||||
|
|
|
@ -124,6 +124,12 @@ endif
|
||||||
FLAC_DIR=
|
FLAC_DIR=
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
|
if USE_GCONF
|
||||||
|
GCONF_DIR=gconf
|
||||||
|
else
|
||||||
|
GCONF_DIR=
|
||||||
|
endif
|
||||||
|
|
||||||
# if USE_GDK_PIXBUF
|
# if USE_GDK_PIXBUF
|
||||||
# GDK_PIXBUF_DIR=gdk_pixbuf
|
# GDK_PIXBUF_DIR=gdk_pixbuf
|
||||||
# else
|
# else
|
||||||
|
@ -393,6 +399,7 @@ SUBDIRS=\
|
||||||
$(FAAC_DIR) \
|
$(FAAC_DIR) \
|
||||||
$(FAAD_DIR) \
|
$(FAAD_DIR) \
|
||||||
$(FLAC_DIR) \
|
$(FLAC_DIR) \
|
||||||
|
$(GCONF_DIR) \
|
||||||
$(GDK_PIXBUF_DIR) \
|
$(GDK_PIXBUF_DIR) \
|
||||||
$(GNOMEVFS_DIR) \
|
$(GNOMEVFS_DIR) \
|
||||||
$(GSM_DIR) \
|
$(GSM_DIR) \
|
||||||
|
@ -434,6 +441,7 @@ SUBDIRS=\
|
||||||
|
|
||||||
DIST_SUBDIRS=\
|
DIST_SUBDIRS=\
|
||||||
mad \
|
mad \
|
||||||
|
gconf \
|
||||||
shout2 \
|
shout2 \
|
||||||
sidplay \
|
sidplay \
|
||||||
aalib \
|
aalib \
|
||||||
|
|
|
@ -3,15 +3,16 @@ plugin_LTLIBRARIES = libgstgconfelements.la
|
||||||
libgstgconfelements_la_SOURCES = \
|
libgstgconfelements_la_SOURCES = \
|
||||||
gstgconfaudiosink.c \
|
gstgconfaudiosink.c \
|
||||||
gstgconfelements.c \
|
gstgconfelements.c \
|
||||||
gstgconfvideosink.c
|
gstgconfvideosink.c \
|
||||||
|
gconf.c
|
||||||
|
|
||||||
DIR_CFLAGS = -DGST_GCONF_DIR=\"/system/gstreamer/@GST_MAJORMINOR@\"
|
DIR_CFLAGS = -DGST_GCONF_DIR=\"/system/gstreamer/@GST_MAJORMINOR@\"
|
||||||
libgstgconfelements_la_CFLAGS = $(GST_CFLAGS) $(GCONF_CFLAGS) $(DIR_CFLAGS)
|
libgstgconfelements_la_CFLAGS = $(GST_CFLAGS) $(GCONF_CFLAGS) $(DIR_CFLAGS)
|
||||||
libgstgconfelements_la_LIBADD = $(GCONF_LIBS) \
|
libgstgconfelements_la_LIBADD = $(GCONF_LIBS)
|
||||||
$(top_builddir)/gst-libs/gst/gconf/libgstgconf-@GST_MAJORMINOR@.la
|
|
||||||
libgstgconfelements_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
libgstgconfelements_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
||||||
|
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
gstgconfaudiosink.h \
|
gstgconfaudiosink.h \
|
||||||
gstgconfelements.h \
|
gstgconfelements.h \
|
||||||
gstgconfvideosink.h
|
gstgconfvideosink.h \
|
||||||
|
gconf.h
|
||||||
|
|
|
@ -55,25 +55,34 @@ gst_bin_find_unconnected_pad (GstBin * bin, GstPadDirection direction)
|
||||||
const GList *pads = NULL;
|
const GList *pads = NULL;
|
||||||
GstElement *element = NULL;
|
GstElement *element = NULL;
|
||||||
|
|
||||||
elements = (GList *) gst_bin_get_list (bin);
|
GST_LOCK (bin);
|
||||||
|
elements = bin->children;
|
||||||
/* traverse all elements looking for unconnected pads */
|
/* traverse all elements looking for unconnected pads */
|
||||||
while (elements && pad == NULL) {
|
while (elements && pad == NULL) {
|
||||||
element = GST_ELEMENT (elements->data);
|
element = GST_ELEMENT (elements->data);
|
||||||
pads = gst_element_get_pad_list (element);
|
GST_LOCK (element);
|
||||||
|
pads = element->pads;
|
||||||
while (pads) {
|
while (pads) {
|
||||||
|
GstPad *testpad = GST_PAD (pads->data);
|
||||||
|
|
||||||
/* check if the direction matches */
|
/* check if the direction matches */
|
||||||
if (GST_PAD_DIRECTION (GST_PAD (pads->data)) == direction) {
|
if (GST_PAD_DIRECTION (testpad) == direction) {
|
||||||
if (GST_PAD_PEER (GST_PAD (pads->data)) == NULL) {
|
GST_LOCK (testpad);
|
||||||
|
if (GST_PAD_PEER (testpad) == NULL) {
|
||||||
|
GST_UNLOCK (testpad);
|
||||||
/* found it ! */
|
/* found it ! */
|
||||||
pad = GST_PAD (pads->data);
|
pad = testpad;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
GST_UNLOCK (testpad);
|
||||||
}
|
}
|
||||||
if (pad)
|
|
||||||
break; /* found one already */
|
|
||||||
pads = g_list_next (pads);
|
pads = g_list_next (pads);
|
||||||
}
|
}
|
||||||
|
GST_UNLOCK (element);
|
||||||
elements = g_list_next (elements);
|
elements = g_list_next (elements);
|
||||||
}
|
}
|
||||||
|
GST_UNLOCK (bin);
|
||||||
|
|
||||||
return pad;
|
return pad;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,10 +167,10 @@ gst_gconf_render_bin_from_description (const gchar * description)
|
||||||
|
|
||||||
/* find pads and ghost them if necessary */
|
/* find pads and ghost them if necessary */
|
||||||
if ((pad = gst_bin_find_unconnected_pad (GST_BIN (bin), GST_PAD_SRC))) {
|
if ((pad = gst_bin_find_unconnected_pad (GST_BIN (bin), GST_PAD_SRC))) {
|
||||||
gst_element_add_ghost_pad (bin, pad, "src");
|
gst_element_add_pad (bin, gst_ghost_pad_new ("src", pad));
|
||||||
}
|
}
|
||||||
if ((pad = gst_bin_find_unconnected_pad (GST_BIN (bin), GST_PAD_SINK))) {
|
if ((pad = gst_bin_find_unconnected_pad (GST_BIN (bin), GST_PAD_SINK))) {
|
||||||
gst_element_add_ghost_pad (bin, pad, "sink");
|
gst_element_add_pad (bin, gst_ghost_pad_new ("sink", pad));
|
||||||
}
|
}
|
||||||
return bin;
|
return bin;
|
||||||
}
|
}
|
||||||
|
@ -217,8 +226,8 @@ gst_gconf_get_default_audio_sink (void)
|
||||||
* gst_gconf_get_default_video_sink:
|
* gst_gconf_get_default_video_sink:
|
||||||
*
|
*
|
||||||
* Render video output bin from GStreamer GConf key : "default/videosink".
|
* Render video output bin from GStreamer GConf key : "default/videosink".
|
||||||
* If key is invalid, the default video sink for the platform is used,
|
* If key is invalid, the default video sink for the platform is used
|
||||||
* and this is detected by the autodetection bin autovideosink.
|
* (typically xvimagesink or ximagesink).
|
||||||
*
|
*
|
||||||
* Returns: a #GstElement containing the video output bin, or NULL if
|
* Returns: a #GstElement containing the video output bin, or NULL if
|
||||||
* everything failed.
|
* everything failed.
|
||||||
|
@ -229,7 +238,7 @@ gst_gconf_get_default_video_sink (void)
|
||||||
GstElement *ret = gst_gconf_render_bin_from_key ("default/videosink");
|
GstElement *ret = gst_gconf_render_bin_from_key ("default/videosink");
|
||||||
|
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
ret = gst_element_factory_make ("xvimagesink", NULL);
|
ret = gst_element_factory_make (DEFAULT_VIDEOSINK, NULL);
|
||||||
|
|
||||||
if (!ret)
|
if (!ret)
|
||||||
g_warning ("No GConf default video sink key and %s doesn't work",
|
g_warning ("No GConf default video sink key and %s doesn't work",
|
||||||
|
@ -243,7 +252,7 @@ gst_gconf_get_default_video_sink (void)
|
||||||
* gst_gconf_get_default_audio_src:
|
* gst_gconf_get_default_audio_src:
|
||||||
*
|
*
|
||||||
* Render audio acquisition bin from GStreamer GConf key : "default/audiosrc".
|
* Render audio acquisition bin from GStreamer GConf key : "default/audiosrc".
|
||||||
* If key is invalid, the default audio source for the plaform is used
|
* If key is invalid, the default audio source for the plaform is used.
|
||||||
* (typically osssrc or sunaudiosrc).
|
* (typically osssrc or sunaudiosrc).
|
||||||
*
|
*
|
||||||
* Returns: a #GstElement containing the audio source bin, or NULL if
|
* Returns: a #GstElement containing the audio source bin, or NULL if
|
||||||
|
|
|
@ -24,46 +24,16 @@
|
||||||
#include "gstgconfelements.h"
|
#include "gstgconfelements.h"
|
||||||
#include "gstgconfaudiosink.h"
|
#include "gstgconfaudiosink.h"
|
||||||
|
|
||||||
static void gst_gconf_audio_sink_base_init (GstGConfAudioSinkClass * klass);
|
|
||||||
static void gst_gconf_audio_sink_class_init (GstGConfAudioSinkClass * klass);
|
|
||||||
static void gst_gconf_audio_sink_init (GstGConfAudioSink * sink);
|
|
||||||
static void gst_gconf_audio_sink_dispose (GObject * object);
|
static void gst_gconf_audio_sink_dispose (GObject * object);
|
||||||
|
|
||||||
static void cb_toggle_element (GConfClient * client,
|
static void cb_toggle_element (GConfClient * client,
|
||||||
guint connection_id, GConfEntry * entry, gpointer data);
|
guint connection_id, GConfEntry * entry, gpointer data);
|
||||||
|
|
||||||
static GstElementStateReturn
|
static GstElementStateReturn
|
||||||
gst_gconf_audio_sink_change_state (GstElement * element);
|
gst_gconf_audio_sink_change_state (GstElement * element);
|
||||||
|
|
||||||
static GstBinClass *parent_class = NULL;
|
GST_BOILERPLATE (GstGConfAudioSink, gst_gconf_audio_sink, GstBin, GST_TYPE_BIN);
|
||||||
|
|
||||||
GType
|
|
||||||
gst_gconf_audio_sink_get_type (void)
|
|
||||||
{
|
|
||||||
static GType gst_gconf_audio_sink_type = 0;
|
|
||||||
|
|
||||||
if (!gst_gconf_audio_sink_type) {
|
|
||||||
static const GTypeInfo gst_gconf_audio_sink_info = {
|
|
||||||
sizeof (GstGConfAudioSinkClass),
|
|
||||||
(GBaseInitFunc) gst_gconf_audio_sink_base_init,
|
|
||||||
NULL,
|
|
||||||
(GClassInitFunc) gst_gconf_audio_sink_class_init,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
sizeof (GstGConfAudioSink),
|
|
||||||
0,
|
|
||||||
(GInstanceInitFunc) gst_gconf_audio_sink_init,
|
|
||||||
};
|
|
||||||
|
|
||||||
gst_gconf_audio_sink_type = g_type_register_static (GST_TYPE_BIN,
|
|
||||||
"GstGConfAudioSink", &gst_gconf_audio_sink_info, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return gst_gconf_audio_sink_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_gconf_audio_sink_base_init (GstGConfAudioSinkClass * klass)
|
gst_gconf_audio_sink_base_init (gpointer klass)
|
||||||
{
|
{
|
||||||
GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
|
GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
|
||||||
GstElementDetails gst_gconf_audio_sink_details = {
|
GstElementDetails gst_gconf_audio_sink_details = {
|
||||||
|
@ -88,8 +58,6 @@ gst_gconf_audio_sink_class_init (GstGConfAudioSinkClass * klass)
|
||||||
GObjectClass *oklass = G_OBJECT_CLASS (klass);
|
GObjectClass *oklass = G_OBJECT_CLASS (klass);
|
||||||
GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
|
GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
|
||||||
|
|
||||||
parent_class = g_type_class_ref (GST_TYPE_BIN);
|
|
||||||
|
|
||||||
oklass->dispose = gst_gconf_audio_sink_dispose;
|
oklass->dispose = gst_gconf_audio_sink_dispose;
|
||||||
eklass->change_state = gst_gconf_audio_sink_change_state;
|
eklass->change_state = gst_gconf_audio_sink_change_state;
|
||||||
}
|
}
|
||||||
|
@ -120,7 +88,7 @@ gst_gconf_audio_sink_dispose (GObject * object)
|
||||||
sink->client = NULL;
|
sink->client = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -128,15 +96,17 @@ cb_toggle_element (GConfClient * client,
|
||||||
guint connection_id, GConfEntry * entry, gpointer data)
|
guint connection_id, GConfEntry * entry, gpointer data)
|
||||||
{
|
{
|
||||||
GstGConfAudioSink *sink = GST_GCONF_AUDIO_SINK (data);
|
GstGConfAudioSink *sink = GST_GCONF_AUDIO_SINK (data);
|
||||||
GstPad *peer = NULL;
|
GstPad *peer = NULL, *targetpad;
|
||||||
GstElementState state = GST_STATE (sink);
|
|
||||||
|
|
||||||
/* save ghostpad */
|
/* save ghostpad */
|
||||||
if (sink->pad) {
|
if (sink->pad) {
|
||||||
gst_object_ref (GST_OBJECT (sink->pad));
|
peer = GST_PAD_PEER (sink->pad);
|
||||||
peer = GST_PAD_PEER (GST_PAD_REALIZE (sink->pad));
|
if (peer) {
|
||||||
if (peer)
|
|
||||||
gst_pad_unlink (peer, sink->pad);
|
gst_pad_unlink (peer, sink->pad);
|
||||||
|
GST_DEBUG_OBJECT (sink, "Caching peer %p", peer);
|
||||||
|
}
|
||||||
|
gst_element_remove_pad (GST_ELEMENT (sink), sink->pad);
|
||||||
|
sink->pad = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* kill old element */
|
/* kill old element */
|
||||||
|
@ -158,21 +128,16 @@ cb_toggle_element (GConfClient * client,
|
||||||
gst_bin_add (GST_BIN (sink), sink->kid);
|
gst_bin_add (GST_BIN (sink), sink->kid);
|
||||||
|
|
||||||
/* re-attach ghostpad */
|
/* re-attach ghostpad */
|
||||||
if (sink->pad) {
|
|
||||||
GST_DEBUG_OBJECT (sink, "Re-doing existing ghostpad");
|
|
||||||
gst_pad_add_ghost_pad (gst_element_get_pad (sink->kid, "sink"), sink->pad);
|
|
||||||
} else {
|
|
||||||
GST_DEBUG_OBJECT (sink, "Creating new ghostpad");
|
GST_DEBUG_OBJECT (sink, "Creating new ghostpad");
|
||||||
sink->pad = gst_ghost_pad_new ("sink",
|
targetpad = gst_element_get_pad (sink->kid, "sink");
|
||||||
gst_element_get_pad (sink->kid, "sink"));
|
sink->pad = gst_ghost_pad_new ("sink", targetpad);
|
||||||
|
gst_object_unref (targetpad);
|
||||||
gst_element_add_pad (GST_ELEMENT (sink), sink->pad);
|
gst_element_add_pad (GST_ELEMENT (sink), sink->pad);
|
||||||
}
|
|
||||||
if (peer) {
|
if (peer) {
|
||||||
GST_DEBUG_OBJECT (sink, "Linking...");
|
GST_DEBUG_OBJECT (sink, "Linking...");
|
||||||
gst_pad_link (peer, sink->pad);
|
gst_pad_link (peer, sink->pad);
|
||||||
}
|
}
|
||||||
GST_DEBUG_OBJECT (sink, "Syncing state");
|
|
||||||
gst_element_set_state (GST_ELEMENT (sink), state);
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (sink, "done changing gconf audio sink");
|
GST_DEBUG_OBJECT (sink, "done changing gconf audio sink");
|
||||||
sink->init = TRUE;
|
sink->init = TRUE;
|
||||||
|
@ -191,5 +156,6 @@ gst_gconf_audio_sink_change_state (GstElement * element)
|
||||||
return GST_STATE_FAILURE;
|
return GST_STATE_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GST_ELEMENT_CLASS (parent_class)->change_state (element);
|
return GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, change_state,
|
||||||
|
(element), GST_STATE_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
#ifndef __GST_GCONF_ELEMENTS_H__
|
#ifndef __GST_GCONF_ELEMENTS_H__
|
||||||
#define __GST_GCONF_ELEMENTS_H__
|
#define __GST_GCONF_ELEMENTS_H__
|
||||||
|
|
||||||
#include <gst/gconf/gconf.h>
|
#include <gconf.h>
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_EXTERN (gconf_debug);
|
GST_DEBUG_CATEGORY_EXTERN (gconf_debug);
|
||||||
#define GST_CAT_DEFAULT gconf_debug
|
#define GST_CAT_DEFAULT gconf_debug
|
||||||
|
|
|
@ -24,46 +24,16 @@
|
||||||
#include "gstgconfelements.h"
|
#include "gstgconfelements.h"
|
||||||
#include "gstgconfvideosink.h"
|
#include "gstgconfvideosink.h"
|
||||||
|
|
||||||
static void gst_gconf_video_sink_base_init (GstGConfVideoSinkClass * klass);
|
|
||||||
static void gst_gconf_video_sink_class_init (GstGConfVideoSinkClass * klass);
|
|
||||||
static void gst_gconf_video_sink_init (GstGConfVideoSink * sink);
|
|
||||||
static void gst_gconf_video_sink_dispose (GObject * object);
|
static void gst_gconf_video_sink_dispose (GObject * object);
|
||||||
|
|
||||||
static void cb_toggle_element (GConfClient * client,
|
static void cb_toggle_element (GConfClient * client,
|
||||||
guint connection_id, GConfEntry * entry, gpointer data);
|
guint connection_id, GConfEntry * entry, gpointer data);
|
||||||
|
|
||||||
static GstElementStateReturn
|
static GstElementStateReturn
|
||||||
gst_gconf_video_sink_change_state (GstElement * element);
|
gst_gconf_video_sink_change_state (GstElement * element);
|
||||||
|
|
||||||
static GstBinClass *parent_class = NULL;
|
GST_BOILERPLATE (GstGConfVideoSink, gst_gconf_video_sink, GstBin, GST_TYPE_BIN);
|
||||||
|
|
||||||
GType
|
|
||||||
gst_gconf_video_sink_get_type (void)
|
|
||||||
{
|
|
||||||
static GType gst_gconf_video_sink_type = 0;
|
|
||||||
|
|
||||||
if (!gst_gconf_video_sink_type) {
|
|
||||||
static const GTypeInfo gst_gconf_video_sink_info = {
|
|
||||||
sizeof (GstGConfVideoSinkClass),
|
|
||||||
(GBaseInitFunc) gst_gconf_video_sink_base_init,
|
|
||||||
NULL,
|
|
||||||
(GClassInitFunc) gst_gconf_video_sink_class_init,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
sizeof (GstGConfVideoSink),
|
|
||||||
0,
|
|
||||||
(GInstanceInitFunc) gst_gconf_video_sink_init,
|
|
||||||
};
|
|
||||||
|
|
||||||
gst_gconf_video_sink_type = g_type_register_static (GST_TYPE_BIN,
|
|
||||||
"GstGConfVideoSink", &gst_gconf_video_sink_info, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return gst_gconf_video_sink_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_gconf_video_sink_base_init (GstGConfVideoSinkClass * klass)
|
gst_gconf_video_sink_base_init (gpointer klass)
|
||||||
{
|
{
|
||||||
GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
|
GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
|
||||||
GstElementDetails gst_gconf_video_sink_details = {
|
GstElementDetails gst_gconf_video_sink_details = {
|
||||||
|
@ -88,8 +58,6 @@ gst_gconf_video_sink_class_init (GstGConfVideoSinkClass * klass)
|
||||||
GObjectClass *oklass = G_OBJECT_CLASS (klass);
|
GObjectClass *oklass = G_OBJECT_CLASS (klass);
|
||||||
GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
|
GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
|
||||||
|
|
||||||
parent_class = g_type_class_ref (GST_TYPE_BIN);
|
|
||||||
|
|
||||||
oklass->dispose = gst_gconf_video_sink_dispose;
|
oklass->dispose = gst_gconf_video_sink_dispose;
|
||||||
eklass->change_state = gst_gconf_video_sink_change_state;
|
eklass->change_state = gst_gconf_video_sink_change_state;
|
||||||
}
|
}
|
||||||
|
@ -120,7 +88,7 @@ gst_gconf_video_sink_dispose (GObject * object)
|
||||||
sink->client = NULL;
|
sink->client = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -128,15 +96,17 @@ cb_toggle_element (GConfClient * client,
|
||||||
guint connection_id, GConfEntry * entry, gpointer data)
|
guint connection_id, GConfEntry * entry, gpointer data)
|
||||||
{
|
{
|
||||||
GstGConfVideoSink *sink = GST_GCONF_VIDEO_SINK (data);
|
GstGConfVideoSink *sink = GST_GCONF_VIDEO_SINK (data);
|
||||||
GstPad *peer = NULL;
|
GstPad *peer = NULL, *targetpad;
|
||||||
GstElementState state = GST_STATE (sink);
|
|
||||||
|
|
||||||
/* save ghostpad */
|
/* save ghostpad */
|
||||||
if (sink->pad) {
|
if (sink->pad) {
|
||||||
gst_object_ref (GST_OBJECT (sink->pad));
|
peer = GST_PAD_PEER (sink->pad);
|
||||||
peer = GST_PAD_PEER (GST_PAD_REALIZE (sink->pad));
|
if (peer) {
|
||||||
if (peer)
|
|
||||||
gst_pad_unlink (peer, sink->pad);
|
gst_pad_unlink (peer, sink->pad);
|
||||||
|
GST_DEBUG_OBJECT (sink, "Caching peer %p", peer);
|
||||||
|
}
|
||||||
|
gst_element_remove_pad (GST_ELEMENT (sink), sink->pad);
|
||||||
|
sink->pad = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* kill old element */
|
/* kill old element */
|
||||||
|
@ -158,21 +128,16 @@ cb_toggle_element (GConfClient * client,
|
||||||
gst_bin_add (GST_BIN (sink), sink->kid);
|
gst_bin_add (GST_BIN (sink), sink->kid);
|
||||||
|
|
||||||
/* re-attach ghostpad */
|
/* re-attach ghostpad */
|
||||||
if (sink->pad) {
|
|
||||||
GST_DEBUG_OBJECT (sink, "Re-doing existing ghostpad");
|
|
||||||
gst_pad_add_ghost_pad (gst_element_get_pad (sink->kid, "sink"), sink->pad);
|
|
||||||
} else {
|
|
||||||
GST_DEBUG_OBJECT (sink, "Creating new ghostpad");
|
GST_DEBUG_OBJECT (sink, "Creating new ghostpad");
|
||||||
sink->pad = gst_ghost_pad_new ("sink",
|
targetpad = gst_element_get_pad (sink->kid, "sink");
|
||||||
gst_element_get_pad (sink->kid, "sink"));
|
sink->pad = gst_ghost_pad_new ("sink", targetpad);
|
||||||
|
gst_object_unref (targetpad);
|
||||||
gst_element_add_pad (GST_ELEMENT (sink), sink->pad);
|
gst_element_add_pad (GST_ELEMENT (sink), sink->pad);
|
||||||
}
|
|
||||||
if (peer) {
|
if (peer) {
|
||||||
GST_DEBUG_OBJECT (sink, "Linking...");
|
GST_DEBUG_OBJECT (sink, "Linking...");
|
||||||
gst_pad_link (peer, sink->pad);
|
gst_pad_link (peer, sink->pad);
|
||||||
}
|
}
|
||||||
GST_DEBUG_OBJECT (sink, "Syncing state");
|
|
||||||
gst_element_set_state (GST_ELEMENT (sink), state);
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (sink, "done changing gconf video sink");
|
GST_DEBUG_OBJECT (sink, "done changing gconf video sink");
|
||||||
sink->init = TRUE;
|
sink->init = TRUE;
|
||||||
|
@ -191,5 +156,6 @@ gst_gconf_video_sink_change_state (GstElement * element)
|
||||||
return GST_STATE_FAILURE;
|
return GST_STATE_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GST_ELEMENT_CLASS (parent_class)->change_state (element);
|
return GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, change_state,
|
||||||
|
(element), GST_STATE_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,42 +26,14 @@
|
||||||
#include "gstautoaudiosink.h"
|
#include "gstautoaudiosink.h"
|
||||||
#include "gstautodetect.h"
|
#include "gstautodetect.h"
|
||||||
|
|
||||||
static void gst_auto_audio_sink_base_init (GstAutoAudioSinkClass * klass);
|
|
||||||
static void gst_auto_audio_sink_class_init (GstAutoAudioSinkClass * klass);
|
|
||||||
static void gst_auto_audio_sink_init (GstAutoAudioSink * sink);
|
|
||||||
static void gst_auto_audio_sink_detect (GstAutoAudioSink * sink, gboolean fake);
|
static void gst_auto_audio_sink_detect (GstAutoAudioSink * sink, gboolean fake);
|
||||||
static GstElementStateReturn
|
static GstElementStateReturn
|
||||||
gst_auto_audio_sink_change_state (GstElement * element);
|
gst_auto_audio_sink_change_state (GstElement * element);
|
||||||
|
|
||||||
static GstBinClass *parent_class = NULL;
|
GST_BOILERPLATE (GstAutoAudioSink, gst_auto_audio_sink, GstBin, GST_TYPE_BIN);
|
||||||
|
|
||||||
GType
|
|
||||||
gst_auto_audio_sink_get_type (void)
|
|
||||||
{
|
|
||||||
static GType gst_auto_audio_sink_type = 0;
|
|
||||||
|
|
||||||
if (!gst_auto_audio_sink_type) {
|
|
||||||
static const GTypeInfo gst_auto_audio_sink_info = {
|
|
||||||
sizeof (GstAutoAudioSinkClass),
|
|
||||||
(GBaseInitFunc) gst_auto_audio_sink_base_init,
|
|
||||||
NULL,
|
|
||||||
(GClassInitFunc) gst_auto_audio_sink_class_init,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
sizeof (GstAutoAudioSink),
|
|
||||||
0,
|
|
||||||
(GInstanceInitFunc) gst_auto_audio_sink_init,
|
|
||||||
};
|
|
||||||
|
|
||||||
gst_auto_audio_sink_type = g_type_register_static (GST_TYPE_BIN,
|
|
||||||
"GstAutoAudioSink", &gst_auto_audio_sink_info, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return gst_auto_audio_sink_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_auto_audio_sink_base_init (GstAutoAudioSinkClass * klass)
|
gst_auto_audio_sink_base_init (gpointer klass)
|
||||||
{
|
{
|
||||||
GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
|
GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
|
||||||
GstElementDetails gst_auto_audio_sink_details = {
|
GstElementDetails gst_auto_audio_sink_details = {
|
||||||
|
@ -85,8 +57,6 @@ gst_auto_audio_sink_class_init (GstAutoAudioSinkClass * klass)
|
||||||
{
|
{
|
||||||
GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
|
GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
|
||||||
|
|
||||||
parent_class = g_type_class_ref (GST_TYPE_BIN);
|
|
||||||
|
|
||||||
eklass->change_state = gst_auto_audio_sink_change_state;
|
eklass->change_state = gst_auto_audio_sink_change_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,14 +175,17 @@ static void
|
||||||
gst_auto_audio_sink_detect (GstAutoAudioSink * sink, gboolean fake)
|
gst_auto_audio_sink_detect (GstAutoAudioSink * sink, gboolean fake)
|
||||||
{
|
{
|
||||||
GstElement *esink;
|
GstElement *esink;
|
||||||
GstPad *peer = NULL;
|
GstPad *targetpad, *peer = NULL;
|
||||||
|
|
||||||
/* save ghostpad */
|
/* save ghostpad */
|
||||||
if (sink->pad) {
|
if (sink->pad) {
|
||||||
gst_object_ref (GST_OBJECT (sink->pad));
|
peer = GST_PAD_PEER (sink->pad);
|
||||||
peer = GST_PAD_PEER (GST_PAD_REALIZE (sink->pad));
|
if (peer) {
|
||||||
if (peer)
|
|
||||||
gst_pad_unlink (peer, sink->pad);
|
gst_pad_unlink (peer, sink->pad);
|
||||||
|
GST_DEBUG_OBJECT (sink, "Element was linked, caching peer %p", peer);
|
||||||
|
}
|
||||||
|
gst_element_remove_pad (GST_ELEMENT (sink), sink->pad);
|
||||||
|
sink->pad = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* kill old element */
|
/* kill old element */
|
||||||
|
@ -235,17 +208,12 @@ gst_auto_audio_sink_detect (GstAutoAudioSink * sink, gboolean fake)
|
||||||
gst_bin_add (GST_BIN (sink), esink);
|
gst_bin_add (GST_BIN (sink), esink);
|
||||||
|
|
||||||
/* attach ghost pad */
|
/* attach ghost pad */
|
||||||
if (sink->pad) {
|
|
||||||
GST_DEBUG_OBJECT (sink, "Re-doing existing ghostpad");
|
|
||||||
gst_pad_add_ghost_pad (gst_element_get_pad (sink->kid, "sink"), sink->pad);
|
|
||||||
if (GST_ELEMENT (sink)->pads == NULL)
|
|
||||||
gst_element_add_pad (GST_ELEMENT (sink), sink->pad);
|
|
||||||
} else {
|
|
||||||
GST_DEBUG_OBJECT (sink, "Creating new ghostpad");
|
GST_DEBUG_OBJECT (sink, "Creating new ghostpad");
|
||||||
sink->pad = gst_ghost_pad_new ("sink",
|
targetpad = gst_element_get_pad (sink->kid, "sink");
|
||||||
gst_element_get_pad (sink->kid, "sink"));
|
sink->pad = gst_ghost_pad_new ("sink", targetpad);
|
||||||
|
gst_object_unref (targetpad);
|
||||||
gst_element_add_pad (GST_ELEMENT (sink), sink->pad);
|
gst_element_add_pad (GST_ELEMENT (sink), sink->pad);
|
||||||
}
|
|
||||||
if (peer) {
|
if (peer) {
|
||||||
GST_DEBUG_OBJECT (sink, "Linking...");
|
GST_DEBUG_OBJECT (sink, "Linking...");
|
||||||
gst_pad_link (peer, sink->pad);
|
gst_pad_link (peer, sink->pad);
|
||||||
|
@ -266,5 +234,6 @@ gst_auto_audio_sink_change_state (GstElement * element)
|
||||||
return GST_STATE_FAILURE;
|
return GST_STATE_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GST_ELEMENT_CLASS (parent_class)->change_state (element);
|
return GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, change_state,
|
||||||
|
(element), GST_STATE_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,42 +26,14 @@
|
||||||
#include "gstautovideosink.h"
|
#include "gstautovideosink.h"
|
||||||
#include "gstautodetect.h"
|
#include "gstautodetect.h"
|
||||||
|
|
||||||
static void gst_auto_video_sink_base_init (GstAutoVideoSinkClass * klass);
|
|
||||||
static void gst_auto_video_sink_class_init (GstAutoVideoSinkClass * klass);
|
|
||||||
static void gst_auto_video_sink_init (GstAutoVideoSink * sink);
|
|
||||||
static void gst_auto_video_sink_detect (GstAutoVideoSink * sink, gboolean fake);
|
static void gst_auto_video_sink_detect (GstAutoVideoSink * sink, gboolean fake);
|
||||||
static GstElementStateReturn
|
static GstElementStateReturn
|
||||||
gst_auto_video_sink_change_state (GstElement * element);
|
gst_auto_video_sink_change_state (GstElement * element);
|
||||||
|
|
||||||
static GstBinClass *parent_class = NULL;
|
GST_BOILERPLATE (GstAutoVideoSink, gst_auto_video_sink, GstBin, GST_TYPE_BIN);
|
||||||
|
|
||||||
GType
|
|
||||||
gst_auto_video_sink_get_type (void)
|
|
||||||
{
|
|
||||||
static GType gst_auto_video_sink_type = 0;
|
|
||||||
|
|
||||||
if (!gst_auto_video_sink_type) {
|
|
||||||
static const GTypeInfo gst_auto_video_sink_info = {
|
|
||||||
sizeof (GstAutoVideoSinkClass),
|
|
||||||
(GBaseInitFunc) gst_auto_video_sink_base_init,
|
|
||||||
NULL,
|
|
||||||
(GClassInitFunc) gst_auto_video_sink_class_init,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
sizeof (GstAutoVideoSink),
|
|
||||||
0,
|
|
||||||
(GInstanceInitFunc) gst_auto_video_sink_init,
|
|
||||||
};
|
|
||||||
|
|
||||||
gst_auto_video_sink_type = g_type_register_static (GST_TYPE_BIN,
|
|
||||||
"GstAutoVideoSink", &gst_auto_video_sink_info, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return gst_auto_video_sink_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_auto_video_sink_base_init (GstAutoVideoSinkClass * klass)
|
gst_auto_video_sink_base_init (gpointer klass)
|
||||||
{
|
{
|
||||||
GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
|
GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
|
||||||
GstElementDetails gst_auto_video_sink_details = {
|
GstElementDetails gst_auto_video_sink_details = {
|
||||||
|
@ -85,8 +57,6 @@ gst_auto_video_sink_class_init (GstAutoVideoSinkClass * klass)
|
||||||
{
|
{
|
||||||
GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
|
GstElementClass *eklass = GST_ELEMENT_CLASS (klass);
|
||||||
|
|
||||||
parent_class = g_type_class_ref (GST_TYPE_BIN);
|
|
||||||
|
|
||||||
eklass->change_state = gst_auto_video_sink_change_state;
|
eklass->change_state = gst_auto_video_sink_change_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,9 +117,11 @@ gst_auto_video_sink_find_best (GstAutoVideoSink * sink)
|
||||||
GstElementFactory *f = GST_ELEMENT_FACTORY (list->data);
|
GstElementFactory *f = GST_ELEMENT_FACTORY (list->data);
|
||||||
GstElement *el;
|
GstElement *el;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (sink, "Trying %s", GST_PLUGIN_FEATURE (f)->name);
|
||||||
if ((el = gst_element_factory_create (f, "actual-sink"))) {
|
if ((el = gst_element_factory_create (f, "actual-sink"))) {
|
||||||
|
GST_DEBUG_OBJECT (sink, "Changing state to READY");
|
||||||
if (gst_element_set_state (el, GST_STATE_READY) == GST_STATE_SUCCESS) {
|
if (gst_element_set_state (el, GST_STATE_READY) == GST_STATE_SUCCESS) {
|
||||||
gst_element_set_state (el, GST_STATE_NULL);
|
GST_DEBUG_OBJECT (sink, "success");
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,14 +136,17 @@ static void
|
||||||
gst_auto_video_sink_detect (GstAutoVideoSink * sink, gboolean fake)
|
gst_auto_video_sink_detect (GstAutoVideoSink * sink, gboolean fake)
|
||||||
{
|
{
|
||||||
GstElement *esink;
|
GstElement *esink;
|
||||||
GstPad *peer = NULL;
|
GstPad *targetpad, *peer = NULL;
|
||||||
|
|
||||||
/* save ghostpad */
|
/* save ghostpad */
|
||||||
if (sink->pad) {
|
if (sink->pad) {
|
||||||
gst_object_ref (GST_OBJECT (sink->pad));
|
peer = GST_PAD_PEER (sink->pad);
|
||||||
peer = GST_PAD_PEER (GST_PAD_REALIZE (sink->pad));
|
if (peer) {
|
||||||
if (peer)
|
|
||||||
gst_pad_unlink (peer, sink->pad);
|
gst_pad_unlink (peer, sink->pad);
|
||||||
|
GST_DEBUG_OBJECT (sink, "Element was linked, caching peer %p", peer);
|
||||||
|
}
|
||||||
|
gst_element_remove_pad (GST_ELEMENT (sink), sink->pad);
|
||||||
|
sink->pad = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* kill old element */
|
/* kill old element */
|
||||||
|
@ -185,7 +160,6 @@ gst_auto_video_sink_detect (GstAutoVideoSink * sink, gboolean fake)
|
||||||
GST_DEBUG_OBJECT (sink, "Creating new kid (%ssink)", fake ? "fake" : "video");
|
GST_DEBUG_OBJECT (sink, "Creating new kid (%ssink)", fake ? "fake" : "video");
|
||||||
if (fake) {
|
if (fake) {
|
||||||
esink = gst_element_factory_make ("fakesink", "temporary-sink");
|
esink = gst_element_factory_make ("fakesink", "temporary-sink");
|
||||||
g_return_if_fail (esink);
|
|
||||||
} else if (!(esink = gst_auto_video_sink_find_best (sink))) {
|
} else if (!(esink = gst_auto_video_sink_find_best (sink))) {
|
||||||
GST_ELEMENT_ERROR (sink, LIBRARY, INIT, (NULL),
|
GST_ELEMENT_ERROR (sink, LIBRARY, INIT, (NULL),
|
||||||
("Failed to find a supported video sink"));
|
("Failed to find a supported video sink"));
|
||||||
|
@ -195,15 +169,12 @@ gst_auto_video_sink_detect (GstAutoVideoSink * sink, gboolean fake)
|
||||||
gst_bin_add (GST_BIN (sink), esink);
|
gst_bin_add (GST_BIN (sink), esink);
|
||||||
|
|
||||||
/* attach ghost pad */
|
/* attach ghost pad */
|
||||||
if (sink->pad) {
|
|
||||||
GST_DEBUG_OBJECT (sink, "Re-doing existing ghostpad");
|
|
||||||
gst_pad_add_ghost_pad (gst_element_get_pad (sink->kid, "sink"), sink->pad);
|
|
||||||
} else {
|
|
||||||
GST_DEBUG_OBJECT (sink, "Creating new ghostpad");
|
GST_DEBUG_OBJECT (sink, "Creating new ghostpad");
|
||||||
sink->pad = gst_ghost_pad_new ("sink",
|
targetpad = gst_element_get_pad (sink->kid, "sink");
|
||||||
gst_element_get_pad (sink->kid, "sink"));
|
sink->pad = gst_ghost_pad_new ("sink", targetpad);
|
||||||
|
gst_object_unref (targetpad);
|
||||||
gst_element_add_pad (GST_ELEMENT (sink), sink->pad);
|
gst_element_add_pad (GST_ELEMENT (sink), sink->pad);
|
||||||
}
|
|
||||||
if (peer) {
|
if (peer) {
|
||||||
GST_DEBUG_OBJECT (sink, "Linking...");
|
GST_DEBUG_OBJECT (sink, "Linking...");
|
||||||
gst_pad_link (peer, sink->pad);
|
gst_pad_link (peer, sink->pad);
|
||||||
|
|
Loading…
Reference in a new issue