How Do I Clutter with Gst over Gir for Python

it was quite difficult to figure out how this can be done. i found some code in different places in different languages. some were missing gir but used python, some were in plain c, some used old apis. finally i had a non-op python script and a working c program. to figure out what the difference was is boiled both down to the bare minimum. this is the result:

C

#include <stdio.h>
#include <glib-object.h>
#include <clutter/clutter.h>
#include <clutter-gst/clutter-gst.h>
#include <gst/gst.h>

void on_size_change(ClutterActor *texture, gint width, gint height) {
        ClutterActor * stage = clutter_actor_get_stage(texture);
        if (stage == NULL)
                return;
        clutter_actor_set_size(stage, width, height);
        clutter_actor_set_position(texture, 0, 0);
        clutter_actor_set_size(texture, width, height);
        }

void on_error(void* bus, void* msg) {
        printf("on_error: \n");
        }

void on_destroy() {
        clutter_main_quit();
        }

int __init__(char* uri) {
        ClutterActor * stage = CLUTTER_ACTOR(g_object_new(CLUTTER_TYPE_STAGE, NULL));

        ClutterActor * texture = CLUTTER_ACTOR(g_object_new(CLUTTER_TYPE_TEXTURE, NULL));
        if (texture == NULL)
                return 2;
        g_signal_connect(texture, "size-change", G_CALLBACK(on_size_change), NULL);
        GstElement * pipeline = gst_element_factory_make("playbin", NULL);
        if (pipeline == NULL)
                return 3;
        GstBus * bus = gst_pipeline_get_bus(pipeline);
        g_signal_connect(bus, "message::error", on_error, NULL);
        g_object_set(pipeline, "uri", uri, NULL);
        GstElement * sink = gst_element_factory_make("autocluttersink", NULL);
        if (sink == NULL)
                return 4;
        g_object_set(sink, "texture", texture, NULL);
        g_object_set(pipeline, "video-sink", sink, NULL);
        gst_element_set_state(pipeline, GST_STATE_PLAYING);
        clutter_group_add(CLUTTER_GROUP(stage), texture);
        clutter_actor_show_all(stage);
        clutter_main();
        gst_element_set_state(pipeline, GST_STATE_NULL);
        gst_object_unref(pipeline);
        return 0;
}

int main(int argc, char *argv[]) {
        if (clutter_gst_init(&argc, &argv) != CLUTTER_INIT_SUCCESS)
                return 1;
        return __init__(argv[1]);
}

Python

   1 from sys import argv
   2 from gi.repository import GObject
   3 from gi.repository import Clutter
   4 from gi.repository import ClutterGst
   5 from gi.repository import Gst
   6 
   7 def on_size_change(texture, width, height):
   8         stage = texture.get_stage()
   9         if stage is None:
  10                 return
  11         stage.set_size(width, height)
  12         texture.set_position(0, 0)
  13         texture.set_size(width, height)
  14         #
  15 
  16 def on_error(bus, msg):
  17         print('on_error():', msg.parse_error())
  18         #
  19 
  20 def on_destroy(*args):
  21         Clutter.main_quit()
  22         #
  23 
  24 def __init__ (uri):
  25         stage = Clutter.Stage()
  26         stage.connect('destroy', on_destroy)
  27         texture = Clutter.Texture()
  28         if texture is None:
  29                 return 2
  30         texture.connect('size-change', on_size_change)
  31         pipeline = Gst.ElementFactory.make('playbin', None)
  32         if pipeline is None:
  33                 return 3
  34         bus = pipeline.get_bus()
  35         bus.connect('message::error', on_error)
  36         pipeline.set_property('uri', uri)
  37         sink = Gst.ElementFactory.make('autocluttersink', None)
  38         if sink is None:
  39                 return 4
  40         sink.set_property('texture', texture)
  41         pipeline.set_property('video-sink', sink)
  42         pipeline.set_state(Gst.State.PLAYING)
  43         stage.add_actor(texture)
  44         stage.show_all()
  45         Clutter.main()
  46         pipeline.set_state(Gst.State.NULL)
  47         del pipeline
  48         return 0
  49         #
  50 
  51 def main(argv):
  52         if ClutterGst.init(argv)[0] != 1: # Clutter.INIT_SUCCESS:
  53                 return 1
  54         return __init__(*argv[1:])
  55         #
  56 
  57 if __name__ == '__main__':
  58         exit(main(argv))

