-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
bpd: Updated to GStreamer 1.0 #2062
Changes from 4 commits
0917e67
5613d9d
0a3d18c
9fd227c
7b154ce
f9ed53a
a48a4c9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,16 +21,21 @@ | |
|
||
import sys | ||
import time | ||
import gobject | ||
import thread | ||
import os | ||
import copy | ||
import urllib | ||
|
||
import pygst | ||
pygst.require('0.10') | ||
import gst # noqa | ||
import gi | ||
from gi.repository import GObject, Gst | ||
|
||
gi.require_version('Gst', '1.0') | ||
|
||
GObject.threads_init() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Apparently, |
||
Gst.init(None) | ||
|
||
class QueryError(Exception): | ||
pass | ||
|
||
class GstPlayer(object): | ||
"""A music player abstracting GStreamer's Playbin element. | ||
|
@@ -57,8 +62,19 @@ def __init__(self, finished_callback=None): | |
|
||
# Set up the Gstreamer player. From the pygst tutorial: | ||
# http://pygstdocs.berlios.de/pygst-tutorial/playbin.html | ||
self.player = gst.element_factory_make("playbin2", "player") | ||
fakesink = gst.element_factory_make("fakesink", "fakesink") | ||
#### | ||
# Updated to GStreamer 1.0 with: | ||
# https://wiki.ubuntu.com/Novacut/GStreamer1.0 | ||
self.player = Gst.ElementFactory.make("playbin", "player") | ||
|
||
if self.player is None: | ||
raise RuntimeError("Could not create playbin") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For these user-visible errors, please use |
||
|
||
fakesink = Gst.ElementFactory.make("fakesink", "fakesink") | ||
|
||
if fakesink is None: | ||
raise RuntimeError("Could not create fakesink") | ||
|
||
self.player.set_property("video-sink", fakesink) | ||
bus = self.player.get_bus() | ||
bus.add_signal_watch() | ||
|
@@ -74,21 +90,21 @@ def _get_state(self): | |
"""Returns the current state flag of the playbin.""" | ||
# gst's get_state function returns a 3-tuple; we just want the | ||
# status flag in position 1. | ||
return self.player.get_state()[1] | ||
return self.player.get_state(Gst.CLOCK_TIME_NONE)[1] | ||
|
||
def _handle_message(self, bus, message): | ||
"""Callback for status updates from GStreamer.""" | ||
if message.type == gst.MESSAGE_EOS: | ||
if message.type == Gst.MessageType.EOS: | ||
# file finished playing | ||
self.player.set_state(gst.STATE_NULL) | ||
self.player.set_state(Gst.State.NULL) | ||
self.playing = False | ||
self.cached_time = None | ||
if self.finished_callback: | ||
self.finished_callback() | ||
|
||
elif message.type == gst.MESSAGE_ERROR: | ||
elif message.type == Gst.MessageType.ERROR: | ||
# error | ||
self.player.set_state(gst.STATE_NULL) | ||
self.player.set_state(Gst.State.NULL) | ||
err, debug = message.parse_error() | ||
print(u"Error: {0}".format(err)) | ||
self.playing = False | ||
|
@@ -109,27 +125,27 @@ def play_file(self, path): | |
"""Immediately begin playing the audio file at the given | ||
path. | ||
""" | ||
self.player.set_state(gst.STATE_NULL) | ||
self.player.set_state(Gst.State.NULL) | ||
if isinstance(path, unicode): | ||
path = path.encode('utf8') | ||
uri = 'file://' + urllib.quote(path) | ||
self.player.set_property("uri", uri) | ||
self.player.set_state(gst.STATE_PLAYING) | ||
self.player.set_state(Gst.State.PLAYING) | ||
self.playing = True | ||
|
||
def play(self): | ||
"""If paused, resume playback.""" | ||
if self._get_state() == gst.STATE_PAUSED: | ||
self.player.set_state(gst.STATE_PLAYING) | ||
if self._get_state() == Gst.State.PAUSED: | ||
self.player.set_state(Gst.State.PLAYING) | ||
self.playing = True | ||
|
||
def pause(self): | ||
"""Pause playback.""" | ||
self.player.set_state(gst.STATE_PAUSED) | ||
self.player.set_state(Gst.State.PAUSED) | ||
|
||
def stop(self): | ||
"""Halt playback.""" | ||
self.player.set_state(gst.STATE_NULL) | ||
self.player.set_state(Gst.State.NULL) | ||
self.playing = False | ||
self.cached_time = None | ||
|
||
|
@@ -139,27 +155,36 @@ def run(self): | |
Call this function before trying to play any music with | ||
play_file() or play(). | ||
""" | ||
|
||
# If we don't use the MainLoop, messages are never sent. | ||
gobject.threads_init() | ||
|
||
def start(): | ||
loop = gobject.MainLoop() | ||
loop = GObject.MainLoop() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Modern GObject apparently recommends |
||
loop.run() | ||
|
||
thread.start_new_thread(start, ()) | ||
|
||
def time(self): | ||
"""Returns a tuple containing (position, length) where both | ||
values are integers in seconds. If no stream is available, | ||
returns (0, 0). | ||
""" | ||
fmt = gst.Format(gst.FORMAT_TIME) | ||
fmt = Gst.Format(Gst.Format.TIME) | ||
try: | ||
pos = self.player.query_position(fmt, None)[0] / (10 ** 9) | ||
length = self.player.query_duration(fmt, None)[0] / (10 ** 9) | ||
posq = self.player.query_position(fmt) | ||
if not posq[0]: | ||
raise QueryError("query_position failed") | ||
pos = posq[1] / (10 ** 9) | ||
|
||
lengthq = self.player.query_duration(fmt) | ||
if not lengthq[0]: | ||
raise QueryError("query_duration failed") | ||
length = lengthq[1] / (10 ** 9) | ||
|
||
self.cached_time = (pos, length) | ||
return (pos, length) | ||
|
||
except gst.QueryError: | ||
except QueryError: | ||
# Stream not ready. For small gaps of time, for instance | ||
# after seeking, the time values are unavailable. For this | ||
# reason, we cache recent. | ||
|
@@ -175,9 +200,9 @@ def seek(self, position): | |
self.stop() | ||
return | ||
|
||
fmt = gst.Format(gst.FORMAT_TIME) | ||
fmt = Gst.Format(Gst.Format.TIME) | ||
ns = position * 10 ** 9 # convert to nanoseconds | ||
self.player.seek_simple(fmt, gst.SEEK_FLAG_FLUSH, ns) | ||
self.player.seek_simple(fmt, Gst.SeekFlags.FLUSH, ns) | ||
|
||
# save new cached time | ||
self.cached_time = (position, cur_len) | ||
|
@@ -208,12 +233,14 @@ def play_complicated(paths): | |
def next_song(): | ||
my_paths.pop(0) | ||
p.play_file(my_paths[0]) | ||
|
||
p = GstPlayer(next_song) | ||
p.run() | ||
p.play_file(my_paths[0]) | ||
while my_paths: | ||
time.sleep(1) | ||
|
||
|
||
if __name__ == '__main__': | ||
# A very simple command-line player. Just give it names of audio | ||
# files on the command line; these are all played in sequence. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably recommend the generic pygobject package (i.e.,
python-gi
orpython2-gobject
) instead of a special-purpose python-gstreamer library.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See also: http://docs.beets.io/en/v1.3.18/plugins/chroma.html?highlight=gstreamer