Repository: stb-tester/stb-tester

Issue:
stbt run teardown hangs indefinitely with Blackmagic Intensity Pro.
With GStreamer 1.0 and the BlackMagic Intensity Pro during pipeline teardown (after the test script has run to completion) stbt run hangs indefinitely.



Discussion:

Resolving patch:
diff --git a/gst_hacks.py b/gst_hacks.py
--- a/gst_hacks.py
+++ b/gst_hacks.py
@@ -70,3 +70,19 @@ def test_map_buffer_modifying_data():
         a[2] = 1
 
     assert b.extract_dup(0, 5) == "he\x01lo"
+
+
+def gst_iterate(gst_iterator):
+    """Wrap a Gst.Iterator to expose the Python iteration protocol.  The
+    gst-python package exposes similar functionality on Gst.Iterator itself so
+    this code should be retired in the future once gst-python is broadly enough
+    available."""
+    result = Gst.IteratorResult.OK
+    while result == Gst.IteratorResult.OK:
+        result, value = gst_iterator.next()
+        if result == Gst.IteratorResult.OK:
+            yield value
+        elif result == Gst.IteratorResult.ERROR:
+            raise RuntimeError("Iteration Error")
+        elif result == Gst.IteratorResult.RESYNC:
+            raise RuntimeError("Iteration Resync")
diff --git a/stbt.py b/stbt.py
--- a/stbt.py
+++ b/stbt.py
@@ -32,7 +32,7 @@
 import numpy
 
 import irnetbox
-from gst_hacks import map_gst_buffer
+from gst_hacks import map_gst_buffer, gst_iterate
 
 
 GObject.threads_init()
@@ -1127,6 +1127,7 @@ def __init__(self, user_source_pipeline, user_sink_pipeline, save_video,
         self.start_timestamp = None
         self.underrun_timeout = None
         self.video_debug = []
+        self.tearing_down = False
 
         self.restart_source_enabled = restart_source
 
@@ -1321,8 +1322,9 @@ def on_warning(self, _bus, message):
         sys.stderr.write("Warning: %s: %s\n%s\n" % (err, err.message, dbg))
 
     def on_eos_from_source_pipeline(self, _bus, _message):
-        warn("Got EOS from source pipeline")
-        self.restart_source()
+        if not self.tearing_down:
+            warn("Got EOS from source pipeline")
+            self.restart_source()
 
     def on_eos_from_sink_pipeline(self, _bus, _message):
         debug("Got EOS")
@@ -1353,6 +1355,8 @@ def restart_source(self, *_args):
         return False  # stop the timeout from running again
 
     def start_source(self):
+        if self.tearing_down:
+            return False
         warn("Restarting source pipeline...")
         self.create_source_pipeline()
         self.source_pipeline.set_state(Gst.State.PLAYING)
@@ -1361,9 +1365,31 @@ def start_source(self):
             self.underrun_timeout.start()
         return False  # stop the timeout from running again
 
+    @staticmethod
+    def appsink_await_eos(appsink, timeout=None):
+        done = threading.Event()
+
+        def on_eos(_appsink):
+            done.set()
+            return True
+        hid = appsink.connect('eos', on_eos)
+        if appsink.get_property('eos'):
+            appsink.disconnect(hid)
+            return True
+        d = done.wait(timeout)
+        appsink.disconnect(hid)
+        return d
+
     def teardown(self):
+        self.tearing_down = True
         if self.source_pipeline:
+            for elem in gst_iterate(self.source_pipeline.iterate_sources()):
+                elem.send_event(Gst.Event.new_eos())
+            if not self.appsink_await_eos(
+                    self.source_pipeline.get_by_name('appsink'), timeout=10):
+                debug("teardown: Source pipeline did not teardown gracefully")
             self.source_pipeline.set_state(Gst.State.NULL)
+            self.source_pipeline = None
         if not self.novideo:
             debug("teardown: Sending eos")
             self.appsrc.emit("end-of-stream")


Test patch: