4 * An glib SMIL library.
6 * Authored By Koos Vriezen <koos.vriezen@gmail.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
26 * @short_description: text/img/brush/ref/audio/video media classes.
33 #include "laugh-marshal.h"
34 #include "laugh-media.h"
35 #include "laugh-layout.h"
39 #include <glib/gprintf.h>
40 #include <clutter/clutter-label.h>
41 #include <clutter/clutter-group.h>
42 #include <clutter/clutter-rectangle.h>
43 #include <clutter/clutter-texture.h>
44 #include <clutter-gst/clutter-gst.h>
46 #define LAUGH_MEDIA_GET_PRIVATE(obj) \
47 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), LAUGH_TYPE_MEDIA, LaughMediaPrivate))
56 LaughMediaTypeUnknown
= 0,
63 struct _LaughMediaPrivate
65 LaughRoleDisplay
*display_role
;
66 LaughRoleTiming
*timing_role
;
68 LaughSizeSetting size_setting
;
72 guint intrinsic_width
;
73 guint intrinsic_height
;
77 static gpointer laugh_media_parent_class
= ((void *)0);
78 static guint laugh_media_signals
[LAST_SIGNAL
] = { 0, };
79 static LaughRole laugh_media_media_role
;
81 static void laugh_media_finalize (GObject
*object
)
83 G_OBJECT_CLASS (laugh_media_parent_class
)->finalize (object
);
86 static void laugh_media_dispose (GObject
*object
)
88 LaughMedia
*self
= LAUGH_MEDIA(object
);
89 LaughRoleTiming
*role
= self
->priv
->timing_role
;
91 if (self
->priv
->display_role
) {
92 clutter_actor_destroy (self
->priv
->display_role
->actor
);
93 g_free (self
->priv
->display_role
);
96 switch (self
->priv
->type
) {
97 case LaughMediaTypeText
:
98 g_free (self
->priv
->data
);
100 case LaughMediaTypeImage
:
101 case LaughMediaTypeAudio
:
102 g_object_unref (self
->priv
->data
);
108 g_free (self
->priv
->uri
);
110 G_OBJECT_CLASS (laugh_media_parent_class
)->dispose (object
);
112 laugh_timing_role_delete (role
);
116 _laugh_media_init (LaughNode
*node
, LaughInitializer
*initializer
)
118 LaughMedia
*self
= (LaughMedia
*) node
;
120 LaughRoleTiming
*role
= self
->priv
->timing_role
;
121 LaughRoleTiming
*parent_timing_role
= initializer
->parent_segment
;
123 if (node
->attributes
)
124 g_hash_table_foreach (node
->attributes
, laugh_attributes_set
, node
);
126 if (self
->priv
->io
) {
127 initializer
->init_pending
= g_slist_append (initializer
->init_pending
,
129 g_signal_connect (G_OBJECT (node
), "initialized",
130 (GCallback
) initializer
->initialized
, initializer
);
133 laugh_timing_role_child_add (initializer
->parent_segment
, role
);
135 initializer
->parent_segment
= role
;
137 for (child
= node
->first_child
; child
; child
= child
->next_sibling
)
138 laugh_node_init (child
, initializer
);
140 initializer
->parent_segment
= parent_timing_role
;
143 void _lauch_media_mime_type (LaughIO
*io
, const gchar
*mime
, gpointer d
)
145 LaughMedia
*media
= LAUGH_MEDIA (d
);
146 LaughNode
*node
= (LaughNode
*) media
;
148 g_printf ("lauch_media_mime_type: %s\n", mime
);
149 if (!strncmp (mime
, "text/", 5)) {
150 media
->priv
->type
= LaughMediaTypeText
;
151 } else if (!strncmp (mime
, "image/", 6)) {
152 media
->priv
->type
= LaughMediaTypeImage
;
153 } else if (!strncmp (mime
, "audio/", 6)) {
154 media
->priv
->type
= LaughMediaTypeAudio
;
155 } else if (!strncmp (mime
, "video/", 6)) {
156 media
->priv
->type
= LaughMediaTypeVideo
;
157 } else if (!strcmp (mime
, "application/octet-stream")) {
159 media
->priv
->type
= LaughMediaTypeAudio
;
160 else if (LaughTagIdVideo
)
161 media
->priv
->type
= LaughMediaTypeVideo
;
163 if (LaughMediaTypeUnknown
== media
->priv
->type
)
164 g_printerr ("%s mimetype '%s' not (yet) supported\n",
165 laugh_node_get_attribute (node
, LaughAttrIdSrc
), mime
);
166 if (LaughMediaTypeImage
!= media
->priv
->type
&&
167 LaughMediaTypeText
!= media
->priv
->type
) {
168 if (LaughMediaTypeUnknown
!= media
->priv
->type
)
169 media
->priv
->uri
= g_strdup (laugh_io_get_uri (io
));
170 laugh_io_cancel (io
);
171 media
->priv
->io
= NULL
;
173 g_object_unref (G_OBJECT (io
));
175 laugh_node_base_emit_initialized (node
);
180 _lauch_media_completed (LaughIO
*io
, gsize sz
, gpointer data
, gpointer d
)
182 LaughMedia
*media
= LAUGH_MEDIA (d
);
183 LaughNode
*node
= (LaughNode
*) media
;
184 LaughMediaPrivate
*priv
= media
->priv
;
186 g_printf ("_lauch_media_completed %s: %u\n", laugh_io_get_uri (io
), sz
);
188 switch (priv
->type
) {
189 case LaughMediaTypeText
:
191 g_printf ("text: %s\n", (const char *)data
);
193 case LaughMediaTypeImage
: {
194 GdkPixbufLoader
*loader
= gdk_pixbuf_loader_new ();
195 if (gdk_pixbuf_loader_write (loader
,
196 (const guchar
*) data
, sz
, NULL
) &&
197 gdk_pixbuf_loader_close (loader
, NULL
)) {
198 GdkPixbuf
*pix
= gdk_pixbuf_loader_get_pixbuf (loader
);
199 g_printf ("image:\n");
202 priv
->intrinsic_width
= gdk_pixbuf_get_width (pix
);
203 priv
->intrinsic_height
= gdk_pixbuf_get_height (pix
);
205 g_object_unref (loader
);
209 case LaughMediaTypeAudio
:
211 case LaughMediaTypeVideo
:
212 /*TODO: determine sizes*/
219 g_object_unref (G_OBJECT (io
));
222 laugh_node_base_emit_initialized (node
);
225 static void _laugh_media_actor_position (LaughNode
*node
)
227 LaughMedia
*self
= (LaughMedia
*)node
;
228 LaughMediaPrivate
*priv
= self
->priv
;
229 LaughRoleDisplay
*region_display
;
231 float sx
, sy
, sw
, sh
;
235 (LaughRoleDisplay
*) laugh_node_role_get (priv
->region
, LaughRoleTypeDisplay
);
236 clutter_actor_get_size (region_display
->actor
, &pw
, &ph
);
237 laugh_size_setting_get (&priv
->size_setting
, pw
, ph
, &sx
, &sy
, &sw
, &sh
);
238 fit
= laugh_node_get_attribute(node
, LaughAttrIdFit
);
240 fit
= laugh_node_get_attribute (priv
->region
, LaughAttrIdFit
);
241 if (priv
->intrinsic_width
> 0 && priv
->intrinsic_height
> 0) {
242 if (!fit
|| !strcmp (fit
, "hidden") || !strcmp (fit
, "scroll")) {
243 sw
= priv
->intrinsic_width
;
244 sh
= priv
->intrinsic_height
;
245 } else if (!strcmp (fit
, "meet")) {
246 float iasp
= 1.0 * priv
->intrinsic_width
/ priv
->intrinsic_height
;
247 float rasp
= 1.0 * sw
/ sh
;
249 sh
= priv
->intrinsic_height
* sw
/ priv
->intrinsic_width
;
251 sw
= priv
->intrinsic_width
* sh
/ priv
->intrinsic_height
;
252 } else if (!strcmp (fit
, "slice")) {
253 float iasp
= 1.0 * priv
->intrinsic_width
/ priv
->intrinsic_height
;
254 float rasp
= 1.0 * sw
/ sh
;
256 sw
= priv
->intrinsic_width
* sh
/ priv
->intrinsic_height
;
258 sh
= priv
->intrinsic_height
* sw
/ priv
->intrinsic_width
;
261 clutter_actor_set_size (priv
->display_role
->actor
, sw
, sh
);
262 clutter_actor_set_position (priv
->display_role
->actor
, sx
, sy
);
265 static void _laugh_media_set_attribute (LaughNode
*node
,
266 LaughNodeAttributeId att
, const gchar
*value
, gpointer
*undo
)
268 const gchar
*val
= value
;
269 LaughMedia
*self
= (LaughMedia
*)node
;
270 LaughMediaPrivate
*priv
= self
->priv
;
272 laugh_node_base_set_attribute (node
, att
, val
, undo
);
275 val
= *(const gchar
**)undo
;
276 g_printf ("_laugh_media_set_attribute %s=%s\n", laugh_attribute_from_id (att
), val
);
278 if (laugh_timing_setting_set_attribute (priv
->timing_role
, att
, val
)) {
279 } else if (laugh_size_setting_set_attribute (&self
->priv
->size_setting
, att
, value
) ||
280 LaughAttrIdFit
== att
) {
281 if (priv
->region
&& priv
->display_role
)
282 _laugh_media_actor_position (node
);
287 priv
->io
= laugh_io_new (val
,
288 laugh_document_get_uri (node
->document
));
290 g_signal_connect (G_OBJECT (priv
->io
), "mime-type",
291 (GCallback
) _lauch_media_mime_type
, (gpointer
) node
);
292 g_signal_connect (G_OBJECT (priv
->io
), "completed",
293 (GCallback
) _lauch_media_completed
, (gpointer
) node
);
295 laugh_io_open (priv
->io
);
304 static void _laugh_media_region_destroyed (gpointer data
, GObject
*obj
)
306 LaughMediaPrivate
*priv
= (LaughMediaPrivate
*) data
;
310 g_free (priv
->display_role
);
311 priv
->display_role
= NULL
;
314 static void _lauch_media_actor_destroyed (ClutterActor
*actor
, gpointer data
)
316 LaughMedia
*self
= LAUGH_MEDIA(data
);
318 g_free (self
->priv
->display_role
);
319 self
->priv
->display_role
= NULL
;
322 static void _laugh_media_eos (ClutterMedia
*media
, LaughNode
*node
)
324 LaughMedia
*self
= (LaughMedia
*)node
;
325 LaughRoleTiming
*role
= self
->priv
->timing_role
;
326 g_printf ("playing stopped %d %d\n", node
->state
== LaughStateBegun
, node
->state
);
328 if (LaughStateBegun
== node
->state
&&
330 LaughTimingMedia
== role
->dur
->type
) {
331 laugh_timing_notify (role
, role
->dur
->handler_id
);
336 void _laugh_media_size_change (ClutterTexture
*tex
, gint w
, gint h
, LaughMedia
*self
)
338 g_printf ("size_change %d %d\n", w
, h
);
342 _lauch_media_actor_event (ClutterActor
*actor
, ClutterEvent
*event
, LaughMedia
*self
)
345 clutter_actor_transform_stage_point (actor
,
346 CLUTTER_UNITS_FROM_INT (event
->button
.x
),
347 CLUTTER_UNITS_FROM_INT (event
->button
.y
),
349 g_signal_emit (self
, laugh_media_signals
[ACTIVATED
],
350 0, CLUTTER_UNITS_TO_INT(x
), CLUTTER_UNITS_TO_INT(y
));
351 g_printf ("button_press %d %d\n", CLUTTER_UNITS_TO_INT(x
), CLUTTER_UNITS_TO_INT(y
));
356 static void _laugh_media_display_role_new (LaughNode
*node
)
358 LaughMedia
*self
= (LaughMedia
*)node
;
359 LaughRoleDisplay
*display_role
;
361 display_role
= g_new0 (LaughRoleDisplay
, 1);
362 display_role
->role
.type
= LaughRoleTypeDisplay
;
364 self
->priv
->display_role
= display_role
;
367 static void _laugh_media_start (LaughNode
*node
)
369 LaughMedia
*self
= (LaughMedia
*)node
;
370 LaughMediaPrivate
*priv
= self
->priv
;
372 ClutterMedia
*media
= NULL
;
373 const gchar
*region_id
= laugh_node_get_attribute(node
, LaughAttrIdRegion
);
376 g_printerr ("'%s' default region not implemented\n",
377 laugh_tag_from_id (node
->id
));
380 region
= laugh_document_get_element_by_id (node
->document
,
381 laugh_document_id_from_string (node
->document
, region_id
));
382 if (region
&& LAUGH_IS_LAYOUT(region
)) {
383 priv
->region
= region
;
385 g_printerr ("start '%s' region \"%s\" not found\n",
386 laugh_tag_from_id (node
->id
), region_id
);
389 g_object_weak_ref ((GObject
*) region
, _laugh_media_region_destroyed
, priv
);
391 switch (priv
->type
) {
392 case LaughMediaTypeText
:
394 _laugh_media_display_role_new (node
);
395 priv
->display_role
->actor
= clutter_label_new_with_text (
396 "sans 10", (const char *)priv
->data
);
399 case LaughMediaTypeImage
:
401 _laugh_media_display_role_new (node
);
402 priv
->display_role
->actor
= clutter_texture_new_from_pixbuf (
403 (GdkPixbuf
*)priv
->data
);
406 case LaughMediaTypeAudio
:
407 media
= (ClutterMedia
*) clutter_gst_audio_new ();
408 priv
->data
= (gpointer
) media
;
410 case LaughMediaTypeVideo
:
411 _laugh_media_display_role_new (node
);
412 priv
->display_role
->actor
= clutter_gst_video_texture_new ();
413 media
= (ClutterMedia
*) priv
->display_role
->actor
;
414 g_object_set (G_OBJECT(priv
->display_role
->actor
), "sync-size", FALSE
, NULL
);
415 g_signal_connect (CLUTTER_TEXTURE(priv
->display_role
->actor
),
417 G_CALLBACK (_laugh_media_size_change
), self
);
422 if (LaughMediaTypeAudio
== priv
->type
|| LaughMediaTypeVideo
== priv
->type
) {
423 LaughRoleTiming
*role
= self
->priv
->timing_role
;
425 role
->dur
= laugh_timing_new ();
426 role
->dur
->type
= LaughTimingMedia
;
428 role
->dur
->handler_id
= g_signal_connect (G_OBJECT (media
),
429 "eos", (GCallback
) _laugh_media_eos
, self
);
431 g_printf ("playing gst %s\n", priv
->uri
);
432 clutter_media_set_uri (media
, priv
->uri
);
433 clutter_media_set_playing (media
, TRUE
);
435 if (priv
->display_role
) {
436 LaughRoleDisplayGroup
*group
= (LaughRoleDisplayGroup
*)
437 laugh_node_role_get (priv
->region
, LaughRoleTypeDisplayGroup
);
438 clutter_container_add_actor (CLUTTER_CONTAINER (group
->actor
), priv
->display_role
->actor
);
439 _laugh_media_actor_position (node
);
440 CLUTTER_ACTOR_SET_FLAGS (priv
->display_role
->actor
, CLUTTER_ACTOR_REACTIVE
);
441 clutter_actor_show (priv
->display_role
->actor
);
442 g_signal_connect (G_OBJECT (priv
->display_role
->actor
), "button-press-event",
443 (GCallback
) _lauch_media_actor_event
, self
);
444 g_signal_connect (G_OBJECT (priv
->display_role
->actor
), "destroy",
445 (GCallback
) _lauch_media_actor_destroyed
, self
);
449 static void _laugh_media_stop (LaughNode
*node
)
451 LaughMedia
*self
= (LaughMedia
*)node
;
452 LaughMediaPrivate
*priv
= self
->priv
;
455 case LaughTagIdAudio
:
456 case LaughTagIdVideo
:
458 ClutterMedia
*m
= CLUTTER_MEDIA (priv
->data
);
459 clutter_media_set_playing (m
, FALSE
);
460 g_object_unref (priv
->data
);
469 g_object_weak_unref ((GObject
*) priv
->region
,
470 _laugh_media_region_destroyed
, priv
);
474 if (priv
->display_role
) {
475 clutter_actor_destroy (priv
->display_role
->actor
);
476 g_free (priv
->display_role
);
477 priv
->display_role
= NULL
;
481 static LaughRole
*_laugh_media_role (LaughNode
*node
, LaughRoleType type
)
483 LaughMedia
*self
= (LaughMedia
*) node
;
486 case LaughRoleTypeDisplay
:
487 return (LaughRole
*) self
->priv
->display_role
;
488 case LaughRoleTypeMedia
:
489 return &laugh_media_media_role
;
490 case LaughRoleTypeTiming
:
491 return (LaughRole
*) self
->priv
->timing_role
;
497 static void laugh_media_class_init (LaughMediaClass
*klass
)
499 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
500 LaughNodeClass
*node_class
= (LaughNodeClass
*) klass
;
502 laugh_media_parent_class
= g_type_class_peek_parent (klass
);
504 gobject_class
->finalize
= laugh_media_finalize
;
505 gobject_class
->dispose
= laugh_media_dispose
;
506 node_class
->init
= _laugh_media_init
;
507 node_class
->set_attribute
= _laugh_media_set_attribute
;
508 node_class
->start
= _laugh_media_start
;
509 node_class
->stop
= _laugh_media_stop
;
510 node_class
->role
= _laugh_media_role
;
512 laugh_media_signals
[ACTIVATED
] =
513 g_signal_new ("activated",
514 G_TYPE_FROM_CLASS (gobject_class
),
516 G_STRUCT_OFFSET (LaughMediaClass
, activated
),
518 laugh_marshal_VOID__INT_INT
,
519 G_TYPE_NONE
, 2, G_TYPE_INT
, G_TYPE_INT
);
521 g_type_class_add_private (gobject_class
, sizeof (LaughMediaPrivate
));
525 void laugh_media_instance_init (GTypeInstance
*instance
, gpointer g_class
)
527 LaughMedia
*self
= (LaughMedia
*)instance
;
529 self
->priv
= LAUGH_MEDIA_GET_PRIVATE (self
);
531 self
->priv
->timing_role
= laugh_timing_role_new ((LaughNode
*)self
);
534 GType
laugh_media_get_type (void)
536 static GType type
= 0;
538 static const GTypeInfo info
= {
539 sizeof (LaughMediaClass
),
540 NULL
, /* base_init */
541 NULL
, /* base_finalize */
542 (GClassInitFunc
) laugh_media_class_init
, /* class_init */
543 NULL
, /* class_finalize */
544 NULL
, /* class_data */
547 laugh_media_instance_init
/* instance_init */
549 type
= g_type_register_static (LAUGH_TYPE_NODE
,
552 laugh_media_media_role
.type
= LaughRoleTypeMedia
;
557 LaughNode
*laugh_media_new (LaughDocument
*doc
, LaughNodeTagId id
,
558 GHashTable
*attributes
)
560 LaughNode
*n
= (LaughNode
*)g_object_new(LAUGH_TYPE_MEDIA
, NULL
);
562 laugh_node_base_construct (doc
, n
, id
, attributes
);