jaseg / python-mpv

Python interface to the awesome mpv media player
https://git.jaseg.de/python-mpv.git
Other
547 stars 68 forks source link

threading-related crash after multiple video playbacks #88

Open r41d opened 5 years ago

r41d commented 5 years ago

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.

McSinyx commented 5 years ago

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.

r41d commented 5 years ago

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...

McSinyx commented 5 years ago

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?

r41d commented 5 years ago

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?

McSinyx commented 5 years ago

You can try to cycle the video property either through the API or by the binding _.

jaseg commented 4 years ago

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.