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.