Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

fix occasional gstreamer hang

The backend would hang unrecoverably in the buffer dequeue; this change seems to
have fixed it. I believe that the problem was caused by the fact that buffers
could be decoded before the "playing" flag was set on the object, leading to
missed buffers when Gstreamer ran fast.
- The main thread started the pipeline and acquired the semaphore.
- The file was decoded very quickly, and _new_buffer was called repeatedly but
  did nothing because self.playing was False.
- Finally, when self.playing became True, the file had already been decoded, so
  the queue was empty. Thus the self.queue.get() blocked forever.
It was a dumb idea to make the modification of the "playing" flag unsynchronized
with respect to its accesses in _pad_added.
  • Loading branch information...
commit 16e1deb0e40e26a2ca6ff07d1d4b4661b3030589 1 parent 715cbce
@sampsyo authored
Showing with 6 additions and 6 deletions.
  1. +3 −1 README.rst
  2. +3 −5 audioread/gstdec.py
View
4 README.rst
@@ -64,7 +64,9 @@ Version History
---------------
0.6
- Make FFmpeg timeout more robust. Dump FFmpeg output on timeout.
+ Make FFmpeg timeout more robust.
+ Dump FFmpeg output on timeout.
+ Fix a nondeterministic hang in the Gstreamer backend.
0.5
Fix crash when FFmpeg fails to report a duration.
View
8 audioread/gstdec.py
@@ -106,11 +106,9 @@ class MainLoopThread(threading.Thread):
def __init__(self):
super(MainLoopThread, self).__init__()
self.loop = gobject.MainLoop()
- self.running = False
self.daemon = True
def run(self):
- self.running = True
self.loop.run()
@@ -202,13 +200,13 @@ def __init__(self, path):
self.read_exc = None
# Return as soon as the stream is ready!
+ self.running = True
self.pipeline.set_state(gst.STATE_PLAYING)
self.ready_sem.acquire()
if self.read_exc:
# An error occurred before the stream became ready.
self.close(True)
raise self.read_exc
- self.running = True
# Gstreamer callbacks.
@@ -254,9 +252,9 @@ def _no_more_pads(self, element):
# Sent when the pads are done adding (i.e., there are no more
# streams in the file). If we haven't gotten at least one
# decodable stream, raise an exception.
- if not self.running and not self._got_a_pad:
+ if not self._got_a_pad:
self.read_exc = NoStreamError()
- self.ready_sem.release()
+ self.ready_sem.release() # No effect if we've already started.
def _new_buffer(self, sink):
if self.running:

2 comments on commit 16e1deb

@weisslj

Thanks a lot! I think this bug fixes the hang in my program... I spent 1-2 days trying to debug it, but wasn't successful...

@sampsyo
Owner

It certainly was a tricky one! Took me a good year or so of bug reports to finally nail it down. :)

Please sign in to comment.
Something went wrong with that request. Please try again.