Merge branch 'release-2.0' into develop
This commit is contained in:
commit
aa3293673a
@ -10,7 +10,11 @@ v2.1.0 (UNRELEASED)
|
|||||||
|
|
||||||
Feature release.
|
Feature release.
|
||||||
|
|
||||||
- Nothing yet.
|
- MPD: Fix MPD protocol for ``replay_gain_status`` command. The actual command
|
||||||
|
remains unimplemented. (PR: :issue:`1520`)
|
||||||
|
|
||||||
|
- MPD: Add ``nextsong`` and ``nextsongid`` to the response of MPD ``status`` command.
|
||||||
|
(Fixes: :issue:`1133`, :issue:`1516`, PR: :issue:`1523`)
|
||||||
|
|
||||||
|
|
||||||
v2.0.1 (UNRELEASED)
|
v2.0.1 (UNRELEASED)
|
||||||
@ -31,23 +35,32 @@ Bug fix release.
|
|||||||
(Fixes: :issue:`935`, :issue:`1453`, :issue:`1474` and :issue:`1480`, PR:
|
(Fixes: :issue:`935`, :issue:`1453`, :issue:`1474` and :issue:`1480`, PR:
|
||||||
:issue:`1487`)
|
:issue:`1487`)
|
||||||
|
|
||||||
|
- Audio: Better handling of seek when position does not match the expected
|
||||||
|
pending position. (Fixes: :issue:`1462`, PR: :issue:`1496`)
|
||||||
|
|
||||||
|
- Audio: Handle bad date tags from audio, thanks to Mario Lang and Tom Parker
|
||||||
|
who fixed this in parallel. (Fixes: :issue:`1506`, PR: :issue:`1525`,
|
||||||
|
:issue:`1517`)
|
||||||
|
|
||||||
|
- Audio: Make sure scanner handles streams without a duration.
|
||||||
|
(Fixes: :issue:`1526`)
|
||||||
|
|
||||||
|
- Audio: Ensure audio tags are never `None`. (Fixes: :issue:`1449`)
|
||||||
|
|
||||||
- Core: Avoid endless loop if all tracks in the tracklist are unplayable and
|
- Core: Avoid endless loop if all tracks in the tracklist are unplayable and
|
||||||
consume mode is off. (Fixes: :issue:`1221`, :issue:`1454`, PR: :issue:`1455`)
|
consume mode is off. (Fixes: :issue:`1221`, :issue:`1454`, PR: :issue:`1455`)
|
||||||
|
|
||||||
- File: Ensure path comparision is done between bytestrings only. Fixes crash
|
|
||||||
where a :confval:`file/media_dirs` path contained non-ASCII characters.
|
|
||||||
(Fixes: :issue:`1345`, PR: :issue:`1493`)
|
|
||||||
|
|
||||||
- MPD: Fix MPD protocol for ``replay_gain_status`` command. The actual command
|
|
||||||
remains unimplemented. (PR: :issue:`1520`)
|
|
||||||
|
|
||||||
- MPD: Add ``nextsong`` and ``nextsongid`` to the response of MPD ``status`` command.
|
|
||||||
(Fixes: :issue:`1133`, :issue:`1516`, PR: :issue:`1523`)
|
|
||||||
|
|
||||||
- Core: Correctly record the last position of a track when switching to another
|
- Core: Correctly record the last position of a track when switching to another
|
||||||
one. Particularly relevant for `mopidy-scrobbler` users, as before it was
|
one. Particularly relevant for `mopidy-scrobbler` users, as before it was
|
||||||
essentially unusable. (Fixes: :issue:`1456`, PR: :issue:`1534`)
|
essentially unusable. (Fixes: :issue:`1456`, PR: :issue:`1534`)
|
||||||
|
|
||||||
|
- File: Ensure path comparison is done between bytestrings only. Fixes crash
|
||||||
|
where a :confval:`file/media_dirs` path contained non-ASCII characters.
|
||||||
|
(Fixes: :issue:`1345`, PR: :issue:`1493`)
|
||||||
|
|
||||||
|
- Stream: Fix milliseconds vs seconds mistake in timeout handling.
|
||||||
|
(Fixes: :issue:`1521`, PR: :issue:`1522`)
|
||||||
|
|
||||||
|
|
||||||
v2.0.0 (2016-02-15)
|
v2.0.0 (2016-02-15)
|
||||||
===================
|
===================
|
||||||
|
|||||||
@ -22,7 +22,6 @@ logger = logging.getLogger(__name__)
|
|||||||
gst_logger = logging.getLogger('mopidy.audio.gst')
|
gst_logger = logging.getLogger('mopidy.audio.gst')
|
||||||
|
|
||||||
_GST_PLAY_FLAGS_AUDIO = 0x02
|
_GST_PLAY_FLAGS_AUDIO = 0x02
|
||||||
_GST_PLAY_FLAGS_SOFT_VOLUME = 0x10
|
|
||||||
|
|
||||||
_GST_STATE_MAPPING = {
|
_GST_STATE_MAPPING = {
|
||||||
Gst.State.PLAYING: PlaybackState.PLAYING,
|
Gst.State.PLAYING: PlaybackState.PLAYING,
|
||||||
@ -369,7 +368,7 @@ class _Handler(object):
|
|||||||
|
|
||||||
# Emit any postponed tags that we got after about-to-finish.
|
# Emit any postponed tags that we got after about-to-finish.
|
||||||
tags, self._audio._pending_tags = self._audio._pending_tags, None
|
tags, self._audio._pending_tags = self._audio._pending_tags, None
|
||||||
self._audio._tags = tags
|
self._audio._tags = tags or {}
|
||||||
|
|
||||||
if tags:
|
if tags:
|
||||||
logger.debug('Audio event: tags_changed(tags=%r)', tags.keys())
|
logger.debug('Audio event: tags_changed(tags=%r)', tags.keys())
|
||||||
@ -451,8 +450,7 @@ class Audio(pykka.ThreadingActor):
|
|||||||
|
|
||||||
def _setup_playbin(self):
|
def _setup_playbin(self):
|
||||||
playbin = Gst.ElementFactory.make('playbin')
|
playbin = Gst.ElementFactory.make('playbin')
|
||||||
playbin.set_property(
|
playbin.set_property('flags', _GST_PLAY_FLAGS_AUDIO)
|
||||||
'flags', _GST_PLAY_FLAGS_AUDIO | _GST_PLAY_FLAGS_SOFT_VOLUME)
|
|
||||||
|
|
||||||
# TODO: turn into config values...
|
# TODO: turn into config values...
|
||||||
playbin.set_property('buffer-size', 5 << 20) # 5MB
|
playbin.set_property('buffer-size', 5 << 20) # 5MB
|
||||||
@ -489,15 +487,16 @@ class Audio(pykka.ThreadingActor):
|
|||||||
|
|
||||||
def _setup_audio_sink(self):
|
def _setup_audio_sink(self):
|
||||||
audio_sink = Gst.ElementFactory.make('bin', 'audio-sink')
|
audio_sink = Gst.ElementFactory.make('bin', 'audio-sink')
|
||||||
|
queue = Gst.ElementFactory.make('queue')
|
||||||
|
volume = Gst.ElementFactory.make('volume')
|
||||||
|
|
||||||
# Queue element to buy us time between the about-to-finish event and
|
# Queue element to buy us time between the about-to-finish event and
|
||||||
# the actual switch, i.e. about to switch can block for longer thanks
|
# the actual switch, i.e. about to switch can block for longer thanks
|
||||||
# to this queue.
|
# to this queue.
|
||||||
|
|
||||||
# TODO: See if settings should be set to minimize latency. Previous
|
# TODO: See if settings should be set to minimize latency. Previous
|
||||||
# setting breaks appsrc, and settings before that broke on a few
|
# setting breaks appsrc, and settings before that broke on a few
|
||||||
# systems. So leave the default to play it safe.
|
# systems. So leave the default to play it safe.
|
||||||
queue = Gst.ElementFactory.make('queue')
|
|
||||||
|
|
||||||
if self._config['audio']['buffer_time'] > 0:
|
if self._config['audio']['buffer_time'] > 0:
|
||||||
queue.set_property(
|
queue.set_property(
|
||||||
'max-size-time',
|
'max-size-time',
|
||||||
@ -505,15 +504,13 @@ class Audio(pykka.ThreadingActor):
|
|||||||
|
|
||||||
audio_sink.add(queue)
|
audio_sink.add(queue)
|
||||||
audio_sink.add(self._outputs)
|
audio_sink.add(self._outputs)
|
||||||
|
|
||||||
if self.mixer:
|
|
||||||
volume = Gst.ElementFactory.make('volume')
|
|
||||||
audio_sink.add(volume)
|
audio_sink.add(volume)
|
||||||
|
|
||||||
queue.link(volume)
|
queue.link(volume)
|
||||||
volume.link(self._outputs)
|
volume.link(self._outputs)
|
||||||
|
|
||||||
|
if self.mixer:
|
||||||
self.mixer.setup(volume, self.actor_ref.proxy().mixer)
|
self.mixer.setup(volume, self.actor_ref.proxy().mixer)
|
||||||
else:
|
|
||||||
queue.link(self._outputs)
|
|
||||||
|
|
||||||
ghost_pad = Gst.GhostPad.new('sink', queue.get_static_pad('sink'))
|
ghost_pad = Gst.GhostPad.new('sink', queue.get_static_pad('sink'))
|
||||||
audio_sink.add_pad(ghost_pad)
|
audio_sink.add_pad(ghost_pad)
|
||||||
|
|||||||
@ -135,6 +135,17 @@ def _start_pipeline(pipeline):
|
|||||||
pipeline.set_state(Gst.State.PLAYING)
|
pipeline.set_state(Gst.State.PLAYING)
|
||||||
|
|
||||||
|
|
||||||
|
def _query_duration(pipeline):
|
||||||
|
success, duration = pipeline.query_duration(Gst.Format.TIME)
|
||||||
|
if not success:
|
||||||
|
duration = None # Make sure error case preserves None.
|
||||||
|
elif duration < 0:
|
||||||
|
duration = None # Stream without duration.
|
||||||
|
else:
|
||||||
|
duration = duration // Gst.MSECOND
|
||||||
|
return success, duration
|
||||||
|
|
||||||
|
|
||||||
def _query_seekable(pipeline):
|
def _query_seekable(pipeline):
|
||||||
query = Gst.Query.new_seeking(Gst.Format.TIME)
|
query = Gst.Query.new_seeking(Gst.Format.TIME)
|
||||||
pipeline.query(query)
|
pipeline.query(query)
|
||||||
@ -187,13 +198,8 @@ def _process(pipeline, timeout_ms):
|
|||||||
elif message.type == Gst.MessageType.EOS:
|
elif message.type == Gst.MessageType.EOS:
|
||||||
return tags, mime, have_audio, duration
|
return tags, mime, have_audio, duration
|
||||||
elif message.type == Gst.MessageType.ASYNC_DONE:
|
elif message.type == Gst.MessageType.ASYNC_DONE:
|
||||||
success, duration = pipeline.query_duration(Gst.Format.TIME)
|
success, duration = _query_duration(pipeline)
|
||||||
if success:
|
if tags and success:
|
||||||
duration = duration // Gst.MSECOND
|
|
||||||
else:
|
|
||||||
duration = None
|
|
||||||
|
|
||||||
if tags and duration is not None:
|
|
||||||
return tags, mime, have_audio, duration
|
return tags, mime, have_audio, duration
|
||||||
|
|
||||||
# Workaround for upstream bug which causes tags/duration to arrive
|
# Workaround for upstream bug which causes tags/duration to arrive
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user