fix audio getting stuck (1/2)

The problem was caused by stop() doing a spin loop on the main
thread waiting for the completion signal. This prevented Qt's run
loop from executing, and so the completion signal was never delivered,
meaning longer files would time out.

Fixed by reworking the code so that stop() does not block at all -
instead it just sets the termination flag, and AVPlayer does not
unset current_player. Then when the completion callback fires, it
can advance to the next file.

TTS code still needs updating, and the lock should be safe to remove
as the start/stop logic is all on the main thread.
This commit is contained in:
Damien Elmes 2020-03-15 09:26:31 +10:00
parent e7452300a2
commit f30853f5ed

View File

@ -129,7 +129,6 @@ class AVPlayer:
def _stop_if_playing(self) -> None:
if self.current_player:
self.current_player.stop()
self.current_player = None
def _pop_next(self) -> Optional[AVTag]:
if not self._enqueued:
@ -235,17 +234,15 @@ class SimpleProcessPlayer(Player): # pylint: disable=abstract-method
self._lock = threading.Lock()
def play(self, tag: AVTag, on_done: OnDoneCallback) -> None:
self._terminate_flag = False
self._taskman.run_in_background(
lambda: self._play(tag), lambda res: self._on_done(res, on_done)
)
def stop(self) -> None:
self._terminate_flag = True
# block until stopped
t = time.time()
while self._terminate_flag and time.time() - t < 3:
time.sleep(0.1)
# note: mplayer implementation overrides this
def _play(self, tag: AVTag) -> None:
assert isinstance(tag, SoundOrVideoTag)
self._process = subprocess.Popen(
@ -264,19 +261,11 @@ class SimpleProcessPlayer(Player): # pylint: disable=abstract-method
while True:
with self._lock:
# if .stop() timed out, another thread may run when
# there is no process
if not self._process:
self._process = None
self._terminate_flag = False
return
# should we abort playing?
if self._terminate_flag:
self._process.terminate()
self._process = None
self._terminate_flag = False
raise PlayerInterrupted()
return
# wait for completion
try:
@ -284,17 +273,14 @@ class SimpleProcessPlayer(Player): # pylint: disable=abstract-method
if self._process.returncode != 0:
print(f"player got return code: {self._process.returncode}")
self._process = None
self._terminate_flag = False
return
except subprocess.TimeoutExpired:
# process still running, repeat loop
pass
def _on_done(self, ret: Future, cb: OnDoneCallback) -> None:
try:
ret.result()
except PlayerInterrupted:
# don't fire done callback when interrupted
return
except FileNotFoundError:
showWarning(
_(