mutter/examples/video-player.c
Emmanuele Bassi 212c4a0ee8 2006-07-06 Emmanuele Bassi <ebassi@openedhand.com>
Big rework of the actor management semantics: now ClutterActor
	objects behave like GtkObjects - that is they have an initial
	"floating" reference that gets "sunk" when they are added to
	a ClutterGroup.  This makes a group responsible of de-allocating
	each actor inside it, so you just have to destroy the group to
	get every child actor destroyed.  Also, now you can do:

	  clutter_group_add (group, clutter_video_texture_new ());
	
	without having to care about reference counting and explicit
	unreffing.

	* clutter/clutter-private.h: Add private flags setter and
	getter macros.

	* clutter/clutter-actor.h:
	* clutter/clutter-actor.c: Clean up; inherit from GInitiallyUnowned;
	add a "visible" property; add the "destroy", "show" and "hide"
	signals to ClutterActorClass.

	(clutter_actor_show), (clutter_actor_hide): Refactor a bit; emit
	the "show" and "hide" signals.

	(clutter_actor_set_property), (clutter_actor_get_property),
	(clutter_actor_class_init): Implement the "visible" property; add
	signals.

	(clutter_actor_finalize): Do not leak the actor's name, if it is
	set.
	
	(clutter_actor_dispose): Emit the "destroy" signal here.

	(clutter_actor_init): Sink the initial floating flag if needed.

	(clutter_actor_destroy): Add a function to explicitely destroy
	a ClutterActor.

	(clutter_actor_set_parent), (clutter_actor_get_parent),
	(clutter_actor_unparent): Make set_parent require a valid parent;
	add unparent; check on get_parent; ref_sink the actor when
	setting its parent and unref it when unsetting it.  Probably we'll
	need a function that does reparenting as unparent+set_parent in
	a single shot.

	* clutter/clutter-group.h:
	* clutter/clutter-group.c (clutter_group_dispose),
	(clutter_group_finalize), (clutter_group_add),
	(clutter_group_remove): Make the group destroy its children when
	disposing it; clean up, and use the newly-available
	clutter_actor_unparent().

	* clutter/clutter-stage.h:
	* clutter/clutter-stage.c (clutter_stage_init): ClutterStage is
	a top-level actor; clean up.

	* clutter/clutter-video-texture.h:
	* clutter/clutter-video-texture.c: Clean up.

	* examples/super-oh.c:
	* examples/test.c:
	* examples/video-player.c:
	* examples/test-text.c:
	* examples/video-cube.c: Remove the g_object_unref() call, as the
	ClutterStage object is destroyed on clutter_main_quit().
2006-07-06 17:52:57 +00:00

382 lines
9.7 KiB
C

