Open dfaker opened 4 years ago
Inspecting the output of _event_generator in this scenario shows that that final event generated is a 22 property change, event 1 for shutdown never makes it through the pipe and no NONE 0 event is sent either, the grim_reaper will therefore I think wait forever blocking exit.
If it is seemingly an issue that the shutdown command is never sent at least giving an option to pass daemonic_reaper to terminate as terminate(daemonic_reaper=True)
would seem to make sense so that the reaper thread doesn't block exit when we know we don't care about it.
There were some issues around multithreading and terminate()
. I re-structured the code somewhat and now things should generally work much more reliably than before. I tried your first example and it seemed to work as expected after replacint terminate()
with quit()
. Here's my test code:
import tkinter as tk
import mpv
root=tk.Tk()
player = mpv.MPV(wid=str(int(root.winfo_id())), vo='x11')
player.play('test.webm')
def handlePropertyChange(name,value):
print('property change',name,value)
root.title(str(value))
player.observe_property('time-pos', handlePropertyChange)
tk.mainloop()
print('calling player.terminate.')
player.quit()
print('terminate player.returned.')
When using terminate()
and closing the TK window while mpv is still playing, it seems either mpv or the tk mainloop try to do some teardown work on the X11 window that was already done by the other and some x11 lib barfs with an error message:
calling player.terminate.
X Error of failed request: BadWindow (invalid Window parameter)
Major opcode of failed request: 2 (X_ChangeWindowAttributes)
Resource id in failed request: 0x1200002
Serial number of failed request: 144
Current serial number in output stream: 149
I'm not sure where that is coming from. If you can, just calling quit()
and exiting the program should already shut down everything cleanly enough and should be a decent workaround.
Thanks! I'll pull these and report back.
I had the same issue in linux (Ubuntu 20.04) and pyqt5. Terminating mpv player from another thread solved my problem.
class TerminateMPV(QRunnable):
def __init__(self,player):
super().__init__()
self.player: mpv.MPV = player
def run(self) -> None:
self.player.terminate()
Main thread (which mpv player is defined and started in):
...
def on_stop():
self.player.stop()
self.threadpool = QThreadPool()
terminate_mpv_thread = TerminateMPV(self.player)
self.threadpool.start(terminate_mpv_thread)
...
@m44soroush I'm glad that your workaround functions, but it would be interesting to see why this happened. Do you think you could get a stack trace of the hanging code? Python should spit out a stack trace if you just Ctrl+C, and you can get a stack trace of mpv's threads by running python inside gdb via gdb --args python3 the_script.py
, then Ctrl+C'ing the hanging program, then running thread apply all bt
in the gdb shell that opens. Alternatively, if you could post a hanging testcase I could reproduce the issue myself.
Reproducible on windows 10 with python-mpv 0.4.6 and mpv-dev-x86_64-20200426-git-640db1e, the last print statement in this snippet is never reached:
The same behaviour isn't noted if
osd-width
orduration
is observed, additionally placing aplayer.unobserve_property('time-pos', handlePropertyChange)
directly before player.terminate() has no effect, however removing it ahead of time,presumably giving enough time for the event queue to empty does seem to work:Does the event loop stop working when the frame with the wid passed to mpv is destroyed?