1 /*
2  * Clutter-GStreamer.
3  *
4  * GStreamer integration library for Clutter.
5  *
6  * clutter-gst-video-texture.c - ClutterTexture using GStreamer to display a
7  *                               video stream.
8  *
9  * Authored By Matthew Allum     <mallum@openedhand.com>
10  *             Damien Lespiau    <damien.lespiau@intel.com>
11  *             Lionel Landwerlin <lionel.g.landwerlin@linux.intel.com>
12  *
13  * Copyright (C) 2006 OpenedHand
14  * Copyright (C) 2010, 2011 Intel Corporation
15  *
16  * This library is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU Lesser General Public
18  * License as published by the Free Software Foundation; either
19  * version 2 of the License, or (at your option) any later version.
20  *
21  * This library is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24  * Lesser General Public License for more details.
25  *
26  * You should have received a copy of the GNU Lesser General Public
27  * License along with this library; if not, write to the
28  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
29  * Boston, MA 02111-1307, USA.
30  */
31 
32 /**
33  * SECTION:clutter-gst-video-texture
34  * @short_description: Actor for playback of video files.
35  *
36  * #ClutterGstVideoTexture is a #ClutterTexture that plays video files.
37  */
38 
39 #ifdef HAVE_CONFIG_H
40 #include "config.h"
41 #endif
42 
43 #include <string.h>
44 
45 #include <glib.h>
46 #include <gio/gio.h>
47 #include <gst/base/gstbasesink.h>
48 #include <gst/video/video.h>
49 
50 #include "clutter-gst-debug.h"
51 #include "clutter-gst-enum-types.h"
52 #include "clutter-gst-marshal.h"
53 #include "clutter-gst-player.h"
54 #include "clutter-gst-private.h"
55 #include "clutter-gst-video-texture.h"
56 
57 struct _ClutterGstVideoTexturePrivate
58 {
59   /* width / height (in pixels) of the frame data before applying the pixel
60    * aspect ratio */
61   gint buffer_width;
62   gint buffer_height;
63 
64   /* Pixel aspect ration is par_n / par_d. this is set by the sink */
65   guint par_n, par_d;
66 
67   /* natural width / height (in pixels) of the texture (after par applied) */
68   guint texture_width;
69   guint texture_height;
70 
71   CoglHandle idle_material;
72   CoglColor idle_color_unpre;
73 };
74 
75 enum {
76   PROP_0 = 32,  /* Avoid overlap with player properties */
77 
78   PROP_IDLE_MATERIAL,
79   PROP_PAR
80 };
81 
82 static void clutter_gst_video_texture_media_init (ClutterMediaIface *iface);
83 static void clutter_gst_video_texture_player_init (ClutterGstPlayerIface *iface);
84 
85 G_DEFINE_TYPE_WITH_CODE (ClutterGstVideoTexture,
86                          clutter_gst_video_texture,
87                          CLUTTER_TYPE_TEXTURE,
88                          G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_MEDIA,
89                                                 clutter_gst_video_texture_media_init)
90                          G_IMPLEMENT_INTERFACE (CLUTTER_GST_TYPE_PLAYER,
91                                                 clutter_gst_video_texture_player_init));
92 
93 /* Clutter 1.4 has this symbol, we don't want to depend on 1.4 just for that
94  * just yet */
95 static void
_cogl_color_unpremultiply(CoglColor * color)96 _cogl_color_unpremultiply (CoglColor *color)
97 {
98   gfloat alpha;
99 
100   alpha = cogl_color_get_alpha (color);
101 
102   if (alpha != 0)
103     {
104       gfloat red, green, blue;
105 
106       red = cogl_color_get_red (color);
107       green = cogl_color_get_green (color);
108       blue = cogl_color_get_blue (color);
109 
110       red = red / alpha;
111       green = green / alpha;
112       blue = blue / alpha;
113 
114       cogl_color_set_from_4f (color, red, green, blue, alpha);
115     }
116 }
117 
118 /* Clutter 1.4 has this symbol, we don't want to depend on 1.4 just for that
119  * just yet */
120 static void
_cogl_color_set_alpha_byte(CoglColor * color,unsigned char alpha)121 _cogl_color_set_alpha_byte (CoglColor     *color,
122                             unsigned char  alpha)
123 {
124   unsigned char red, green, blue;
125 
126   red = cogl_color_get_red_byte (color);
127   green = cogl_color_get_green_byte (color);
128   blue = cogl_color_get_blue_byte (color);
129 
130   cogl_color_set_from_4ub (color, red, green, blue, alpha);
131 }
132 
133 static void
gen_texcoords_and_draw_cogl_rectangle(ClutterActor * self)134 gen_texcoords_and_draw_cogl_rectangle (ClutterActor *self)
135 {
136   ClutterActorBox box;
137 
138   clutter_actor_get_allocation_box (self, &box);
139 
140   cogl_rectangle_with_texture_coords (0, 0,
141                                       box.x2 - box.x1,
142                                       box.y2 - box.y1,
143                                       0, 0, 1.0, 1.0);
144 }
145 
146 static void
create_black_idle_material(ClutterGstVideoTexture * video_texture)147 create_black_idle_material (ClutterGstVideoTexture *video_texture)
148 {
149   ClutterGstVideoTexturePrivate *priv = video_texture->priv;
150 
151   priv->idle_material = cogl_material_new ();
152   cogl_color_set_from_4ub (&priv->idle_color_unpre, 0, 0, 0, 0xff);
153   cogl_material_set_color (priv->idle_material, &priv->idle_color_unpre);
154 }
155 
156 static void
clutter_gst_video_texture_media_init(ClutterMediaIface * iface)157 clutter_gst_video_texture_media_init (ClutterMediaIface *iface)
158 {
159 }
160 
161 static void
clutter_gst_video_texture_player_init(ClutterGstPlayerIface * iface)162 clutter_gst_video_texture_player_init (ClutterGstPlayerIface *iface)
163 {
164 }
165 
166 static void
clutter_gst_video_texture_size_change(ClutterTexture * texture,gint width,gint height)167 clutter_gst_video_texture_size_change (ClutterTexture *texture,
168                                        gint            width,
169                                        gint            height)
170 {
171   ClutterGstVideoTexture *video_texture = CLUTTER_GST_VIDEO_TEXTURE (texture);
172   ClutterGstVideoTexturePrivate *priv = video_texture->priv;
173   gboolean changed;
174 
175   /* we are being told the actual (as in number of pixels in the buffers)
176    * frame size. Store the values to be used in preferred_width/height() */
177   changed = (priv->buffer_width != width) || (priv->buffer_height != height);
178   priv->buffer_width = width;
179   priv->buffer_height = height;
180 
181   if (changed)
182     {
183       /* reset the computed texture dimensions if the underlying frames have
184        * changed size */
185       CLUTTER_GST_NOTE (ASPECT_RATIO, "frame size has been updated to %dx%d",
186                         width, height);
187 
188       priv->texture_width = priv->texture_height = 0;
189 
190       /* queue a relayout to ask containers/layout manager to ask for
191        * the preferred size again */
192       clutter_actor_queue_relayout (CLUTTER_ACTOR (texture));
193     }
194 }
195 
196 /*
197  * Clutter actor implementation
198  */
199 
200 static void
clutter_gst_video_texture_get_natural_size(ClutterGstVideoTexture * texture,gfloat * width,gfloat * height)201 clutter_gst_video_texture_get_natural_size (ClutterGstVideoTexture *texture,
202                                             gfloat                 *width,
203                                             gfloat                 *height)
204 {
205   ClutterGstVideoTexturePrivate *priv = texture->priv;
206   guint dar_n, dar_d;
207   gboolean ret;
208 
209   /* we cache texture_width and texture_height */
210 
211   if (G_UNLIKELY (priv->buffer_width == 0 || priv->buffer_height == 0))
212     {
213       /* we don't know the size of the frames yet default to 0,0 */
214       priv->texture_width = 0;
215       priv->texture_height = 0;
216     }
217   else if (G_UNLIKELY (priv->texture_width == 0 || priv->texture_height == 0))
218     {
219       CLUTTER_GST_NOTE (ASPECT_RATIO, "frame is %dx%d with par %d/%d",
220                         priv->buffer_width, priv->buffer_height,
221                         priv->par_n, priv->par_d);
222 
223       ret = gst_video_calculate_display_ratio (&dar_n, &dar_d,
224                                                priv->buffer_width,
225                                                priv->buffer_height,
226                                                priv->par_n, priv->par_d,
227                                                1, 1);
228       if (ret == FALSE)
229         dar_n = dar_d = 1;
230 
231       if (priv->buffer_height % dar_d == 0)
232         {
233           priv->texture_width = gst_util_uint64_scale (priv->buffer_height,
234                                                        dar_n, dar_d);
235           priv->texture_height = priv->buffer_height;
236         }
237       else if (priv->buffer_width % dar_n == 0)
238         {
239           priv->texture_width = priv->buffer_width;
240           priv->texture_height = gst_util_uint64_scale (priv->buffer_width,
241                                                         dar_d, dar_n);
242 
243         }
244       else
245         {
246           priv->texture_width = gst_util_uint64_scale (priv->buffer_height,
247                                                        dar_n, dar_d);
248           priv->texture_height = priv->buffer_height;
249         }
250 
251       CLUTTER_GST_NOTE (ASPECT_RATIO,
252                         "final size is %dx%d (calculated par is %d/%d)",
253                         priv->texture_width, priv->texture_height,
254                         dar_n, dar_d);
255     }
256 
257   if (width)
258     *width = (gfloat)priv->texture_width;
259 
260   if (height)
261     *height = (gfloat)priv->texture_height;
262 }
263 
264 static void
clutter_gst_video_texture_get_preferred_width(ClutterActor * self,gfloat for_height,gfloat * min_width_p,gfloat * natural_width_p)265 clutter_gst_video_texture_get_preferred_width (ClutterActor *self,
266                                                gfloat        for_height,
267                                                gfloat       *min_width_p,
268                                                gfloat       *natural_width_p)
269 {
270   ClutterGstVideoTexture *texture = CLUTTER_GST_VIDEO_TEXTURE (self);
271   ClutterGstVideoTexturePrivate *priv = texture->priv;
272   gboolean sync_size, keep_aspect_ratio;
273   gfloat natural_width, natural_height;
274 
275   /* Min request is always 0 since we can scale down or clip */
276   if (min_width_p)
277     *min_width_p = 0;
278 
279   sync_size = clutter_texture_get_sync_size (CLUTTER_TEXTURE (self));
280   keep_aspect_ratio =
281     clutter_texture_get_keep_aspect_ratio (CLUTTER_TEXTURE (self));
282 
283   clutter_gst_video_texture_get_natural_size (texture,
284                                               &natural_width,
285                                               &natural_height);
286 
287   if (sync_size)
288     {
289       if (natural_width_p)
290         {
291           if (!keep_aspect_ratio ||
292               for_height < 0 ||
293               priv->buffer_height <= 0)
294             {
295               *natural_width_p = natural_width;
296             }
297           else
298             {
299               /* Set the natural width so as to preserve the aspect ratio */
300               gfloat ratio =  natural_width /  natural_height;
301 
302               *natural_width_p = ratio * for_height;
303             }
304         }
305     }
306   else
307     {
308       if (natural_width_p)
309         *natural_width_p = 0;
310     }
311 }
312 
313 static void
clutter_gst_video_texture_get_preferred_height(ClutterActor * self,gfloat for_width,gfloat * min_height_p,gfloat * natural_height_p)314 clutter_gst_video_texture_get_preferred_height (ClutterActor *self,
315                                                 gfloat        for_width,
316                                                 gfloat       *min_height_p,
317                                                 gfloat       *natural_height_p)
318 {
319   ClutterGstVideoTexture *texture = CLUTTER_GST_VIDEO_TEXTURE (self);
320   ClutterGstVideoTexturePrivate *priv = texture->priv;
321   gboolean sync_size, keep_aspect_ratio;
322   gfloat natural_width, natural_height;
323 
324   /* Min request is always 0 since we can scale down or clip */
325   if (min_height_p)
326     *min_height_p = 0;
327 
328   sync_size = clutter_texture_get_sync_size (CLUTTER_TEXTURE (self));
329   keep_aspect_ratio =
330     clutter_texture_get_keep_aspect_ratio (CLUTTER_TEXTURE (self));
331 
332   clutter_gst_video_texture_get_natural_size (texture,
333                                               &natural_width,
334                                               &natural_height);
335 
336   if (sync_size)
337     {
338       if (natural_height_p)
339         {
340           if (!keep_aspect_ratio ||
341               for_width < 0 ||
342               priv->buffer_width <= 0)
343             {
344               *natural_height_p = natural_height;
345             }
346           else
347             {
348               /* Set the natural height so as to preserve the aspect ratio */
349               gfloat ratio = natural_height / natural_width;
350 
351               *natural_height_p = ratio * for_width;
352             }
353         }
354     }
355   else
356     {
357       if (natural_height_p)
358         *natural_height_p = 0;
359     }
360 }
361 
362 /*
363  * ClutterTexture unconditionnaly sets the material color to:
364  *    (opacity,opacity,opacity,opacity)
365  * so we can't set a black material to the texture. Let's override paint()
366  * for now.
367  */
368 static void
clutter_gst_video_texture_paint(ClutterActor * actor)369 clutter_gst_video_texture_paint (ClutterActor *actor)
370 {
371   ClutterGstVideoTexture *video_texture = (ClutterGstVideoTexture *) actor;
372   ClutterGstVideoTexturePrivate *priv = video_texture->priv;
373   ClutterActorClass *actor_class;
374   gboolean is_idle;
375 
376   is_idle = clutter_gst_player_get_idle (CLUTTER_GST_PLAYER (video_texture));
377   if (G_UNLIKELY (is_idle))
378     {
379       CoglColor *color;
380       gfloat alpha;
381 
382       /* blend the alpha of the idle material with the actor's opacity */
383       color = cogl_color_copy (&priv->idle_color_unpre);
384       alpha = clutter_actor_get_paint_opacity (actor) *
385               cogl_color_get_alpha_byte (color) / 0xff;
386       _cogl_color_set_alpha_byte (color, alpha);
387       cogl_color_premultiply (color);
388       cogl_material_set_color (priv->idle_material, color);
389 
390       cogl_set_source (priv->idle_material);
391 
392       /* draw */
393       gen_texcoords_and_draw_cogl_rectangle (actor);
394     }
395   else
396     {
397       /* when not idle, just chain up to ClutterTexture::paint() */
398       actor_class =
399         CLUTTER_ACTOR_CLASS (clutter_gst_video_texture_parent_class);
400       actor_class->paint (actor);
401     }
402 
403 }
404 
405 /*
406  * GObject implementation
407  */
408 
409 static void
clutter_gst_video_texture_dispose(GObject * object)410 clutter_gst_video_texture_dispose (GObject *object)
411 {
412   ClutterGstVideoTexture *self = CLUTTER_GST_VIDEO_TEXTURE (object);
413 
414   clutter_gst_player_deinit (CLUTTER_GST_PLAYER (self));
415 
416   G_OBJECT_CLASS (clutter_gst_video_texture_parent_class)->dispose (object);
417 }
418 
419 static void
clutter_gst_video_texture_finalize(GObject * object)420 clutter_gst_video_texture_finalize (GObject *object)
421 {
422   ClutterGstVideoTexture        *self;
423   ClutterGstVideoTexturePrivate *priv;
424 
425   self = CLUTTER_GST_VIDEO_TEXTURE (object);
426   priv = self->priv;
427 
428   if (priv->idle_material != COGL_INVALID_HANDLE)
429     cogl_handle_unref (priv->idle_material);
430 
431   G_OBJECT_CLASS (clutter_gst_video_texture_parent_class)->finalize (object);
432 }
433 
434 static void
clutter_gst_video_texture_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)435 clutter_gst_video_texture_set_property (GObject      *object,
436 				        guint         property_id,
437 				        const GValue *value,
438 				        GParamSpec   *pspec)
439 {
440   ClutterGstVideoTexture *video_texture = CLUTTER_GST_VIDEO_TEXTURE (object);
441   ClutterGstVideoTexturePrivate *priv = video_texture->priv;
442 
443   switch (property_id)
444     {
445     case PROP_IDLE_MATERIAL:
446       clutter_gst_video_texture_set_idle_material (video_texture,
447                                                    g_value_get_boxed (value));
448       break;
449     case PROP_PAR:
450       priv->par_n = gst_value_get_fraction_numerator (value);
451       priv->par_d = gst_value_get_fraction_denominator (value);
452       break;
453 
454     default:
455       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
456     }
457 }
458 
459 static void
clutter_gst_video_texture_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)460 clutter_gst_video_texture_get_property (GObject    *object,
461 				        guint       property_id,
462 				        GValue     *value,
463 				        GParamSpec *pspec)
464 {
465   ClutterGstVideoTexture *video_texture;
466   ClutterGstVideoTexturePrivate *priv;
467 
468   video_texture = CLUTTER_GST_VIDEO_TEXTURE (object);
469   priv = video_texture->priv;
470 
471   switch (property_id)
472     {
473     case PROP_IDLE_MATERIAL:
474       g_value_set_boxed (value, priv->idle_material);
475       break;
476     case PROP_PAR:
477       gst_value_set_fraction (value, priv->par_n, priv->par_d);
478       break;
479 
480     default:
481       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
482     }
483 }
484 
485 static void
clutter_gst_video_texture_class_init(ClutterGstVideoTextureClass * klass)486 clutter_gst_video_texture_class_init (ClutterGstVideoTextureClass *klass)
487 {
488   GObjectClass *object_class = G_OBJECT_CLASS (klass);
489   ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
490   ClutterTextureClass *texture_class = CLUTTER_TEXTURE_CLASS (klass);
491   GParamSpec *pspec;
492 
493   g_type_class_add_private (klass, sizeof (ClutterGstVideoTexturePrivate));
494 
495   object_class->dispose      = clutter_gst_video_texture_dispose;
496   object_class->finalize     = clutter_gst_video_texture_finalize;
497   object_class->set_property = clutter_gst_video_texture_set_property;
498   object_class->get_property = clutter_gst_video_texture_get_property;
499 
500   actor_class->paint = clutter_gst_video_texture_paint;
501   actor_class->get_preferred_width =
502     clutter_gst_video_texture_get_preferred_width;
503   actor_class->get_preferred_height =
504     clutter_gst_video_texture_get_preferred_height;
505 
506   texture_class->size_change = clutter_gst_video_texture_size_change;
507 
508   pspec = g_param_spec_boxed ("idle-material",
509                               "Idle material",
510                               "Material to use for drawing when not playing",
511                               COGL_TYPE_HANDLE,
512                               CLUTTER_GST_PARAM_READWRITE);
513   g_object_class_install_property (object_class, PROP_IDLE_MATERIAL, pspec);
514 
515   pspec = gst_param_spec_fraction ("pixel-aspect-ratio",
516                                    "Pixel Aspect Ratio",
517                                    "Pixel aspect ratio of incoming frames",
518                                    1, 100, 100, 1, 1, 1,
519                                    CLUTTER_GST_PARAM_READWRITE);
520   g_object_class_install_property (object_class, PROP_PAR, pspec);
521 
522 
523   clutter_gst_player_class_init (object_class);
524 }
525 
526 static void
idle_cb(ClutterGstVideoTexture * video_texture,GParamSpec * pspec,gpointer data)527 idle_cb (ClutterGstVideoTexture *video_texture,
528          GParamSpec             *pspec,
529          gpointer                data)
530 {
531   /* restore the idle material so we don't just display the last frame */
532   clutter_actor_queue_redraw (CLUTTER_ACTOR (video_texture));
533 }
534 
535 static void
on_autocluttersink_element_added(GstBin * bin,GstElement * element,ClutterGstVideoTexture * data)536 on_autocluttersink_element_added (GstBin                 *bin,
537 				  GstElement             *element,
538 				  ClutterGstVideoTexture *data)
539 {
540   if (GST_IS_BASE_SINK (element))
541     g_object_set (G_OBJECT (element), "qos", TRUE, NULL);
542 }
543 
544 static gboolean
setup_pipeline(ClutterGstVideoTexture * video_texture)545 setup_pipeline (ClutterGstVideoTexture *video_texture)
546 {
547   GstElement *pipeline, *video_sink;
548 
549   pipeline =
550     clutter_gst_player_get_pipeline (CLUTTER_GST_PLAYER (video_texture));
551   if (!pipeline)
552     {
553       g_critical ("Unable to get playbin2 element");
554       return FALSE;
555     }
556 
557   video_sink = gst_element_factory_make ("cluttersink", NULL);
558 
559   g_object_set (G_OBJECT (video_sink),
560                 "texture", CLUTTER_TEXTURE (video_texture),
561 		NULL);
562   g_object_set (G_OBJECT (pipeline),
563                 "video-sink", video_sink,
564                 "subtitle-font-desc", "Sans 16",
565                 NULL);
566 
567   return TRUE;
568 }
569 
570 static void
clutter_gst_video_texture_init(ClutterGstVideoTexture * video_texture)571 clutter_gst_video_texture_init (ClutterGstVideoTexture *video_texture)
572 {
573   ClutterGstVideoTexturePrivate *priv;
574 
575   video_texture->priv = priv =
576     G_TYPE_INSTANCE_GET_PRIVATE (video_texture,
577                                  CLUTTER_GST_TYPE_VIDEO_TEXTURE,
578                                  ClutterGstVideoTexturePrivate);
579 
580   if (!clutter_gst_player_init (CLUTTER_GST_PLAYER (video_texture)))
581     {
582       g_warning ("Failed to initiate suitable playback pipeline.");
583       return;
584     }
585 
586   if (!setup_pipeline (video_texture))
587     {
588       g_warning ("Failed to initiate suitable sinks for pipeline.");
589       return;
590     }
591 
592   create_black_idle_material (video_texture);
593 
594   priv->par_n = priv->par_d = 1;
595 
596   g_signal_connect (video_texture, "notify::idle",
597                     G_CALLBACK (idle_cb),
598                     NULL);
599 }
600 
601 /*
602  * Private symbols
603  */
604 
605 /**
606  * clutter_gst_video_texture_new:
607  *
608  * Creates a video texture.
609  *
610  * <note>This function has to be called from Clutter's main thread. While
611  * GStreamer will spawn threads to do its work, we want all the GL calls to
612  * happen in the same thread. Clutter-gst knows which thread it is by
613  * assuming this constructor is called from the Clutter thread.</note>
614  *
615  * Return value: the newly created video texture actor
616  */
617 ClutterActor*
clutter_gst_video_texture_new(void)618 clutter_gst_video_texture_new (void)
619 {
620   return g_object_new (CLUTTER_GST_TYPE_VIDEO_TEXTURE,
621                        "disable-slicing", TRUE,
622                        NULL);
623 }
624 
625 /**
626  * clutter_gst_video_texture_get_pipeline:
627  * @texture: a #ClutterGstVideoTexture
628  *
629  * Retrieves the #GstPipeline used by the @texture, for direct use with
630  * GStreamer API.
631  *
632  * Return value: (transfer none): the pipeline element used by the video texture
633  */
634 GstElement *
clutter_gst_video_texture_get_pipeline(ClutterGstVideoTexture * texture)635 clutter_gst_video_texture_get_pipeline (ClutterGstVideoTexture *texture)
636 {
637   return clutter_gst_player_get_pipeline (CLUTTER_GST_PLAYER (texture));
638 }
639 
640 /**
641  * clutter_gst_video_texture_get_idle_material:
642  * @texture: a #ClutterGstVideoTexture
643  *
644  * Retrieves the material used to draw when no media is being played.
645  *
646  * Return value: (transfer none): the #CoglHandle of the idle material
647  *
648  * Since: 1.2
649  */
650 CoglHandle
clutter_gst_video_texture_get_idle_material(ClutterGstVideoTexture * texture)651 clutter_gst_video_texture_get_idle_material (ClutterGstVideoTexture *texture)
652 {
653   g_return_val_if_fail (CLUTTER_GST_IS_VIDEO_TEXTURE (texture),
654                         COGL_INVALID_HANDLE);
655 
656   return texture->priv->idle_material;
657 }
658 /**
659  * clutter_gst_video_texture_set_idle_material:
660  * @texture: a #ClutterGstVideoTexture
661  * @material: the handle of a Cogl material
662  *
663  * Sets a material to use to draw when no media is being played. The
664  * #ClutterGstVideoTexture holds a reference of the @material.
665  *
666  * The default idle material will paint the #ClutterGstVideoTexture in black.
667  * If %COGL_INVALID_HANDLE is given as @material to this function, this
668  * default idle material will be used.
669  *
670  * Since: 1.2
671  */
672 void
clutter_gst_video_texture_set_idle_material(ClutterGstVideoTexture * texture,CoglHandle material)673 clutter_gst_video_texture_set_idle_material (ClutterGstVideoTexture *texture,
674                                              CoglHandle              material)
675 {
676   ClutterGstVideoTexturePrivate *priv;
677 
678   g_return_if_fail (CLUTTER_GST_IS_VIDEO_TEXTURE (texture));
679 
680   priv = texture->priv;
681   /* priv->idle_material always has a valid material */
682   cogl_handle_unref (priv->idle_material);
683 
684   if (material != COGL_INVALID_HANDLE)
685     {
686       priv->idle_material = cogl_handle_ref (material);
687       cogl_material_get_color (material, &priv->idle_color_unpre);
688       _cogl_color_unpremultiply (&priv->idle_color_unpre);
689     }
690   else
691     {
692       create_black_idle_material (texture);
693     }
694 
695   g_object_notify (G_OBJECT (texture), "idle-material");
696 }
697 
698 /**
699  * clutter_gst_video_texture_get_user_agent:
700  * @texture: a #ClutterGstVideoTexture
701  *
702  * Retrieves the user agent used when streaming.
703  *
704  * Return value: the user agent used. The returned string has to be freed with
705  * g_free()
706  *
707  * Since: 1.2
708  */
709 gchar *
clutter_gst_video_texture_get_user_agent(ClutterGstVideoTexture * texture)710 clutter_gst_video_texture_get_user_agent (ClutterGstVideoTexture *texture)
711 {
712   return clutter_gst_player_get_user_agent (CLUTTER_GST_PLAYER (texture));
713 }
714 
715 /**
716  * clutter_gst_video_texture_set_user_agent:
717  * @texture: a #ClutterGstVideoTexture
718  * @user_agent: the user agent
719  *
720  * Sets the user agent to use when streaming.
721  *
722  * When streaming content, you might want to set a custom user agent, eg. to
723  * promote your software, make it appear in statistics or because the server
724  * requires a special user agent you want to impersonate.
725  *
726  * Since: 1.2
727  */
728 void
clutter_gst_video_texture_set_user_agent(ClutterGstVideoTexture * texture,const gchar * user_agent)729 clutter_gst_video_texture_set_user_agent (ClutterGstVideoTexture *texture,
730                                           const gchar *           user_agent)
731 {
732   clutter_gst_player_set_user_agent (CLUTTER_GST_PLAYER (texture),
733                                      user_agent);
734 }
735 
736 /**
737  * clutter_gst_video_texture_get_seek_flags:
738  * @texture: a #ClutterGstVideoTexture
739  *
740  * Get the current value of the seek-flags property.
741  *
742  * Return value: a combination of #ClutterGstSeekFlags
743  *
744  * Since: 1.4
745  */
746 ClutterGstSeekFlags
clutter_gst_video_texture_get_seek_flags(ClutterGstVideoTexture * texture)747 clutter_gst_video_texture_get_seek_flags (ClutterGstVideoTexture *texture)
748 {
749   return clutter_gst_player_get_seek_flags (CLUTTER_GST_PLAYER (texture));
750 }
751 
752 /**
753  * clutter_gst_video_texture_set_seek_flags:
754  * @texture: a #ClutterGstVideoTexture
755  * @flags: a combination of #ClutterGstSeekFlags
756  *
757  * Seeking can be done with several trade-offs. Clutter-gst defaults
758  * to %CLUTTER_GST_SEEK_FLAG_NONE.
759  *
760  * Since: 1.4
761  */
762 void
clutter_gst_video_texture_set_seek_flags(ClutterGstVideoTexture * texture,ClutterGstSeekFlags flags)763 clutter_gst_video_texture_set_seek_flags (ClutterGstVideoTexture *texture,
764                                           ClutterGstSeekFlags     flags)
765 {
766   clutter_gst_player_set_seek_flags (CLUTTER_GST_PLAYER (texture), flags);
767 }
768 
769 /**
770  * clutter_gst_video_texture_get_buffering_mode:
771  * @texture: a #ClutterGstVideoTexture
772  *
773  * Return value: a #ClutterGstBufferingMode
774  *
775  * Since: 1.4
776  */
777 ClutterGstBufferingMode
clutter_gst_video_texture_get_buffering_mode(ClutterGstVideoTexture * texture)778 clutter_gst_video_texture_get_buffering_mode (ClutterGstVideoTexture *texture)
779 {
780   return clutter_gst_player_get_buffering_mode (CLUTTER_GST_PLAYER (texture));
781 }
782 
783 /**
784  * clutter_gst_video_texture_set_buffering_mode:
785  * @texture: a #ClutterGstVideoTexture
786  * @mode: a #ClutterGstBufferingMode
787  *
788  * Since: 1.4
789  */
790 void
clutter_gst_video_texture_set_buffering_mode(ClutterGstVideoTexture * texture,ClutterGstBufferingMode mode)791 clutter_gst_video_texture_set_buffering_mode (ClutterGstVideoTexture *texture,
792                                               ClutterGstBufferingMode mode)
793 {
794   clutter_gst_player_set_buffering_mode (CLUTTER_GST_PLAYER (texture), mode);
795 }
796 
797 /**
798  * clutter_gst_video_texture_get_audio_streams:
799  * @texture: a #ClutterGstVideoTexture
800  *
801  * Get the list of audio streams of the current media.
802  *
803  * Return value: (transfer none) (element-type Gst.TagList): a list of
804  * #GstTagList describing the available audio streams
805  *
806  * Since: 1.4
807  */
808 GList *
clutter_gst_video_texture_get_audio_streams(ClutterGstVideoTexture * texture)809 clutter_gst_video_texture_get_audio_streams (ClutterGstVideoTexture *texture)
810 {
811   return clutter_gst_player_get_audio_streams (CLUTTER_GST_PLAYER (texture));
812 }
813 
814 /**
815  * clutter_gst_video_texture_get_audio_stream:
816  * @texture: a #ClutterGstVideoTexture
817  *
818  * Get the current audio stream. The number returned in the index of the
819  * audio stream playing in the list returned by
820  * clutter_gst_video_texture_get_audio_streams().
821  *
822  * Return value: the index of the current audio stream, -1 if the media has no
823  * audio stream
824  *
825  * Since: 1.4
826  */
827 gint
clutter_gst_video_texture_get_audio_stream(ClutterGstVideoTexture * texture)828 clutter_gst_video_texture_get_audio_stream (ClutterGstVideoTexture *texture)
829 {
830   return clutter_gst_player_get_audio_stream (CLUTTER_GST_PLAYER (texture));
831 }
832 
833 /**
834  * clutter_gst_video_texture_set_audio_stream:
835  * @texture: a #ClutterGstVideoTexture
836  * @index_: the index of the audio stream
837  *
838  * Set the audio stream to play. @index_ is the index of the stream
839  * in the list returned by clutter_gst_video_texture_get_audio_streams().
840  *
841  * Since: 1.4
842  */
843 void
clutter_gst_video_texture_set_audio_stream(ClutterGstVideoTexture * texture,gint index_)844 clutter_gst_video_texture_set_audio_stream (ClutterGstVideoTexture *texture,
845                                             gint                    index_)
846 {
847   clutter_gst_player_set_audio_stream (CLUTTER_GST_PLAYER (texture), index_);
848 }
849 
850 /**
851  * clutter_gst_video_texture_get_subtitle_tracks:
852  * @texture: a #ClutterGstVideoTexture
853  *
854  * Get the list of subtitles tracks of the current media.
855  *
856  * Return value: (transfer none) (element-type Gst.TagList): a list
857  * of #GstTagList describing the available subtitles tracks
858  *
859  * Since: 1.4
860  */
861 GList *
clutter_gst_video_texture_get_subtitle_tracks(ClutterGstVideoTexture * texture)862 clutter_gst_video_texture_get_subtitle_tracks (ClutterGstVideoTexture *texture)
863 {
864   return clutter_gst_player_get_subtitle_tracks (CLUTTER_GST_PLAYER (texture));
865 }
866 
867 /**
868  * clutter_gst_video_texture_get_subtitle_track:
869  * @texture: a #ClutterGstVideoTexture
870  *
871  * Get the current subtitles track. The number returned is the index of the
872  * subitles track in the list returned by
873  * clutter_gst_video_texture_get_subtitle_tracks().
874  *
875  * Return value: the index of the current subtitlest track, -1 if the media has
876  * no subtitles track or if the subtitles have been turned off
877  *
878  * Since: 1.4
879  */
880 gint
clutter_gst_video_texture_get_subtitle_track(ClutterGstVideoTexture * texture)881 clutter_gst_video_texture_get_subtitle_track (ClutterGstVideoTexture *texture)
882 {
883   return clutter_gst_player_get_subtitle_track (CLUTTER_GST_PLAYER (texture));
884 }
885 
886 /**
887  * clutter_gst_video_texture_set_subtitle_track:
888  * @texture: a #ClutterGstVideoTexture
889  * @index_: the index of the subtitles track
890  *
891  * Set the subtitles track to play. @index_ is the index of the stream
892  * in the list returned by clutter_gst_video_texture_get_subtitle_tracks().
893  *
894  * If @index_ is -1, the subtitles are turned off.
895  *
896  * Since: 1.4
897  */
898 void
clutter_gst_video_texture_set_subtitle_track(ClutterGstVideoTexture * texture,gint index_)899 clutter_gst_video_texture_set_subtitle_track (ClutterGstVideoTexture *texture,
900                                               gint                    index_)
901 {
902   clutter_gst_player_set_subtitle_track (CLUTTER_GST_PLAYER (texture), index_);
903 }
904