Skip to content

Commit

Permalink
fix occasional gstreamer hang
Browse files Browse the repository at this point in the history
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
sampsyo committed Apr 2, 2012
1 parent 715cbce commit 16e1deb
Show file tree
Hide file tree
Showing 2 changed files with 6 additions and 6 deletions.
4 changes: 3 additions & 1 deletion README.rst
Expand Up @@ -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.
Expand Down
8 changes: 3 additions & 5 deletions audioread/gstdec.py
Expand Up @@ -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()


Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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:
Expand Down

2 comments on commit 16e1deb

@weisslj
Copy link

@weisslj weisslj commented on 16e1deb Jul 5, 2012

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Member Author

@sampsyo sampsyo commented on 16e1deb Jul 8, 2012

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.