Wrong video size on MacOS
Hello,
When using this lib on MacOS with the simplified code below, inspired from examples/tkvlc.py, the size of the video is not adapted to the size of the window. If the video is too large for the window, it is cropped, if it is too small, it stands in the left bottom corner and is surrounded by black. The size of the video is updated only when the window is resized. Here is a video capture of the problem.
import os
import platform
import sys
import tkinter
from ctypes import c_void_p, cdll
from threading import Thread
import vlc
system = platform.system()
if system == "Darwin":
# find the accurate Tk lib for Mac
libtk = "libtk%s.dylib" % (tkinter.TkVersion,)
if "TK_LIBRARY_PATH" in os.environ:
libtk = os.path.join(os.environ["TK_LIBRARY_PATH"], libtk)
else:
prefix = getattr(sys, "base_prefix", sys.prefix)
libtk = os.path.join(prefix, "lib", libtk)
dylib = cdll.LoadLibrary(libtk)
_GetNSView = dylib.TkMacOSXGetRootControl
_GetNSView.restype = c_void_p
_GetNSView.argtypes = (c_void_p,)
del dylib
class Window(tkinter.Tk):
def register(self, player):
id = self.winfo_id()
print(id)
if system == "Darwin":
player.set_nsobject(_GetNSView(id))
elif system == "Linux":
player.set_xwindow(id)
elif system == "Windows":
player.set_hwnd(id)
def play(instance, player, path):
media = instance.media_new_path(path)
player.set_media(media)
player.play()
if __name__ == "__main__":
instance = vlc.Instance()
player = instance.media_player_new()
window = Window()
window.register(player)
thread = Thread(target=play, args=(instance, player, sys.argv[1]))
thread.start()
window.mainloop()
This does not occur on Linux or on Window, and occurs with either Tkinter or PyCocoa. I guess the problem stands on the Python-VLC or the VLC side. I tried several instance parameters:
--(no-)embedded-video: does not change anything;--(no-)autoscale: centers the video in the black space, but the video is not resized anymore (makes sense); and--(no-)macosx-video-autoresize: is not recognized.
The Window class needs to handle some Tk events: at least add a OnResize method. Following is a modified Window class. This may not be sufficient, more may be needed.
class Window(tkinter.Tk):
_geometry = ''
def register(self, player):
wid = self.winfo_id()
print(wid)
if system == "Darwin":
player.set_nsobject(_GetNSView(wid))
self.player = player
self.after(200, self.OnResize)
elif system == "Linux":
player.set_xwindow(wid)
elif system == "Windows":
player.set_hwnd(wid)
def OnResize(self, *unused):
"""Adjust the window/frame to the video aspect ratio.
"""
g = self.geometry()
if g != self._geometry and self.player:
u, v = self.player.video_get_size() # often (0, 0)
if v > 0 and u > 0:
# get window size and position
g, x, y = g.split('+')
w, h = g.split('x')
# alternatively, use .winfo_...
# w = self.winfo_width()
# h = self.winfo_height()
# x = self.winfo_x()
# y = self.winfo_y()
# use the video aspect ratio ...
if u > v: # ... for landscape
# adjust the window height
h = round(float(w) * v / u)
else: # ... for portrait
# adjust the window width
w = round(float(h) * u / v)
self.geometry("%sx%s+%s+%s" % (w, h, x, y))
self._geometry = self.geometry() # actual
I saw this "trick" in examples/tkvlc.py, to force resize the window, and thus force the upscale. I don't really need to keep the video aspect ratio, so I tried to fool VLC by setting the same window geometry self.geometry(self.geometry()), but it didn't work.
This leaves me with two questions, however:
- why this behavior on Mac only?
- does it work when the player is set fullscreen from the start? E.g.:
instance = vlc.Instance() player = instance.media_player_new() player.set_fullscreen(True) window = Window() window.register(player) thread = Thread(target=play, args=(instance, player, sys.argv[1])) thread.start()
Several things changed on macOS, the past few years. To make this issue work like it does on other systems, Tcl/Tk needs some updates for macOS.
Another example is the _ GetNSView kludge to get to the Drawable View. Tcl/Tk should make TkMacOSXDrawableView public.
Conversely, VLC could provide those things consistently across all platforms it supports.
PS) Setting the geometry to the cuurent geometry is a NOP. Try setting the geometry to something else, like the size of the video or half of that.
PPS) Try making the window a subclass of Tk.Frame instead of Tk.
PPPS) Another problem may be running the video in a thread. That is OK for playing the video, but not for events like key strokes, window resizing, window closing, etc.
I see. So there a possibility this problem will be solved in the long run.
From what I have read from Tk source code, TkMacOSXGetRootControl takes as argument (and Tk.winfo_id() returns) the pointer of the window itself, which is, in my humble experience, very low-level for a Python program.
PS) Setting the geometry to the cuurent geometry is a NOP. Try setting the geometry to something else, like the size of the video or half of that.
I was feeling it'd be too easy.
PPS) Try making the window a subclass of Tk.Frame instead of Tk.
What would it change? My program has a unique window.
PPPS) Another problem may be running the video in a thread. That is OK for playing the video, but not for events like key strokes, window resizing, window closing, etc.
With Cocoa, it seems the window must run in the main thread on Mac. I get systematic errors otherwise.
The point was, if the OnResize method is not getting called, some other changes may be needed. Two examples were the PPS and PPPS above. There may be more or other things.
To avoid those black areas, just add another self.after(200, self.OnResize) call at the end of the OnResize method. That will work in any thread.