-
Notifications
You must be signed in to change notification settings - Fork 68
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Stuck in threading on macOS #61
Comments
The semaphore is most likely working alright. That is part of python and that is probably well tested even on osx. The problem is most likely in the event handling. I'm a bit challenged here since I've never used osx and I don't have any osx machines available. Could you try running your script in gdb to get a stack trace of the event handler thread? See this comment for instructions. |
The bouncing launcher you mentioned might be evidence that libmpv tried to create a window, but for some reason did not succeed. This sounds vaguely similar to what @Shu-Ji describes in this comment. Could you try the workaround described there (manually creating a PyQT window and passing the window ID to mpv)? |
Thanks for your suggestions, I agree that a bug in Python's Event Handler Stack Trace
[New Thread 0x2703 of process 1513]
warning: unhandled dyld version (15)
[New Thread 0x1a03 of process 1513]
Thread 3 received signal SIGTRAP, Trace/breakpoint trap.
[Switching to Thread 0x1a03 of process 1513]
0x000000010000519c in ?? () I will try again when I have more time. Manual PyQT windowIs there a way of doing this with your python-mpv version? |
You can use the linked pyqt example almost unmodified: #!/usr/bin/env python3
import mpv
import time
import os
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class Test(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.container = QWidget(self)
self.setCentralWidget(self.container)
self.container.setAttribute(Qt.WA_DontCreateNativeAncestors)
self.container.setAttribute(Qt.WA_NativeWindow)
player = mpv.MPV(wid=str(int(self.container.winId())),
vo='x11',
log_handler=print,
loglevel='debug')
player.play('test.webm')
app = QApplication(sys.argv)
import locale
locale.setlocale(locale.LC_NUMERIC, 'C')
win = Test()
win.show()
sys.exit(app.exec_()) You may not need the |
Neat, your example works nicely and plays the video on macOS for me (after removing How is the Do you think that python-mpv should take care of this |
Yep, wid is effectively passed in as a command line option. See also this upstream example. According to the README of the upstream examples a caveat seems to be that this way of embedding is not very stable, so you might also try embedding via OpenGL instead. The callbacks are supported by python-mpv. Upstream issue #556 gives some detail on mpv GUI initialization on OSX, though it still doesn't explain the hang observed here. I would like to avoid putting PyQT-specific code into python-mpv. Since getting that up and running is very simple (3loc) I'd keep it in the README examples for now. For now, I think two things to add there are a) an OpenGL callback example and b) a hint to OSX users to create their own window. As for proper debugging, yes, that would be great. However, I don't have an Apple machine so you or one of the other OSX users would have to do most of that. And given what upstream issue #556 says we might well find out that maybe on OSX embedding is only supported if you bring your own window. |
I agree, adding PyQT-specific code to handle macOS-specific problems does not seem sensible in this scenario. Assuming that I want to use python-mpv's [..]
def foo():
app = QApplication(sys.argv)
import locale
locale.setlocale(locale.LC_NUMERIC, 'C')
win = Test()
win.show()
sys.exit(app.exec_())
threading.Thread(target=foo).start() This however crashes: 2017-12-29 12:29:13.231 Python[21946:737607] *** Assertion failure in +[NSUndoManager _endTopLevelGroupings], /BuildRoot/Library/Caches/com.apple.xbs/Sources/Foundation/Foundation-1450.16/Foundation/Misc.subproj/NSUndoManager.m:361
2017-12-29 12:29:13.246 Python[21946:737607] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '+[NSUndoManager(NSInternal) _endTopLevelGroupings] is only safe to invoke on the main thread.' with the main point probably being: Do you have a suggestion for this, or is it again some macOS-specific difficulty? |
According to the Qt doc, Qt must run on the application's main thread. So this is not a limitation of python-mpv, mpv or even PyQt. I know this is inconvenient, but the best solution to this is probably to move other python work to auxiliary threads and possibly use PyQts Qt threading bindings. A more low-effort alternative would be to farm out Qt code to a separate process using the multiprocessing module. It sounds like that would be quite easy to implement in your use case. |
I can indeed display the video using an extra The simplest code (which abuses concurrency quite heavily) I could come up with to achieve this (in theory), is this: import sys
import multiprocessing
from PyQt5.QtWidgets import QWidget, QMainWindow, QApplication
from PyQt5.QtCore import Qt
import mpv
class Test(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.container = QWidget(self)
self.setCentralWidget(self.container)
self.container.setAttribute(Qt.WA_DontCreateNativeAncestors)
self.container.setAttribute(Qt.WA_NativeWindow)
self.window_id = str(int(self.container.winId()))
def foo(player):
app = QApplication(sys.argv)
win = Test()
win.show()
mpv._mpv_set_option_string(player, 'wid', win.window_id)
sys.exit(app.exec_())
player = mpv.MPV()
multiprocessing.Process(target=foo, args=(player,)).start()
player.play('test.webm') Unfortunately, it crashes: objc[61728]: +[__NSPlaceholderDate initialize] may have been in progress in another thread when fork() was called.
objc[61728]: +[__NSPlaceholderDate initialize] may have been in progress in another thread when fork() was called. We cannot safely call it or ignore it in the fork() child process. Crashing instead. Set a breakpoint on objc_initializeAfterForkError to debug. Playing around with arbitrary |
Yes. You cannot create the mpv object in the parent process, since it initializes the mpv handle. When using multiprocessing, import mpv and create the MPV object in the child process, then pass commands to the child process either low-level using multiprocessing's pipes and queues or high-level using multiprocessing's managers and proxies. |
Uuuff. This special treatment of macOS starts to become annoying :-P Consider this thread-inside-a-process solution: import sys
import time
import threading
import multiprocessing
from PyQt5.QtWidgets import QWidget, QMainWindow, QApplication
from PyQt5.QtCore import Qt
class MPVProxy:
def __init__(self):
# setup MPV
self.pipe = multiprocessing.Pipe()
multiprocessing.Process(
target=self._run, args=(self.pipe,)).start()
def __getattr__(self, cmd):
def wrapper(*args, **kwargs):
output_p, input_p = self.pipe
input_p.send((cmd, args, kwargs))
return wrapper
def _run(self, pipe):
class Test(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.container = QWidget(self)
self.setCentralWidget(self.container)
self.container.setAttribute(Qt.WA_DontCreateNativeAncestors)
self.container.setAttribute(Qt.WA_NativeWindow)
self.window_id = str(int(self.container.winId()))
# setup QT window
app = QApplication(sys.argv)
win = Test()
win.show()
# initialize MPV
import mpv
player = mpv.MPV()
mpv._mpv_set_option_string(
player.handle,
'wid'.encode('utf-8'), win.window_id.encode('utf-8'))
# poll pipe
def handle_pipe():
output_p, input_p = pipe
while True:
try:
msg = output_p.recv()
cmd, args, kwargs = msg
try:
func = getattr(player, cmd)
except AttributeError:
print(f'Invalid command "{cmd}"')
continue
func(*args, **kwargs)
except EOFError:
break
threading.Thread(target=handle_pipe).start()
# run QT main-loop
sys.exit(app.exec_())
mp = MPVProxy()
time.sleep(2)
mp.non_existing_function()
time.sleep(2)
mp.play('test.webm') It waits 2 seconds, prints 'Invalid command "non_existing_function"', waits another 2 seconds and then plays the movie. I had to use the thread inside of the process, in order to poll the pipe and run the QT main-loop at the same time. |
Sorry for not actually improving your code, but I just played around a bit and came up with the solution below. So far I tested it on Linux and it works fine there. The main advantage of that is that it's rather simple and it provides access to most methods on an #!/usr/bin/env python3
import mpv
import time
import os
import sys
import multiprocessing
from multiprocessing.managers import BaseManager, BaseProxy
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class MpvManager(BaseManager):
pass
MpvManager.register('MPV', mpv.MPV,
exposed=[
'play',
'_get_property',
'_set_property'
])
class MpvWindowThing(QMainWindow):
def __init__(self, manager, parent=None):
super().__init__(parent)
self.container = QWidget(self)
self.setCentralWidget(self.container)
self.container.setAttribute(Qt.WA_DontCreateNativeAncestors)
self.container.setAttribute(Qt.WA_NativeWindow)
print('Running as pid', os.getpid())
player = manager.MPV(wid=str(int(self.container.winId())),
vo='x11', # You may not need this
log_handler=print,
loglevel='debug')
player._set_property('loop', 'inf')
player.play('test.webm')
with MpvManager() as manager:
app = QApplication(sys.argv)
win = MpvWindowThing(manager)
win.show()
sys.exit(app.exec_())
sys.exit(0) |
On macOS there seems to be a problem with Furthermore, everything in your code happens in the main-process, right? |
Oh my. No, what happens is I'm sorry I don't have any better suggestions. All ways I could think of to replace your custom I guess your |
I see. I now ended up with this implementation. It's not as flexible as I want it to be (e.g. attribute setting doesn't work properly yet, setting time-observers fails as lambdas cannot be pickled, ...), but it's a start. |
Looks like you guys are having some issues with multiprocessing. May I suggest using my library, zproc? Will try to cook something up myself. P.S. Awesome library! |
If anyone is looking for a workaround to this, my external MPV library works on OSX. It implements a decent amount of the API of |
Hi, is there any new on this topic or an workaround? The PyQt solution is not really an option for me. |
@iwalton3 I actually gave your library a shot, and got some success. You may or may not find this interesting. To make a long story short, I had to shove the |
This issue has been bugging me so much that I have opened a $250 bounty for anyone who solves this (in a way that ends up also resolving the issue for hydrusnetwork/hydrus#1132, i.e., gets an embedded player working in a FWIW, SMPlayer is a Qt based app that embeds MPV, apparently. I don't know what they do differently from python-mpv to make it work, but it's probably worth referencing their code: https://github.com/smplayer-dev/smplayer/blob/master/src/mplayerwindow.cpp |
@roachcord3 I'm very glad you opened this bounty because I've been stuck with this issue for a while now. |
Hey, this is intriguing and something I'd be happy to look into, but first I'd need to know if a: this is still an issue (it's been two years) and b: if the bounty is still active. |
Yes, it's still an issue. However, I don't know whether that boss bounty system still works. I logged into my dashboard and see my bounties are still active, but the claim link shows that they are having some error. So, you might want to contact the owner of boss bounty about it if the error persists. |
The original poster's example from Dec 2017 still doesn't work so, yes, this is still an issue. A fix that gets the example to work (or an example that's simpler than the QT approach) would be greatly appreciated. Thanks for looking into it. I could probably toss USD10 or something into a pot if you need a monetary motivation. |
From my end I can promise credit and thanks in the readme to whoever manages to solve this. I still don't have a mac, so I still can't work on this myself. |
Hello,
consider the following code:
On Linux, this will print
waiting
, then playtest.webm
, and finally printdone
as expected.On macOS 10.12.6 using Python 3.6 and MPV 0.27 however, it only prints
waiting
and is then stuck forever while the Python-Rocket starts jumping in my Dock.I looked into python-mpv's code, and for
mpv.wait_for_playback()
it is stuck in line 557, which is:Using
mpv.wait_for_property('filename', lambda x: x is None)
instead, makes it stuck in line 569, which is:The problem thus seems to somehow be related to the handling of the
threading
library.Do you have any suggestion what might be causing this?
This is also possibly a duplicate of #59.
The text was updated successfully, but these errors were encountered: