dd7a84bec19877f474ffcf7bd003604aae6b4729
[experiments/gstreamer.git] / gst-custom-player.py
1 #!/usr/bin/env python
2
3 # Simple media player with GStreamer
4 #
5 # Copyright (C) 2013  Antonio Ospite <ospite@studenti.unina.it>
6 #
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20 # References:
21 # http://pygstdocs.berlios.de/pygst-reference
22 # https://core.fluendo.com/gstreamer/svn/trunk/gst-fluendo-gdlsink/test/ismdplay.py
23
24 import sys
25 import os
26
27 import gobject
28 gobject.threads_init()
29
30 import gst
31
32 # The player window will have a fixed width and height.
33 # This is just to demonstrate the use of capabilities.
34 WIDTH = 640
35 HEIGHT = 300
36
37
38 class CustomVideoBin(gst.Bin):
39     def __init__(self):
40         gst.Bin.__init__(self, 'CustomVideoBin')
41
42         queue = gst.element_factory_make('queue', 'vqueue')
43         self.add(queue)
44
45         caps = gst.Caps("video/x-raw-yuv,format=(fourcc)AYUV,width=%d,height=%d" % (HEIGHT, WIDTH))
46         capsfilter = gst.element_factory_make("capsfilter", "filter")
47         capsfilter.set_property("caps", caps)
48         self.add(capsfilter)
49
50         rescale = gst.element_factory_make("videoscale", "rescale")
51         self.add(rescale)
52
53         colorspace = gst.element_factory_make("colorspace", "colorspace")
54         self.add(colorspace)
55
56         videosink = gst.element_factory_make("autovideosink", "vidoesink")
57         self.add(videosink)
58
59         gst.element_link_many(queue, capsfilter, rescale, colorspace, videosink)
60         sink = queue.get_pad('sink')
61         self.add_pad(gst.GhostPad('sink', sink))
62
63
64 class CustomAudioBin(gst.Bin):
65     def __init__(self):
66         gst.Bin.__init__(self, 'CustomAudioBin')
67
68         queue = gst.element_factory_make('queue', 'aqueue')
69         self.add(queue)
70
71         audioconvert = gst.element_factory_make("audioconvert", "audioconverter")
72         self.add(audioconvert)
73
74         audiosink = gst.element_factory_make("autoaudiosink", "audiosink")
75         self.add(audiosink)
76
77         gst.element_link_many(queue, audioconvert, audiosink)
78         sink = queue.get_pad('sink')
79         self.add_pad(gst.GhostPad('sink', sink))
80
81
82 class CustomPlayBin(gst.Pipeline):
83     __gproperties__ = {
84         'source': (gst.Element, "source", "Source element", gobject.PARAM_READABLE)
85     }
86
87     def __init__(self, uri=None):
88         gst.Pipeline.__init__(self, 'CustomPlayBin')
89
90         self._uri = uri
91
92         self._playbin = gst.element_factory_make("playbin2", "playbin")
93         self.add(self._playbin)
94
95         self._playbin.set_property("uri", self._uri)
96         self._playbin.set_property("video-sink", CustomVideoBin())
97         self._playbin.set_property("audio-sink", CustomAudioBin())
98
99     def set_uri(self, uri):
100         self._uri = uri
101         self._playbin.set_property("uri", self._uri)
102
103
104 class GstPlayer:
105     def __init__(self):
106
107         # The user can require some action at End Of Stream
108         self.eos_cb = None
109
110         self.pipeline = CustomPlayBin()
111
112         bus = self.pipeline.get_bus()
113         bus.add_signal_watch()
114         bus.connect('message::eos', self.on_eos)
115         bus.connect('message::tag', self.on_tag)
116         bus.connect('message::error', self.on_error)
117         bus.connect('message::state-changed', self.on_state_changed)
118
119     def on_eos(self, bus, msg):
120         print 'on_eos'
121         self.stop()
122         if self.eos_cb:
123             self.eos_cb()
124
125     def on_tag(self, bus, msg):
126         print 'on_tag:'
127         taglist = msg.parse_tag()
128         for key in taglist.keys():
129             print '\t%s = %s' % (key, taglist[key])
130
131     def on_error(self, bus, msg):
132         print 'on_error'
133         error, debug = msg.parse_error()
134         print "Error: %s" % error, debug
135         self.stop()
136
137     def on_state_changed(self, bus, msg):
138         print 'on_state_changed'
139         if msg.src != self.pipeline:
140             return
141
142         old_state, new_state, pending = msg.parse_state_changed()
143
144     def set_location(self, location):
145         self.pipeline.set_uri(location)
146
147     def play(self):
148         self.pipeline.set_state(gst.STATE_PLAYING)
149
150     def pause(self):
151         self.pipeline.set_state(gst.STATE_PAUSED)
152
153     def stop(self):
154         self.pipeline.set_state(gst.STATE_NULL)
155
156
157 class PlayerTUI():
158     def __init__(self, location):
159
160         self.player = GstPlayer()
161         self.player.eos_cb = self.quit
162
163         self.mainloop = gobject.MainLoop()
164
165         self.player.set_location(location)
166         self.player.play()
167
168         gobject.io_add_watch(sys.stdin, gobject.IO_IN, self.on_stdin)
169
170         try:
171             self.mainloop.run()
172         except KeyboardInterrupt:
173             self.quit()
174
175     def on_stdin(self, fd, event):
176         # The user has to send a newline fo this to go on
177         c = os.read(fd.fileno(), 1)
178
179         if c == "q":
180             self.quit()
181         elif c == "s":
182             self.player.pause()
183         elif c == "r":
184             self.player.play()
185
186         return True
187
188     def quit(self):
189         self.player.stop()
190         self.player = None
191         self.mainloop.quit()
192
193
194 def main(args):
195     def usage():
196         sys.stdout.write("usage: %s <URI-OF-MEDIA-FILE>\n" % args[0])
197
198     if len(args) != 2:
199         usage()
200         sys.exit(1)
201
202     if not gst.uri_is_valid(args[1]):
203         sys.stderr.write("Error: Invalid URI: %s\n" % args[1])
204         sys.exit(1)
205
206     tui = PlayerTUI(args[1])
207
208 if __name__ == '__main__':
209     sys.exit(main(sys.argv))