#include <clutter/clutter.h>
#define SEEK_H 20
#define SEEK_W 690
typedef struct VideoApp
{
ClutterActor *vtexture;
ClutterActor *control, *control_bg, *control_play, *control_pause,
*control_seek1, *control_seek2, *control_seekbar, *control_label;
gboolean controls_showing, paused;
guint controls_timeout;
ClutterTimeline *controls_tl, *effect1_tl;
}
VideoApp;
static void
show_controls (VideoApp *app, gboolean vis);
void
control_tl_cb (ClutterTimeline *timeline,
gint frame_num,
VideoApp *app)
{
guint8 opacity;
clutter_group_show_all (CLUTTER_GROUP (app->control));
clutter_actor_hide (app->paused ? app->control_pause : app->control_play);
clutter_actor_show (app->paused ? app->control_play : app->control_pause);
opacity = ( frame_num * 0xde ) / clutter_timeline_get_n_frames(timeline);
if (!app->controls_showing)
opacity = 0xde - opacity;
clutter_actor_set_opacity (app->control, opacity);
}
void
control_tl_complete_cb (ClutterTimeline *timeline,
VideoApp *app)
{
if (!app->controls_showing)
clutter_group_hide_all (CLUTTER_GROUP (app->control));
app->controls_timeout = 0;
}
static gboolean
controls_timeout_cb (VideoApp *app)
{
show_controls (app, FALSE);
return FALSE;
}
static void
show_controls (VideoApp *app, gboolean vis)
{
if (clutter_timeline_is_playing (app->controls_tl))
return;
if (vis == TRUE && app->controls_showing == FALSE)
{
app->controls_showing = TRUE;
clutter_timeline_start (app->controls_tl);
app->controls_timeout = g_timeout_add (5 * 1000,
(GSourceFunc)controls_timeout_cb,
app);
return;
}
if (vis == TRUE && app->controls_showing == TRUE)
{
if (app->controls_timeout)
{
g_source_remove (app->controls_timeout);
app->controls_timeout
= g_timeout_add (5 * 1000,
(GSourceFunc)controls_timeout_cb,
app);
}
return;
}
if (vis == FALSE && app->controls_showing == TRUE)
{
app->controls_showing = FALSE;
clutter_timeline_start (app->controls_tl);
return;
}
}
void
toggle_pause_state (VideoApp *app)
{
if (app->paused)
{
clutter_media_set_playing (CLUTTER_MEDIA(app->vtexture),
TRUE);
app->paused = FALSE;
clutter_actor_hide (app->control_play);
clutter_actor_show (app->control_pause);
}
else
{
clutter_media_set_playing (CLUTTER_MEDIA(app->vtexture),
FALSE);
app->paused = TRUE;
clutter_actor_hide (app->control_pause);
clutter_actor_show (app->control_play);
}
}
void
input_cb (ClutterStage *stage,
ClutterEvent *event,
gpointer user_data)
{
VideoApp *app = (VideoApp*)user_data;
switch (event->type)
{
case CLUTTER_MOTION:
show_controls (app, TRUE);
break;
case CLUTTER_BUTTON_PRESS:
if (app->controls_showing)
{
ClutterActor *actor;
ClutterButtonEvent *bev = (ClutterButtonEvent *) event;
actor
= clutter_stage_get_actor_at_pos
(CLUTTER_STAGE(clutter_stage_get_default()),
bev->x, bev->y);
if (actor == app->control_pause || actor == app->control_play)
{
toggle_pause_state (app);
return;
}
if (actor == app->control_seek1
|| actor == app->control_seek2
|| actor == app->control_seekbar)
{
gint x, y, dist, pos;
clutter_actor_get_abs_position (app->control_seekbar, &x, &y);
dist = bev->x - x;
CLAMP(dist, 0, SEEK_W);
pos = (dist * clutter_media_get_duration
(CLUTTER_MEDIA(app->vtexture))) / SEEK_W;
clutter_media_set_position (CLUTTER_MEDIA(app->vtexture),
pos);
}
}
break;
case CLUTTER_KEY_RELEASE:
{
ClutterKeyEvent* kev = (ClutterKeyEvent *) event;
switch (clutter_key_event_symbol (kev))
{
case CLUTTER_q:
case CLUTTER_Escape:
clutter_main_quit ();
break;
case CLUTTER_e:
if (!clutter_timeline_is_playing (app->effect1_tl))
clutter_timeline_start (app->effect1_tl);
break;
default:
toggle_pause_state (app);
break;
}
}
default:
break;
}
}
void
size_change (ClutterTexture *texture,
gint width,
gint height,
gpointer user_data)
{
ClutterActor *stage;
ClutterGeometry stage_geom;
gint vid_width, vid_height, new_y, new_height;
clutter_texture_get_base_size (texture, &vid_width, &vid_height);
new_height = ( vid_height * CLUTTER_STAGE_WIDTH() ) / vid_width;
new_y = ( (gint)CLUTTER_STAGE_HEIGHT() - new_height) / 2;
clutter_actor_set_position (CLUTTER_ACTOR (texture), 0, new_y);
clutter_actor_set_size (CLUTTER_ACTOR (texture),
CLUTTER_STAGE_WIDTH(),
new_height);
}
void
tick (GObject *object,
GParamSpec *pspec,
VideoApp *app)
{
ClutterVideoTexture *vtex;
gint w, h, position, duration, seek_w;
gchar buf[256];
vtex = CLUTTER_VIDEO_TEXTURE(object);
position = clutter_media_get_position (CLUTTER_MEDIA(vtex));
duration = clutter_media_get_duration (CLUTTER_MEDIA(vtex));
if (duration == 0 || position == 0)
return;
clutter_actor_set_size (app->control_seekbar,
(position * SEEK_W) / duration,
SEEK_H);
}
int
effect1_tl_cb (ClutterTimeline *timeline,
gint frame_num,
VideoApp *app)
{
clutter_actor_rotate_y (app->vtexture,
frame_num * 12,
CLUTTER_STAGE_WIDTH()/2,
0);
}
int
main (int argc, char *argv[])
{
VideoApp *app = NULL;
ClutterActor *stage;
ClutterColor stage_color = { 0x00, 0x00, 0x00, 0x00 };
ClutterColor control_color1 = { 73, 74, 77, 0xee };
ClutterColor control_color2 = { 0xcc, 0xcc, 0xcc, 0xff };
GdkPixbuf *pixb;
gint x,y;
if (argc < 2)
g_error("%s <video file>", argv[0]);
clutter_init (&argc, &argv);
stage = clutter_stage_get_default ();
g_object_set (stage, "fullscreen", TRUE, NULL);
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
app = g_new0(VideoApp, 1);
app->vtexture = clutter_video_texture_new ();
if (app->vtexture == NULL)
{
g_error("failed to create vtexture");
}
/* Dont let the underlying pixbuf dictate size */
g_object_set (G_OBJECT(app->vtexture), "sync-size", FALSE, NULL);
/* Handle it ourselves so can scale up for fullscreen better */
g_signal_connect (CLUTTER_TEXTURE(app->vtexture),
"size-change",
G_CALLBACK (size_change), NULL);
/* Load up out video texture */
clutter_media_set_filename(CLUTTER_MEDIA(app->vtexture), argv[1]);
/* Create the control UI */
app->control = clutter_group_new ();
pixb = gdk_pixbuf_new_from_file ("vid-panel.png", NULL);
if (pixb == NULL)
g_error("Unable to load vid-panel.png");
app->control_bg = clutter_texture_new_from_pixbuf (pixb);
pixb = gdk_pixbuf_new_from_file("media-actions-start.png", NULL);
if (pixb == NULL)
g_error("Unable to load media-actions-start.png");
app->control_play = clutter_texture_new_from_pixbuf (pixb);
pixb = gdk_pixbuf_new_from_file("media-actions-pause.png", NULL);
if (pixb == NULL)
g_error("Unable to load media-actions-pause.png");
app->control_pause = clutter_texture_new_from_pixbuf (pixb);
app->control_seek1 = clutter_rectangle_new_with_color (&control_color1);
app->control_seek2 = clutter_rectangle_new_with_color (&control_color2);
app->control_seekbar = clutter_rectangle_new_with_color (&control_color1);
clutter_actor_set_opacity (app->control_seekbar, 0x99);
app->control_label
= clutter_label_new_with_text("Sans Bold 24",
g_path_get_basename(argv[1]));
clutter_label_set_color (CLUTTER_LABEL(app->control_label), &control_color1);
clutter_group_add_many (CLUTTER_GROUP (app->control),
app->control_bg,
app->control_play,
app->control_pause,
app->control_seek1,
app->control_seek2,
app->control_seekbar,
app->control_label,
NULL);
x = (CLUTTER_STAGE_WIDTH() - clutter_actor_get_width(app->control))/2;
y = CLUTTER_STAGE_HEIGHT() - (CLUTTER_STAGE_HEIGHT()/3);
clutter_actor_set_position (app->control, x, y);
clutter_actor_set_opacity (app->control, 0xee);
clutter_actor_set_position (app->control_play, 30, 30);
clutter_actor_set_position (app->control_pause, 30, 30);
clutter_actor_set_size (app->control_seek1, SEEK_W+10, SEEK_H+10);
clutter_actor_set_position (app->control_seek1, 200, 100);
clutter_actor_set_size (app->control_seek2, SEEK_W, SEEK_H);
clutter_actor_set_position (app->control_seek2, 205, 105);
clutter_actor_set_size (app->control_seekbar, 0, SEEK_H);
clutter_actor_set_position (app->control_seekbar, 205, 105);
clutter_actor_set_position (app->control_label, 200, 40);
/* Add control UI to stage */
clutter_group_add_many (CLUTTER_GROUP (stage),
app->vtexture, app->control, NULL);
/* hook up a time line for fading controls */
app->controls_tl = clutter_timeline_new (10, 30);
g_signal_connect (app->controls_tl, "new-frame",
G_CALLBACK (control_tl_cb), app);
g_signal_connect (app->controls_tl, "completed",
G_CALLBACK (control_tl_complete_cb), app);
app->effect1_tl = clutter_timeline_new (30, 90);
g_signal_connect (app->effect1_tl, "new-frame",
G_CALLBACK (effect1_tl_cb), app);
/* Hook up other events */
g_signal_connect (stage, "input-event",
G_CALLBACK (input_cb),
app);
g_signal_connect (CLUTTER_MEDIA(app->vtexture),
"notify::position",
G_CALLBACK (tick),
app);
clutter_media_set_playing (CLUTTER_MEDIA(app->vtexture), TRUE);
clutter_group_show_all (CLUTTER_GROUP (stage));
clutter_main();
return 0;
}