the funny thing about the sources is that both are line-by-line doing the same stuff. and i found out what was wrong. my sample code used the wrong class in the first place which i fixed. now both work fine (the c-variant seems to have some problems when terminiating, maybe someone can point out what's wrong. as i was looking for a python solution i don't care).

Simple player

next i improved the solution to have some more features. play, pause and stop are included, sizing works by centering (stage is fullscreen).

   1 from gi.repository import GObject
   2 from gi.repository import Clutter
   3 from gi.repository import ClutterGst
   4 from gi.repository import Gst
   5 
   6 class Video(object):
   7         def __init__ (self, filename):
   8                 self.stage = Clutter.Stage()
   9                 self.stage.set_fullscreen(True)
  10                 #self.stage.set_size(1000, 600)
  11                 self.stage.set_color(Clutter.Color.new(0x11, 0x11, 0x11, 0xff))
  12                 self.stage.set_title('GStreamer Clutter Python - ' + filename)
  13                 self.stage.connect('key-press-event', self.on_key_press_event)
  14                 self.stage.connect('destroy', self.on_destroy)
  15                 self.texture = Clutter.Texture.new()
  16                 self.texture.connect('size-change', self.on_size_change)
  17                 self.stage.add_actor(self.texture)
  18                 self.sink = ClutterGst.VideoSink.new(self.texture)
  19                 self.sink.set_property('sync', True)
  20                 self.player = Gst.ElementFactory.make('playbin', None)
  21                 self.player.set_property('video-sink', self.sink)
  22                 self.bus = self.player.get_bus()
  23                 self.bus.add_signal_watch()
  24                 self.bus.connect('message::eos', self.on_eos)
  25                 self.bus.connect('message::error', self.on_error)
  26                 self.stage.show_all()
  27                 self.player.set_property('uri', 'file://' + filename)
  28                 self.player.set_state(Gst.State.PLAYING)
  29 
  30         def on_size_change(self, texture, width, height):
  31                 print('on_size_change:')
  32                 stage = texture.get_stage()
  33                 if stage is not None:
  34                         dx, dy = stage.get_size()
  35                         texture.set_anchor_point(width/2, height/2)
  36                         texture.set_position(dx/2, dy/2)
  37 
  38         def on_error(self, bus, msg):
  39                 print('on_error:', msg.parse_error())
  40 
  41         def on_eos(self, bus, msg):
  42                 print('on_eos:', msg)
  43                 self.player.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, 0)
  44                 self.player.set_state(Gst.State.PAUSED)
  45 
  46         def on_destroy(self, stage, event):
  47                 print('on_destroy:', event)
  48                 self.player.set_state(Gst.State.NULL)
  49                 Clutter.main_quit()
  50 
  51         def on_key_press_event(self, stage, event):
  52                 print('on_key_press_event', event)
  53                 if event.unicode_value:
  54                         c = event.unicode_value
  55                         if c in 'qx':
  56                                 self.on_destroy(stage, event)
  57                                 print('quit')
  58                         elif c in 'pP':
  59                                 self.player.set_state(Gst.State.PAUSED)
  60                                 print('pause')
  61                         elif c in 'gG':
  62                                 self.player.set_state(Gst.State.PLAYING)
  63                                 print('play')
  64                         elif c in 'sS':
  65                                 self.player.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, 0)
  66                                 self.player.set_state(Gst.State.PAUSED)
  67                                 print('stop')
  68 
  69 
  70 if __name__ == '__main__':
  71         from sys import argv
  72         GObject.threads_init()
  73         Gst.init(argv)
  74         ClutterGst.init(argv)
  75         Clutter.init(argv)
  76         video = Video(*argv[1:])
  77         Clutter.main()

i hope these samples are kind of useful.

HowDoI/ClutterGstGirPython (last edited 2013-09-23 23:11:52 by m. dietrich)