Comments (23)
@roachcord3 I'm very glad you opened this bounty because I've been stuck with this issue for a while now.
from python-mpv.
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.
from python-mpv.
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.
from python-mpv.
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)?
from python-mpv.
Thanks for your suggestions, I agree that a bug in Python's threading
is highly unlikely.
Event Handler Stack Trace
- I am using mpv 0.27 and libmpv (1, 25).
- Adding
mpv['vo'] = 'opengl'
to my previous example does not alter its behavior. - I can currently not run GDB due to weird macOS-related problems after gdb-codesigning (Homebrew/homebrew-core#20047), and simply get
[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 window
Is there a way of doing this with your python-mpv version?
from python-mpv.
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 vo='x11'
. The setlocale is necessary since pyqt apparently stomps all over that on load. This could also be solved by just import pyqt before mpv (which already includes that line).
from python-mpv.
Neat, your example works nicely and plays the video on macOS for me (after removing vo='x11',
).
How is the wid
argument handled in python-mpv? It seems to be part of extra_mpv_opts
, which is then somehow processed in _mpv_set_option_string.
Do you think that python-mpv should take care of this QWidget
-creation itself, in order to provide a fix for macOS?
Or one could try to debug this even further, using gdb.
from python-mpv.
I agree, adding PyQT-specific code to handle macOS-specific problems does not seem sensible in this scenario.
For now I'll try to use this work-around instead of diving into more in-depth debugging craziness.
Assuming that I want to use python-mpv's MPV
object in another application (for me that would be here), i.e. I don't want the PyQT event loop blocking my main thread.
What would be the best way of handling this case? At first, I thought about simply starting it in another thread (and then somehow try to access MPV
's loadfile
, etc. methods):
[..]
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: is only safe to invoke on the main thread
.
Do you have a suggestion for this, or is it again some macOS-specific difficulty?
from python-mpv.
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.
from python-mpv.
I can indeed display the video using an extra multiprocessing
process.
The problem then is however, that I still need to access the MPV object in the main process.
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 time.sleep
calls or using multiprocessing.Manager
has not helped me so far.
Do you have a suggestion?
from python-mpv.
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.
from python-mpv.
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.
Although this (I think) perfectly mirrors the interface of MPV
, this solutions seems rather non-optimal to me. Would you have a suggestion how to improve it?
from python-mpv.
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 MPV
instance by just adding them to the exposed
array. The main disadvantages are that all the magic functions taking callables as well as direct property access don't work. Property access must be emulated with player._set_property('loop', 'inf')
. You could build your own multiprocessing.BaseProxy
descendant improving that interface, but I don't think that'd be worth it.
#!/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)
from python-mpv.
On macOS there seems to be a problem with manager.MPV
.
It gets stuck on v vo/opengl Initializing OpenGL backend 'cocoa'
and never plays the video.
If I replace it with mpv.MPV
everything works as expected.
Furthermore, everything in your code happens in the main-process, right?
from python-mpv.
Oh my. No, what happens is multiprocessing.manager.BaseManager
creates a proxy thing for MPV
such that when you do manager.MPV
you get a proxy for a MPV
object living inside a subprocess. This way you have the PyQT process running in the parent process and mpv running in the subprocess. I have no idea why that doesn't work on mac.
I'm sorry I don't have any better suggestions. All ways I could think of to replace your custom multiprocessing.Pipe
logic with the autogenerated proxies from multirprocessing.managers.BaseManager
are ugly as sin as multiprocessing
really does not have a very good or flexible API. I'd definitely go for your way now that I've had a read through multiprocessing's source.
I guess your MPVProxy
really is fine. I'd maybe factor out the receive loop into another class. 50loc for that sort of workaround is ok I guess.
from python-mpv.
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.
from python-mpv.
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!
from python-mpv.
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 python-mpv
, but it probably isn't a drop-in replacement. (It was originally implemented to allow MPV support on platforms where getting a build of libmpv1 is a pain.)
from python-mpv.
Hi, is there any new on this topic or an workaround? The PyQt solution is not really an option for me.
from python-mpv.
@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 MpvEventID
class from python-mpv
into your imported library, and then it worked, for the most part. There were some BrokenPipeError
s and TypeError
s but it mostly worked (though, the intended experience of the Hydrus client is for the player to be embedded in Hydrus' own media viewer window, rather than have the player open as its own separate program. Still, it's progress.) Full writeup here: hydrusnetwork/hydrus#1132 (comment)
from python-mpv.
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 QWidget
.)
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
from python-mpv.
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
QWidget
.)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
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.
from python-mpv.
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.
from python-mpv.
Related Issues (20)
- ctypes.util.find_library() Fails to find Windows libmpv-2.dll HOT 1
- Cannot load mpv.config (Windows) HOT 5
- macOS: libmpv cannot get a VO. HOT 11
- Gapless playback not always working HOT 2
- python-mpv not finding libmpv DLL on MSYS2 HOT 7
- no-border not working as intended HOT 1
- tests/test_mpv.py::TestLifecycle::test_wait_for_event_shutdown hangs HOT 1
- Audio controls
- I do not literally know which DLLs does it need to consume HOT 2
- How to directly play audio bytes? HOT 2
- "Cannot find libmpv in the usual places" on Arch Linux, Hypnotix HOT 2
- How to replace mpv.MpvEventID.METADATA_UPDATE ? HOT 2
- Loadfile command broken by API change in mpv v0.38.0 HOT 5
- The PyPI package is not up to date HOT 3
- playlist_next() and playlist_prev() beyond end of playlist causes SystemError HOT 1
- [QUESTION] How do I add custom scripts? HOT 2
- QOpenGLWidget is displayed in its own window since ibmpv.so.2.3.0 HOT 3
- SyntaxError on Mac os with python3 on v1.0.6 HOT 2
- Unable to set Gapless Playback option HOT 3
- Is it possible to read music files from an USB key or HD ? HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from python-mpv.