python-mpv
python-mpv copied to clipboard
threading-related crash after multiple video playbacks
I have a program where mpv is called multiple times, like, every minute, but the previous playback is always done before a new one is started. After about 16 times I get the following crash:
Fatal Python error: PyEval_SaveThread: NULL tstate
Thread 0x00007f343a012700 (most recent call first):
File "/home/lbuhl/.local/lib/python3.7/site-packages/mpv.py", line 445 in _event_generator
File "/home/lbuhl/.local/lib/python3.7/site-packages/mpv.py", line 452 in _event_loop
File "/usr/lib/python3.7/threading.py", line 865 in run
File "/usr/lib/python3.7/threading.py", line 917 in _bootstrap_inner
File "/usr/lib/python3.7/threading.py", line 885 in _bootstrap
Thread 0x00007f346ce63740 (most recent call first):
File "./main.py", line 239 in run
File "./main.py", line 281 in <module>
Aborted (core dumped)
Unfortunately I have no idea how to deal with this crash :/
The code that invokes mpv looks like this (part of a game made with pygame):
def play_demo_video(self, video_file_name):
video_player = mpv.MPV(input_default_bindings=True, input_vo_keyboard=True)
# When the video plays we want to disable input for the game
self._disable_input_for_video = True
def cleanup_mpv():
# Clean up the helpers
video_player.terminate()
self._disable_input_for_video = False
self.get_screen()._last_action_timestamp = datetime.now()
# make pygame grab the focus again
subprocess.call(["wmctrl", "-a", PROGRAM_NAME])
# This ends MPV and sets up the game back to its normal state
@video_player.on_key_press('q')
def my_q_binding():
cleanup_mpv()
@video_player.event_callback('END_FILE')
def video_finished_handler(event):
cleanup_mpv()
video_player.fullscreen = True
video_player['vo'] = 'gpu'
video_player.play(video_file_name)
Any advice would be appreciated.
Do you really need to create another MPV instance every call? If that is really needed, try video_player.quit() at the end of the function.
I tried out a bit and for me this works best:
def play_demo_video(self, video_file_name):
video_player = mpv.MPV(input_default_bindings=True, input_vo_keyboard=True)
# When the video plays we want to disable gamepad input for the game
self._disable_input_for_video = True
# In order to quit the mpv on any button press, we use "antimicro" to map controller input to the q button
# on demand and exit mpv that way. Don't know if there's a better option or not
antimicro = subprocess.Popen(["antimicro", "-d", "--hidden", "--profile", "controller_q_map.gamecontroller.amgp"])
def cleanup_mpv():
# Clean up the helpers
self._disable_input_for_video = False
self.get_screen()._last_action_timestamp = datetime.now()
# make pygame grab the focus again
subprocess.call(["wmctrl", "-a", PROGRAM_NAME])
@video_player.on_key_press('q')
def my_q_binding():
video_player.quit()
cleanup_mpv()
@video_player.event_callback('END_FILE')
def video_finished_handler(event):
video_player.terminate()
cleanup_mpv()
...
With the quit for when we press q and the terminate for when the file ended. Most other combinations lead to Core Dump crashes...
If you use mpv to play videos (since I assume normal human cannot watch more than one video at the same time), why don't you keep an MPV instance as a variable in the scope of the game that call it, or a attribute of the class if you're doing OO?
I want to go back to the game when no video is playing. For this I would need the MPV window to go away without shutting down the mpv instance. How would I achieve that?
You can try to cycle the video property either through the API or by the binding _.
What might work is providing mpv with a window to draw into using the --wid option. As far as I know that "window" can be a GUI component such as a panel that is part of another window on most platforms and GUI frameworks. Perhaps it is then possible to hide/show that GUI component, thereby hiding/showing the mpv window. The PyQt embedding example might be a good starting point for experiments here.