mirror of
https://github.com/brl/mutter.git
synced 2025-02-20 15:04:09 +00:00
2006-06-12 Matthew Allum <mallum@openedhand.com>
* clutter/Makefile.am: * clutter/clutter-media.c: * clutter/clutter-media.h: * clutter/clutter-video-texture.c: * clutter/clutter-video-texture.h: * clutter/clutter.h: * examples/test-video.c: (main): * examples/video-cube.c: (main): Add new 'media' interface. Drop Totem based video playback code replace with newly rewritten supper Jorn based code. Clutter is now fully LGPL. * clutter/clutter-texture.c: (tile_dimension): Remove uneeded overlap code.
This commit is contained in:
parent
3d7c3295f4
commit
9afe83e695
17
ChangeLog
17
ChangeLog
@ -1,3 +1,20 @@
|
|||||||
|
2006-06-12 Matthew Allum <mallum@openedhand.com>
|
||||||
|
|
||||||
|
* clutter/Makefile.am:
|
||||||
|
* clutter/clutter-media.c:
|
||||||
|
* clutter/clutter-media.h:
|
||||||
|
* clutter/clutter-video-texture.c:
|
||||||
|
* clutter/clutter-video-texture.h:
|
||||||
|
* clutter/clutter.h:
|
||||||
|
* examples/test-video.c: (main):
|
||||||
|
* examples/video-cube.c: (main):
|
||||||
|
Add new 'media' interface.
|
||||||
|
Drop Totem based video playback code replace with newly
|
||||||
|
rewritten supper Jorn based code. Clutter is now fully LGPL.
|
||||||
|
|
||||||
|
* clutter/clutter-texture.c: (tile_dimension):
|
||||||
|
Remove uneeded overlap code.
|
||||||
|
|
||||||
2006-06-08 Iain Holmes <iain@openedhand.com>
|
2006-06-08 Iain Holmes <iain@openedhand.com>
|
||||||
|
|
||||||
* clutter/clutter-texture.c:
|
* clutter/clutter-texture.c:
|
||||||
|
@ -7,6 +7,7 @@ BUILT_SOURCES = $(MARSHALFILES) $(ENUMFILES)
|
|||||||
|
|
||||||
source_h = clutter-keysyms.h \
|
source_h = clutter-keysyms.h \
|
||||||
clutter-util.h \
|
clutter-util.h \
|
||||||
|
clutter-media.h \
|
||||||
clutter-event.h \
|
clutter-event.h \
|
||||||
clutter-color.h \
|
clutter-color.h \
|
||||||
clutter-timeline.h \
|
clutter-timeline.h \
|
||||||
@ -65,6 +66,7 @@ CLEANFILES = $(BUILT_SOURCES) stamp-clutter-enum-types.h
|
|||||||
|
|
||||||
source_c = clutter-main.c \
|
source_c = clutter-main.c \
|
||||||
clutter-util.c \
|
clutter-util.c \
|
||||||
|
clutter-media.c \
|
||||||
clutter-event.c \
|
clutter-event.c \
|
||||||
clutter-color.c \
|
clutter-color.c \
|
||||||
clutter-timeline.c \
|
clutter-timeline.c \
|
||||||
|
@ -143,6 +143,16 @@ clutter_label_make_pixbuf (ClutterLabel *label)
|
|||||||
g_object_unref (pixbuf);
|
g_object_unref (pixbuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clutter_label_allocate_coords (ClutterElement *element,
|
||||||
|
ClutterElementBox *box)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
clutter_label_set_property (GObject *object,
|
clutter_label_set_property (GObject *object,
|
||||||
guint prop_id,
|
guint prop_id,
|
||||||
|
282
clutter/clutter-media.c
Normal file
282
clutter/clutter-media.c
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
/*
|
||||||
|
* Clutter.
|
||||||
|
*
|
||||||
|
* An OpenGL based 'interactive canvas' library.
|
||||||
|
*
|
||||||
|
* Authored By Matthew Allum <mallum@openedhand.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2006 OpenedHand
|
||||||
|
*
|
||||||
|
* 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 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, write to the
|
||||||
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "clutter-media.h"
|
||||||
|
#include "clutter-main.h"
|
||||||
|
#include "clutter-enum-types.h"
|
||||||
|
#include "clutter-private.h" /* for DBG */
|
||||||
|
|
||||||
|
static void clutter_media_base_init (gpointer g_class);
|
||||||
|
|
||||||
|
GType
|
||||||
|
clutter_media_get_type (void)
|
||||||
|
{
|
||||||
|
static GType media_type = 0;
|
||||||
|
|
||||||
|
if (!media_type)
|
||||||
|
{
|
||||||
|
static const GTypeInfo media_info =
|
||||||
|
{
|
||||||
|
sizeof (ClutterMediaInterface),
|
||||||
|
clutter_media_base_init,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
media_type = g_type_register_static (G_TYPE_INTERFACE, "ClutterMedia",
|
||||||
|
&media_info, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return media_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clutter_media_base_init (gpointer g_iface)
|
||||||
|
{
|
||||||
|
static gboolean initialized = FALSE;
|
||||||
|
|
||||||
|
if (!initialized)
|
||||||
|
{
|
||||||
|
initialized = TRUE;
|
||||||
|
|
||||||
|
/* props */
|
||||||
|
|
||||||
|
g_object_interface_install_property
|
||||||
|
(g_iface,
|
||||||
|
g_param_spec_string
|
||||||
|
("uri",
|
||||||
|
"URI",
|
||||||
|
"The loaded URI.",
|
||||||
|
NULL,
|
||||||
|
G_PARAM_READWRITE |
|
||||||
|
G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
|
||||||
|
G_PARAM_STATIC_BLURB));
|
||||||
|
|
||||||
|
g_object_interface_install_property
|
||||||
|
(g_iface,
|
||||||
|
g_param_spec_boolean
|
||||||
|
("playing",
|
||||||
|
"Playing",
|
||||||
|
"TRUE if playing.",
|
||||||
|
FALSE,
|
||||||
|
G_PARAM_READWRITE |
|
||||||
|
G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
|
||||||
|
G_PARAM_STATIC_BLURB));
|
||||||
|
|
||||||
|
g_object_interface_install_property
|
||||||
|
(g_iface,
|
||||||
|
g_param_spec_int
|
||||||
|
("position",
|
||||||
|
"Position",
|
||||||
|
"The position in the current stream in seconds.",
|
||||||
|
0, G_MAXINT, 0,
|
||||||
|
G_PARAM_READWRITE |
|
||||||
|
G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
|
||||||
|
G_PARAM_STATIC_BLURB));
|
||||||
|
|
||||||
|
g_object_interface_install_property
|
||||||
|
(g_iface,
|
||||||
|
g_param_spec_double
|
||||||
|
("volume",
|
||||||
|
"Volume",
|
||||||
|
"The audio volume.",
|
||||||
|
0, 100, 50,
|
||||||
|
G_PARAM_READWRITE |
|
||||||
|
G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
|
||||||
|
G_PARAM_STATIC_BLURB));
|
||||||
|
|
||||||
|
g_object_interface_install_property
|
||||||
|
(g_iface,
|
||||||
|
g_param_spec_boolean
|
||||||
|
("can-seek",
|
||||||
|
"Can seek",
|
||||||
|
"TRUE if the current stream is seekable.",
|
||||||
|
FALSE,
|
||||||
|
G_PARAM_READABLE |
|
||||||
|
G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
|
||||||
|
G_PARAM_STATIC_BLURB));
|
||||||
|
|
||||||
|
g_object_interface_install_property
|
||||||
|
(g_iface,
|
||||||
|
g_param_spec_int
|
||||||
|
("buffer-percent",
|
||||||
|
"Buffer percent",
|
||||||
|
"The percentage the current stream buffer is filled.",
|
||||||
|
0, 100, 0,
|
||||||
|
G_PARAM_READABLE |
|
||||||
|
G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
|
||||||
|
G_PARAM_STATIC_BLURB));
|
||||||
|
|
||||||
|
g_object_interface_install_property
|
||||||
|
(g_iface,
|
||||||
|
g_param_spec_int
|
||||||
|
("duration",
|
||||||
|
"Duration",
|
||||||
|
"The duration of the current stream in seconds.",
|
||||||
|
0, G_MAXINT, 0,
|
||||||
|
G_PARAM_READABLE |
|
||||||
|
G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
|
||||||
|
G_PARAM_STATIC_BLURB));
|
||||||
|
|
||||||
|
/* signals */
|
||||||
|
|
||||||
|
g_signal_new ("metadata-available",
|
||||||
|
CLUTTER_TYPE_MEDIA,
|
||||||
|
G_SIGNAL_RUN_LAST,
|
||||||
|
G_STRUCT_OFFSET (ClutterMediaInterface,
|
||||||
|
metadata_available),
|
||||||
|
NULL, NULL,
|
||||||
|
g_cclosure_marshal_VOID__POINTER,
|
||||||
|
G_TYPE_NONE, 1, G_TYPE_POINTER);
|
||||||
|
|
||||||
|
g_signal_new ("eos",
|
||||||
|
CLUTTER_TYPE_MEDIA,
|
||||||
|
G_SIGNAL_RUN_LAST,
|
||||||
|
G_STRUCT_OFFSET (ClutterMediaInterface,
|
||||||
|
eos),
|
||||||
|
NULL, NULL,
|
||||||
|
g_cclosure_marshal_VOID__VOID,
|
||||||
|
G_TYPE_NONE, 0);
|
||||||
|
|
||||||
|
g_signal_new ("error",
|
||||||
|
CLUTTER_TYPE_MEDIA,
|
||||||
|
G_SIGNAL_RUN_LAST,
|
||||||
|
G_STRUCT_OFFSET (ClutterMediaInterface,
|
||||||
|
error),
|
||||||
|
NULL, NULL,
|
||||||
|
g_cclosure_marshal_VOID__POINTER,
|
||||||
|
G_TYPE_NONE, 1, G_TYPE_POINTER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
clutter_media_set_uri (ClutterMedia *media,
|
||||||
|
const char *uri)
|
||||||
|
{
|
||||||
|
g_return_if_fail (CLUTTER_IS_MEDIA(media));
|
||||||
|
|
||||||
|
CLUTTER_MEDIA_GET_INTERFACE (media)->set_uri (media, uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char*
|
||||||
|
clutter_media_get_uri (ClutterMedia *media)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (CLUTTER_IS_MEDIA(media), NULL);
|
||||||
|
|
||||||
|
return CLUTTER_MEDIA_GET_INTERFACE (media)->get_uri (media);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
clutter_media_set_playing (ClutterMedia *media,
|
||||||
|
gboolean playing)
|
||||||
|
{
|
||||||
|
g_return_if_fail (CLUTTER_IS_MEDIA(media));
|
||||||
|
|
||||||
|
CLUTTER_MEDIA_GET_INTERFACE (media)->set_playing (media, playing);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
clutter_media_get_playing (ClutterMedia *media)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (CLUTTER_IS_MEDIA(media), FALSE);
|
||||||
|
|
||||||
|
return CLUTTER_MEDIA_GET_INTERFACE (media)->get_playing (media);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
clutter_media_set_position (ClutterMedia *media,
|
||||||
|
int position)
|
||||||
|
{
|
||||||
|
g_return_if_fail (CLUTTER_IS_MEDIA(media));
|
||||||
|
|
||||||
|
CLUTTER_MEDIA_GET_INTERFACE (media)->set_position (media, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
clutter_media_get_position (ClutterMedia *media)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (CLUTTER_IS_MEDIA(media), 0);
|
||||||
|
|
||||||
|
return CLUTTER_MEDIA_GET_INTERFACE (media)->get_position (media);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
clutter_media_set_volume (ClutterMedia *media,
|
||||||
|
double volume)
|
||||||
|
{
|
||||||
|
g_return_if_fail (CLUTTER_IS_MEDIA(media));
|
||||||
|
|
||||||
|
CLUTTER_MEDIA_GET_INTERFACE (media)->set_position (media, volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
double
|
||||||
|
clutter_media_get_volume (ClutterMedia *media)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (CLUTTER_IS_MEDIA(media), 0.0);
|
||||||
|
|
||||||
|
return CLUTTER_MEDIA_GET_INTERFACE (media)->get_volume (media);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
clutter_media_get_can_seek (ClutterMedia *media)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (CLUTTER_IS_MEDIA(media), FALSE);
|
||||||
|
|
||||||
|
return CLUTTER_MEDIA_GET_INTERFACE (media)->can_seek (media);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
clutter_media_get_buffer_percent (ClutterMedia *media)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (CLUTTER_IS_MEDIA(media), 0);
|
||||||
|
|
||||||
|
return CLUTTER_MEDIA_GET_INTERFACE (media)->get_buffer_percent (media);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
clutter_media_get_duration (ClutterMedia *media)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (CLUTTER_IS_MEDIA(media), 0);
|
||||||
|
|
||||||
|
return CLUTTER_MEDIA_GET_INTERFACE (media)->get_duration (media);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* helper funcs */
|
||||||
|
|
||||||
|
void
|
||||||
|
clutter_media_set_filename (ClutterMedia *media, const gchar *filename)
|
||||||
|
{
|
||||||
|
gchar *uri;
|
||||||
|
|
||||||
|
if (filename[0] != '/')
|
||||||
|
uri = g_strdup_printf ("file://%s/%s", g_get_current_dir (), filename);
|
||||||
|
else
|
||||||
|
uri = g_strdup_printf ("file://%s", filename);
|
||||||
|
|
||||||
|
clutter_media_set_uri (media, uri);
|
||||||
|
|
||||||
|
g_free(uri);
|
||||||
|
}
|
124
clutter/clutter-media.h
Normal file
124
clutter/clutter-media.h
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* Clutter.
|
||||||
|
*
|
||||||
|
* An OpenGL based 'interactive canvas' library.
|
||||||
|
*
|
||||||
|
* Authored By Matthew Allum <mallum@openedhand.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2006 OpenedHand
|
||||||
|
*
|
||||||
|
* 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 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, write to the
|
||||||
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _HAVE_CLUTTER_MEDIA_H
|
||||||
|
#define _HAVE_CLUTTER_MEDIA_H
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <gst/gsttaglist.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define CLUTTER_TYPE_MEDIA clutter_media_get_type()
|
||||||
|
|
||||||
|
#define CLUTTER_MEDIA(obj) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_CAST ((obj), \
|
||||||
|
CLUTTER_TYPE_MEDIA, ClutterMedia))
|
||||||
|
|
||||||
|
#define CLUTTER_IS_MEDIA(obj) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
|
||||||
|
CLUTTER_TYPE_MEDIA))
|
||||||
|
|
||||||
|
#define CLUTTER_MEDIA_GET_INTERFACE(obj) \
|
||||||
|
(G_TYPE_INSTANCE_GET_INTERFACE ((obj), \
|
||||||
|
CLUTTER_TYPE_MEDIA, ClutterMediaInterface))
|
||||||
|
|
||||||
|
typedef struct _ClutterMedia ClutterMedia;
|
||||||
|
typedef struct _ClutterMediaInterface ClutterMediaInterface;
|
||||||
|
|
||||||
|
struct _ClutterMediaInterface
|
||||||
|
{
|
||||||
|
GTypeInterface base_iface;
|
||||||
|
void (*set_uri) (ClutterMedia *media,
|
||||||
|
const char *uri);
|
||||||
|
const char *(*get_uri) (ClutterMedia *media);
|
||||||
|
void (*set_playing) (ClutterMedia *media,
|
||||||
|
gboolean playing);
|
||||||
|
gboolean (*get_playing) (ClutterMedia *media);
|
||||||
|
void (*set_position) (ClutterMedia *media,
|
||||||
|
int position);
|
||||||
|
int (*get_position) (ClutterMedia *media);
|
||||||
|
void (*set_volume) (ClutterMedia *media,
|
||||||
|
double volume);
|
||||||
|
double (*get_volume) (ClutterMedia *media);
|
||||||
|
gboolean (*can_seek) (ClutterMedia *media);
|
||||||
|
int (*get_buffer_percent) (ClutterMedia *media);
|
||||||
|
int (*get_duration) (ClutterMedia *media);
|
||||||
|
|
||||||
|
/* signals */
|
||||||
|
|
||||||
|
void (* metadata_available) (ClutterMedia *media,
|
||||||
|
GstTagList *tag_list);
|
||||||
|
void (* eos) (ClutterMedia *media);
|
||||||
|
void (* error) (ClutterMedia *media,
|
||||||
|
GError *error);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
GType clutter_media_get_type (void);
|
||||||
|
|
||||||
|
void
|
||||||
|
clutter_media_set_uri (ClutterMedia *media,
|
||||||
|
const char *uri);
|
||||||
|
const char *
|
||||||
|
clutter_media_get_uri (ClutterMedia *media);
|
||||||
|
|
||||||
|
void
|
||||||
|
clutter_media_set_playing (ClutterMedia *media,
|
||||||
|
gboolean playing);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
clutter_media_get_playing (ClutterMedia *media);
|
||||||
|
|
||||||
|
void
|
||||||
|
clutter_media_set_position (ClutterMedia *media,
|
||||||
|
int position);
|
||||||
|
|
||||||
|
int
|
||||||
|
clutter_media_get_position (ClutterMedia *media);
|
||||||
|
|
||||||
|
void
|
||||||
|
clutter_media_set_volume (ClutterMedia *media,
|
||||||
|
double volume);
|
||||||
|
|
||||||
|
double
|
||||||
|
clutter_media_get_volume (ClutterMedia *media);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
clutter_media_get_can_seek (ClutterMedia *media);
|
||||||
|
|
||||||
|
int
|
||||||
|
clutter_media_get_buffer_percent (ClutterMedia *media);
|
||||||
|
|
||||||
|
int
|
||||||
|
clutter_media_get_duration (ClutterMedia *media);
|
||||||
|
|
||||||
|
void
|
||||||
|
clutter_media_set_filename (ClutterMedia *media,
|
||||||
|
const gchar *filename);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif
|
@ -40,9 +40,6 @@ G_DEFINE_TYPE (ClutterTexture, clutter_texture, CLUTTER_TYPE_ELEMENT);
|
|||||||
#define PIXEL_TYPE GL_UNSIGNED_INT_8_8_8_8_REV
|
#define PIXEL_TYPE GL_UNSIGNED_INT_8_8_8_8_REV
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define OVERLAP 0 /* Dont need as CLAMP_TO_EDGE */
|
|
||||||
|
|
||||||
/* FIXME: actually use */
|
|
||||||
typedef struct ClutterTextureTileDimention
|
typedef struct ClutterTextureTileDimention
|
||||||
{
|
{
|
||||||
gint pos, size, waste;
|
gint pos, size, waste;
|
||||||
@ -144,8 +141,7 @@ tile_dimension (int to_fill,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
to_fill -= (size - OVERLAP);
|
to_fill -= size; pos += size;
|
||||||
pos += size - OVERLAP;
|
|
||||||
while (size >= 2 * to_fill || size - to_fill > waste)
|
while (size >= 2 * to_fill || size - to_fill > waste)
|
||||||
size /= 2;
|
size /= 2;
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Clutter.
|
||||||
|
*
|
||||||
|
* An OpenGL based 'interactive canvas' library.
|
||||||
|
*
|
||||||
|
* Authored By Matthew Allum <mallum@openedhand.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2006 OpenedHand
|
||||||
|
*
|
||||||
|
* 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 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, write to the
|
||||||
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef _HAVE_CLUTTER_VIDEO_TEXTURE_H
|
#ifndef _HAVE_CLUTTER_VIDEO_TEXTURE_H
|
||||||
#define _HAVE_CLUTTER_VIDEO_TEXTURE_H
|
#define _HAVE_CLUTTER_VIDEO_TEXTURE_H
|
||||||
|
|
||||||
#include <glib-object.h>
|
#include <glib-object.h>
|
||||||
#include <clutter/clutter-element.h>
|
#include <clutter/clutter-element.h>
|
||||||
#include <clutter/clutter-texture.h>
|
#include <clutter/clutter-texture.h>
|
||||||
|
#include <clutter/clutter-media.h>
|
||||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
@ -34,55 +60,7 @@ typedef struct ClutterVideoTexturePrivate ClutterVideoTexturePrivate ;
|
|||||||
typedef struct _ClutterVideoTexture ClutterVideoTexture;
|
typedef struct _ClutterVideoTexture ClutterVideoTexture;
|
||||||
typedef struct _ClutterVideoTextureClass ClutterVideoTextureClass;
|
typedef struct _ClutterVideoTextureClass ClutterVideoTextureClass;
|
||||||
|
|
||||||
|
/* #define CLUTTER_VIDEO_TEXTURE_ERROR clutter_video_texture_error_quark() */
|
||||||
#define CLUTTER_VIDEO_TEXTURE_ERROR clutter_video_texture_error_quark()
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
/* Plugins */
|
|
||||||
CLUTTER_VIDEO_TEXTURE_ERROR_AUDIO_PLUGIN,
|
|
||||||
CLUTTER_VIDEO_TEXTURE_ERROR_NO_PLUGIN_FOR_FILE,
|
|
||||||
CLUTTER_VIDEO_TEXTURE_ERROR_VIDEO_PLUGIN,
|
|
||||||
CLUTTER_VIDEO_TEXTURE_ERROR_AUDIO_BUSY,
|
|
||||||
|
|
||||||
/* File */
|
|
||||||
CLUTTER_VIDEO_TEXTURE_ERROR_BROKEN_FILE,
|
|
||||||
CLUTTER_VIDEO_TEXTURE_ERROR_FILE_GENERIC,
|
|
||||||
CLUTTER_VIDEO_TEXTURE_ERROR_FILE_PERMISSION,
|
|
||||||
CLUTTER_VIDEO_TEXTURE_ERROR_FILE_ENCRYPTED,
|
|
||||||
CLUTTER_VIDEO_TEXTURE_ERROR_FILE_NOT_FOUND,
|
|
||||||
|
|
||||||
/* Devices */
|
|
||||||
CLUTTER_VIDEO_TEXTURE_ERROR_DVD_ENCRYPTED,
|
|
||||||
CLUTTER_VIDEO_TEXTURE_ERROR_INVALID_DEVICE,
|
|
||||||
|
|
||||||
/* Network */
|
|
||||||
CLUTTER_VIDEO_TEXTURE_ERROR_UNKNOWN_HOST,
|
|
||||||
CLUTTER_VIDEO_TEXTURE_ERROR_NETWORK_UNREACHABLE,
|
|
||||||
CLUTTER_VIDEO_TEXTURE_ERROR_CONNECTION_REFUSED,
|
|
||||||
|
|
||||||
/* Generic */
|
|
||||||
CLUTTER_VIDEO_TEXTURE_ERROR_UNVALID_LOCATION,
|
|
||||||
CLUTTER_VIDEO_TEXTURE_ERROR_GENERIC,
|
|
||||||
CLUTTER_VIDEO_TEXTURE_ERROR_CODEC_NOT_HANDLED,
|
|
||||||
CLUTTER_VIDEO_TEXTURE_ERROR_AUDIO_ONLY,
|
|
||||||
CLUTTER_VIDEO_TEXTURE_ERROR_CANNOT_CAPTURE,
|
|
||||||
CLUTTER_VIDEO_TEXTURE_ERROR_READ_ERROR,
|
|
||||||
CLUTTER_VIDEO_TEXTURE_ERROR_PLUGIN_LOAD,
|
|
||||||
CLUTTER_VIDEO_TEXTURE_ERROR_STILL_IMAGE,
|
|
||||||
CLUTTER_VIDEO_TEXTURE_ERROR_EMPTY_FILE
|
|
||||||
} ClutterVideoTextureError;
|
|
||||||
|
|
||||||
GQuark clutter_video_texture_error_quark (void);
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
CLUTTER_VIDEO_TEXTURE_AUTO,
|
|
||||||
CLUTTER_VIDEO_TEXTURE_SQUARE,
|
|
||||||
CLUTTER_VIDEO_TEXTURE_FOURBYTHREE,
|
|
||||||
CLUTTER_VIDEO_TEXTURE_ANAMORPHIC,
|
|
||||||
CLUTTER_VIDEO_TEXTURE_DVB
|
|
||||||
} ClutterVideoTextureAspectRatio;
|
|
||||||
|
|
||||||
struct _ClutterVideoTexture
|
struct _ClutterVideoTexture
|
||||||
{
|
{
|
||||||
@ -94,20 +72,20 @@ struct _ClutterVideoTextureClass
|
|||||||
{
|
{
|
||||||
ClutterTextureClass parent_class;
|
ClutterTextureClass parent_class;
|
||||||
|
|
||||||
void (*error) (ClutterVideoTexture *cvt, const char *message,
|
/* Signals */
|
||||||
gboolean playback_stopped, gboolean fatal);
|
void (* tag_list_available) (ClutterVideoTexture *video_texture,
|
||||||
void (*eos) (ClutterVideoTexture *cvt);
|
GstTagList *tag_list);
|
||||||
void (*got_metadata) (ClutterVideoTexture *cvt);
|
void (* eos) (ClutterVideoTexture *video_texture);
|
||||||
void (*got_redirect) (ClutterVideoTexture *cvt, const char *mrl);
|
void (* error) (ClutterVideoTexture *video_texture,
|
||||||
void (*title_change) (ClutterVideoTexture *cvt, const char *title);
|
GError *error);
|
||||||
void (*channels_change) (ClutterVideoTexture *cvt);
|
|
||||||
void (*tick) (ClutterVideoTexture *cvt,
|
/* Future padding */
|
||||||
gint64 current_time,
|
void (* _clutter_reserved1) (void);
|
||||||
gint64 stream_length,
|
void (* _clutter_reserved2) (void);
|
||||||
float current_position,
|
void (* _clutter_reserved3) (void);
|
||||||
gboolean seekable);
|
void (* _clutter_reserved4) (void);
|
||||||
void (*buffering) (ClutterVideoTexture *cvt, guint progress);
|
void (* _clutter_reserved5) (void);
|
||||||
void (*speed_warning) (ClutterVideoTexture *cvt);
|
void (* _clutter_reserved6) (void);
|
||||||
};
|
};
|
||||||
|
|
||||||
GType clutter_video_texture_get_type (void);
|
GType clutter_video_texture_get_type (void);
|
||||||
@ -115,99 +93,6 @@ GType clutter_video_texture_get_type (void);
|
|||||||
ClutterElement*
|
ClutterElement*
|
||||||
clutter_video_texture_new (void);
|
clutter_video_texture_new (void);
|
||||||
|
|
||||||
gboolean
|
|
||||||
clutter_video_texture_open (ClutterVideoTexture *video_texture,
|
|
||||||
const gchar *mrl,
|
|
||||||
const gchar *subtitle_uri,
|
|
||||||
GError **error);
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
clutter_video_texture_play (ClutterVideoTexture *video_texture,
|
|
||||||
GError ** error);
|
|
||||||
|
|
||||||
void
|
|
||||||
clutter_video_texture_pause (ClutterVideoTexture *video_texture);
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
clutter_video_texture_can_direct_seek (ClutterVideoTexture *video_texture);
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
clutter_video_texture_seek_time (ClutterVideoTexture *video_texture,
|
|
||||||
gint64 time,
|
|
||||||
GError **gerror);
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
clutter_video_texture_seek (ClutterVideoTexture *video_texture,
|
|
||||||
float position,
|
|
||||||
GError **error);
|
|
||||||
|
|
||||||
void
|
|
||||||
clutter_video_texture_stop (ClutterVideoTexture *video_texture);
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
clutter_video_texture_can_set_volume (ClutterVideoTexture *video_texture);
|
|
||||||
|
|
||||||
void
|
|
||||||
clutter_video_texture_set_volume (ClutterVideoTexture *video_texture,
|
|
||||||
int volume);
|
|
||||||
int
|
|
||||||
clutter_video_texture_get_volume (ClutterVideoTexture *video_texture);
|
|
||||||
|
|
||||||
gint64
|
|
||||||
clutter_video_texture_get_current_time (ClutterVideoTexture *video_texture);
|
|
||||||
|
|
||||||
gint64
|
|
||||||
clutter_video_texture_get_stream_length (ClutterVideoTexture *video_texture);
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
clutter_video_texture_is_playing (ClutterVideoTexture *video_texture);
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
clutter_video_texture_is_seekable (ClutterVideoTexture * video_texture);
|
|
||||||
|
|
||||||
float
|
|
||||||
clutter_video_texture_get_position (ClutterVideoTexture *video_texture);
|
|
||||||
|
|
||||||
void
|
|
||||||
clutter_video_texture_set_aspect_ratio (ClutterVideoTexture *video_texture,
|
|
||||||
ClutterVideoTextureAspectRatio ratio);
|
|
||||||
|
|
||||||
ClutterVideoTextureAspectRatio
|
|
||||||
clutter_video_texture_get_aspect_ratio (ClutterVideoTexture *video_texture);
|
|
||||||
|
|
||||||
/* Metadata
|
|
||||||
* FIXME: This should probably go in some kind of genric 'Media' class
|
|
||||||
* You would open the media, get a media object and then pass
|
|
||||||
* that to the video texture. media object could handle being
|
|
||||||
* just a metadata reader etc...
|
|
||||||
*/
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
CLUTTER_INFO_TITLE,
|
|
||||||
CLUTTER_INFO_ARTIST,
|
|
||||||
CLUTTER_INFO_YEAR,
|
|
||||||
CLUTTER_INFO_ALBUM,
|
|
||||||
CLUTTER_INFO_DURATION,
|
|
||||||
CLUTTER_INFO_TRACK_NUMBER,
|
|
||||||
/* Video */
|
|
||||||
CLUTTER_INFO_HAS_VIDEO,
|
|
||||||
CLUTTER_INFO_DIMENSION_X,
|
|
||||||
CLUTTER_INFO_DIMENSION_Y,
|
|
||||||
CLUTTER_INFO_VIDEO_BITRATE,
|
|
||||||
CLUTTER_INFO_VIDEO_CODEC,
|
|
||||||
CLUTTER_INFO_FPS,
|
|
||||||
/* Audio */
|
|
||||||
CLUTTER_INFO_HAS_AUDIO,
|
|
||||||
CLUTTER_INFO_AUDIO_BITRATE,
|
|
||||||
CLUTTER_INFO_AUDIO_CODEC,
|
|
||||||
} ClutterVideoTextureMetadataType;
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
clutter_video_texture_get_metadata (ClutterVideoTexture *video_texture,
|
|
||||||
ClutterVideoTextureMetadataType type,
|
|
||||||
GValue *value);
|
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "clutter-keysyms.h"
|
#include "clutter-keysyms.h"
|
||||||
#include "clutter-main.h"
|
#include "clutter-main.h"
|
||||||
|
#include "clutter-media.h"
|
||||||
#include "clutter-color.h"
|
#include "clutter-color.h"
|
||||||
#include "clutter-util.h"
|
#include "clutter-util.h"
|
||||||
#include "clutter-event.h"
|
#include "clutter-event.h"
|
||||||
|
@ -2,19 +2,6 @@
|
|||||||
|
|
||||||
ClutterElement *rect; /* um... */
|
ClutterElement *rect; /* um... */
|
||||||
|
|
||||||
gboolean
|
|
||||||
foo (gpointer data)
|
|
||||||
{
|
|
||||||
static int i = 0;
|
|
||||||
|
|
||||||
clutter_element_set_opacity (CLUTTER_ELEMENT(data), i);
|
|
||||||
|
|
||||||
i += 10;
|
|
||||||
|
|
||||||
if (i>255) i = 0;
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void input_cb (ClutterStage *stage,
|
void input_cb (ClutterStage *stage,
|
||||||
ClutterEvent *event,
|
ClutterEvent *event,
|
||||||
@ -27,12 +14,12 @@ void input_cb (ClutterStage *stage,
|
|||||||
{
|
{
|
||||||
if (paused)
|
if (paused)
|
||||||
{
|
{
|
||||||
clutter_video_texture_play (vtex, NULL);
|
clutter_media_set_playing (CLUTTER_MEDIA(vtex), TRUE);
|
||||||
paused = 0;
|
paused = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
clutter_video_texture_pause (vtex);
|
clutter_media_set_playing (CLUTTER_MEDIA(vtex), FALSE);
|
||||||
paused = 1;
|
paused = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,29 +54,30 @@ size_change (ClutterTexture *texture,
|
|||||||
stage_geom.width,
|
stage_geom.width,
|
||||||
new_height);
|
new_height);
|
||||||
|
|
||||||
clutter_element_set_opacity (CLUTTER_ELEMENT (texture), 50);
|
// clutter_element_set_opacity (CLUTTER_ELEMENT (texture), 50);
|
||||||
|
|
||||||
printf("*** Pos set to +%i+%i , %ix%i ***\n",
|
printf("*** Pos set to +%i+%i , %ix%i ***\n",
|
||||||
0, new_y, stage_geom.width, new_height);
|
0, new_y, stage_geom.width, new_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
tick (ClutterVideoTexture *cvt,
|
tick (GObject *object,
|
||||||
gint64 current_time,
|
GParamSpec *pspec,
|
||||||
gint64 stream_length,
|
ClutterLabel *label)
|
||||||
float current_position,
|
|
||||||
gboolean seekable,
|
|
||||||
gpointer userdata)
|
|
||||||
{
|
{
|
||||||
gint w, h;
|
ClutterVideoTexture *vtex;
|
||||||
|
gint w, h, position, duration;
|
||||||
gchar buf[256];
|
gchar buf[256];
|
||||||
ClutterLabel *label = CLUTTER_LABEL(userdata);
|
|
||||||
|
|
||||||
g_snprintf(buf, 256, "%lli/%lli secs",
|
vtex = CLUTTER_VIDEO_TEXTURE(object);
|
||||||
current_time / 1000,
|
|
||||||
stream_length / 1000);
|
position = clutter_media_get_position (CLUTTER_MEDIA(vtex));
|
||||||
|
duration = clutter_media_get_duration (CLUTTER_MEDIA(vtex));
|
||||||
|
|
||||||
|
g_snprintf(buf, 256, "%i / %i", position, duration);
|
||||||
|
|
||||||
clutter_label_set_text (label, buf);
|
clutter_label_set_text (label, buf);
|
||||||
|
|
||||||
clutter_texture_get_base_size (CLUTTER_TEXTURE(label), &w, &h);
|
clutter_texture_get_base_size (CLUTTER_TEXTURE(label), &w, &h);
|
||||||
clutter_element_set_size(rect, w+10, h+10);
|
clutter_element_set_size(rect, w+10, h+10);
|
||||||
}
|
}
|
||||||
@ -100,7 +88,7 @@ main (int argc, char *argv[])
|
|||||||
ClutterElement *label, *vtexture, *ctexture;
|
ClutterElement *label, *vtexture, *ctexture;
|
||||||
ClutterElement *stage;
|
ClutterElement *stage;
|
||||||
ClutterColor rect_color = { 0xde, 0xde, 0xdf, 0xaa };
|
ClutterColor rect_color = { 0xde, 0xde, 0xdf, 0xaa };
|
||||||
ClutterColor stage_color = { 0xff, 0xff, 0xff, 0x00 };
|
ClutterColor stage_color = { 0x00, 0x00, 0x00, 0x00 };
|
||||||
GError *err = NULL;
|
GError *err = NULL;
|
||||||
|
|
||||||
if (argc < 2)
|
if (argc < 2)
|
||||||
@ -110,6 +98,8 @@ main (int argc, char *argv[])
|
|||||||
|
|
||||||
vtexture = clutter_video_texture_new ();
|
vtexture = clutter_video_texture_new ();
|
||||||
|
|
||||||
|
clutter_media_set_filename(CLUTTER_MEDIA(vtexture), argv[1]);
|
||||||
|
|
||||||
stage = clutter_stage_get_default ();
|
stage = clutter_stage_get_default ();
|
||||||
|
|
||||||
/* Broken..
|
/* Broken..
|
||||||
@ -117,8 +107,6 @@ main (int argc, char *argv[])
|
|||||||
g_object_set(vtexture, "repeat-y", TRUE, NULL);
|
g_object_set(vtexture, "repeat-y", TRUE, NULL);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
printf("tiled okey\n");
|
|
||||||
|
|
||||||
if (vtexture == NULL || err != NULL)
|
if (vtexture == NULL || err != NULL)
|
||||||
{
|
{
|
||||||
g_error("failed to create vtexture, err: %s", err->message);
|
g_error("failed to create vtexture, err: %s", err->message);
|
||||||
@ -133,24 +121,13 @@ main (int argc, char *argv[])
|
|||||||
clutter_element_set_position(rect, 5, 5);
|
clutter_element_set_position(rect, 5, 5);
|
||||||
|
|
||||||
ctexture = clutter_clone_texture_new (CLUTTER_TEXTURE(vtexture));
|
ctexture = clutter_clone_texture_new (CLUTTER_TEXTURE(vtexture));
|
||||||
|
clutter_element_set_opacity (CLUTTER_ELEMENT (ctexture), 100);
|
||||||
|
|
||||||
clutter_element_set_size (ctexture, 640, 50);
|
clutter_element_set_size (ctexture, 640, 50);
|
||||||
clutter_element_set_position (ctexture, 0, 430);
|
clutter_element_set_position (ctexture, 0, 430);
|
||||||
|
|
||||||
/*
|
clutter_group_add_many (CLUTTER_GROUP (stage),
|
||||||
clutter_element_set_clip (CLUTTER_ELEMENT(clutter_stage()),
|
vtexture, rect, label, ctexture, NULL);
|
||||||
0, 0, 100, 100);
|
|
||||||
*/
|
|
||||||
|
|
||||||
clutter_video_texture_open(CLUTTER_VIDEO_TEXTURE(vtexture),
|
|
||||||
argv[1],
|
|
||||||
NULL,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
clutter_group_add (CLUTTER_GROUP (stage), vtexture);
|
|
||||||
clutter_group_add (CLUTTER_GROUP (stage), rect);
|
|
||||||
clutter_group_add (CLUTTER_GROUP (stage), label);
|
|
||||||
clutter_group_add (CLUTTER_GROUP (stage), ctexture);
|
|
||||||
|
|
||||||
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
|
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
|
||||||
|
|
||||||
@ -160,11 +137,10 @@ main (int argc, char *argv[])
|
|||||||
|
|
||||||
clutter_group_show_all (CLUTTER_GROUP (stage));
|
clutter_group_show_all (CLUTTER_GROUP (stage));
|
||||||
|
|
||||||
|
clutter_media_set_playing (CLUTTER_MEDIA(vtexture), TRUE);
|
||||||
|
|
||||||
if (!clutter_video_texture_play(CLUTTER_VIDEO_TEXTURE(vtexture), NULL))
|
g_signal_connect (CLUTTER_MEDIA(vtexture),
|
||||||
g_error("failed to play vtexture");
|
"notify::position",
|
||||||
|
|
||||||
g_signal_connect (vtexture, "tick",
|
|
||||||
G_CALLBACK (tick),
|
G_CALLBACK (tick),
|
||||||
label);
|
label);
|
||||||
|
|
||||||
@ -174,8 +150,6 @@ main (int argc, char *argv[])
|
|||||||
"size-change",
|
"size-change",
|
||||||
G_CALLBACK (size_change), NULL);
|
G_CALLBACK (size_change), NULL);
|
||||||
|
|
||||||
/* g_timeout_add (100, foo, vtexture); */
|
|
||||||
|
|
||||||
clutter_main();
|
clutter_main();
|
||||||
|
|
||||||
g_object_unref (stage);
|
g_object_unref (stage);
|
||||||
|
@ -254,17 +254,14 @@ main (int argc, char *argv[])
|
|||||||
g_error("failed to create vtexture, err: %s", err->message);
|
g_error("failed to create vtexture, err: %s", err->message);
|
||||||
}
|
}
|
||||||
|
|
||||||
clutter_video_texture_open(CLUTTER_VIDEO_TEXTURE(vtexture),
|
|
||||||
argv[1],
|
clutter_media_set_filename (CLUTTER_MEDIA(vtexture), argv[1]);
|
||||||
NULL,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
clutter_group_add (CLUTTER_GROUP (stage), texture);
|
clutter_group_add (CLUTTER_GROUP (stage), texture);
|
||||||
clutter_group_add (CLUTTER_GROUP (stage), vtexture);
|
clutter_group_add (CLUTTER_GROUP (stage), vtexture);
|
||||||
clutter_group_show_all (CLUTTER_GROUP (stage));
|
clutter_group_show_all (CLUTTER_GROUP (stage));
|
||||||
|
|
||||||
if (!clutter_video_texture_play(CLUTTER_VIDEO_TEXTURE(vtexture), NULL))
|
clutter_media_set_playing(CLUTTER_MEDIA(vtexture), TRUE);
|
||||||
g_error("failed to play vtexture");
|
|
||||||
|
|
||||||
clutter_main();
|
clutter_main();
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user