position and seeking

--HG--
extra : convert_revision : svn%3A41726ec3-264d-0410-9c23-a9f1637257cc/trunk%40173
This commit is contained in:
adrian.sampson 2009-04-04 10:18:53 +00:00
parent c752f36a4b
commit efddffb094
2 changed files with 76 additions and 13 deletions

View file

@ -306,8 +306,6 @@ class BaseServer(object):
if self.error:
yield 'error: ' + self.error
#fixme Still missing: time, bitrate, audio, updating_db
def cmd_clearerror(self, conn):
"""Removes the persistent error state of the server. This
@ -341,6 +339,7 @@ class BaseServer(object):
"""Clear the playlist."""
self.playlist = []
self.playlist_version += 1
self.cmd_stop(conn)
def cmd_delete(self, conn, index):
"""Remove the song at index from the playlist."""
@ -407,7 +406,7 @@ class BaseServer(object):
except IndexError:
raise ArgumentIndexError()
yield self._item_info(track)
def cmd_playlistid(self, track_id=-1):
def cmd_playlistid(self, conn, track_id=-1):
for l in self.cmd_playlistinfo(conn, self._id_to_index(track_id)):
yield l
@ -464,12 +463,17 @@ class BaseServer(object):
def cmd_play(self, conn, index=-1):
"""Begin playback, possibly at a specified playlist index."""
index = cast_arg(int, index)
if index < -1 or index > len(self.playlist):
raise ArgumentIndexError()
if index == -1: # No index specified: start where we are.
if not self.playlist: # Empty playlist: stop immediately.
self.cmd_stop(conn)
if self.current_index == -1: # No current song.
self.current_index = 0 # Start at the beginning.
# If we have a current song, just stay there.
else: # Start with the specified index.
self.current_index = index
@ -488,16 +492,15 @@ class BaseServer(object):
self.current_index = -1
self.paused = False
def cmd_seek(self, conn, index, time):
def cmd_seek(self, conn, index, pos):
"""Seek to a specified point in a specified song."""
index = cast_arg(int, index)
if index < 0 or index >= len(self.playlist):
raise ArgumentIndexError()
self.current_index = index
self.cmd_play(conn)
def cmd_seekid(self, track_id, time):
def cmd_seekid(self, track_id, pos):
index = self._id_to_index(track_id)
for l in self.cmd_seek(conn, index, time): yield l
for l in self.cmd_seek(conn, index, pos): yield l
class Connection(object):
"""A connection between a client and the server. Handles input and
@ -839,8 +842,15 @@ class Server(BaseServer):
for l in super(Server, self).cmd_status(conn): yield l
if self.current_index > -1:
item = self.playlist[self.current_index]
yield 'bitrate: ' + str(item.bitrate/1000)
yield 'time: 0:' + str(int(item.length)) #fixme
#fixme: missing 'audio'
(pos, total) = self.player.time()
yield 'time: ' + str(pos) + ':' + str(total)
#fixme: also missing 'updating_db'
def cmd_stats(self, conn):
# The first three items need to be done more efficiently. The
@ -918,14 +928,21 @@ class Server(BaseServer):
# The functions below hook into the half-implementations provided
# by the base class. Together, they're enough to implement all
# normal playback functionality.
# Playback control. The functions below hook into the
# half-implementations provided by the base class. Together, they're
# enough to implement all normal playback functionality.
def cmd_play(self, conn, index=-1):
new_index = index != -1 and index != self.current_index
was_paused = self.paused
super(Server, self).cmd_play(conn, index)
if self.current_index > -1: # Not stopped.
self.player.play_file(self.playlist[self.current_index].path)
if was_paused and not new_index:
# Just unpause.
self.player.play()
else:
self.player.play_file(self.playlist[self.current_index].path)
def cmd_pause(self, conn, state=None):
super(Server, self).cmd_pause(conn, state)
@ -935,8 +952,15 @@ class Server(BaseServer):
self.player.play()
def cmd_stop(self, conn):
super(Server, self).cmd_stop()
super(Server, self).cmd_stop(conn)
self.player.stop()
def cmd_seek(self, conn, index, pos):
"""Seeks to the specified position in the specified song."""
index = cast_arg(int, index)
pos = cast_arg(int, pos)
super(Server, self).cmd_seek(conn, index, pos)
self.player.seek(pos)
# When run as a script, just start the server.

View file

@ -47,6 +47,7 @@ class GstPlayer(object):
# Set up our own stuff.
self.playing = False
self.finished_callback = finished_callback
self.cached_time = None
def _get_state(self):
"""Returns the current state flag of the playbin."""
@ -59,6 +60,7 @@ class GstPlayer(object):
if message.type == gst.MESSAGE_EOS:
# file finished playing
self.playing = False
self.cached_time = None
if self.finished_callback:
self.finished_callback()
@ -91,6 +93,8 @@ class GstPlayer(object):
def stop(self):
"""Halt playback."""
self.player.set_state(gst.STATE_NULL)
self.playing = False
self.cached_time = None
def run(self):
"""Start a new thread for the player.
@ -105,6 +109,41 @@ class GstPlayer(object):
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)
try:
pos = self.player.query_position(fmt, None)[0]/(10**9)
length = self.player.query_duration(fmt, None)[0]/(10**9)
self.cached_time = (pos, length)
return (pos, length)
except gst.QueryError:
# Stream not ready. For small gaps of time, for instance
# after seeking, the time values are unavailable. For this
# reason, we cache recent.
if self.playing and self.cached_time:
return self.cached_time
else:
return (0, 0)
def seek(self, position):
"""Seeks to position (in seconds)."""
cur_pos, cur_len = self.time()
if position > cur_len:
self.stop()
return
fmt = gst.Format(gst.FORMAT_TIME)
ns = position * 10**9 # convert to nanoseconds
self.player.seek_simple(fmt, gst.SEEK_FLAG_FLUSH, ns)
# save new cached time
self.cached_time = (position, cur_len)
def block(self):
"""Block until playing finishes."""
while self.playing: