[vlc-commits] [Git][videolan/vlc][master] 17 commits: caopengllayer: replace sys->embed by vd->cfg->window

Jean-Baptiste Kempf (@jbk) gitlab at videolan.org
Thu Apr 28 09:11:51 UTC 2022



Jean-Baptiste Kempf pushed to branch master at VideoLAN / VLC


Commits:
cdd8a75f by Alexandre Janniaux at 2022-04-28T08:08:15+00:00
caopengllayer: replace sys->embed by vd->cfg->window

sys->embed was used to store the window created from the display in
pre-3.0 design, but now the display is created after the window and the
window is available in vd->cfg->window.

The sys->embed was storing the same pointer in the normal case, but
ironically, it was undefined in the libvlc embedding case and was
leading to crash.

- - - - -
fee7cff9 by Marvin Scholz at 2022-04-28T08:08:15+00:00
vout/caopengllayer: remove HDR support

This is anyway not properly supported currently so opting in to it
here does not change anything for the better.

Forward port from 3.0.x branch.
Cherry-picked from commit 4591255d8e72c24d213e2071716cd4dec159f049.

Signed-off-by: Alexandre Janniaux <ajanni at videolabs.io>

- - - - -
48c59025 by Marvin Scholz at 2022-04-28T08:08:15+00:00
vout/caopengllayer: fix crash on close

The CGL context was destroyed too early, so move around the
vout display removal to prevent using the already-gone context.

Forward port from 3.0.x branch.
Cherry-picked from commit 7d1e7f289deb0d9caa77053e83ba1f7b529bca1c.

Signed-off-by: Alexandre Janniaux <ajanni at videolabs.io>

- - - - -
e68145e4 by Marvin Scholz at 2022-04-28T08:08:15+00:00
vout/caopengllayer: move module descriptor

Forward port from 3.0.x branch.
Cherry-picked from commit 52ee032826a297febfc9efeb0602636762aca88a.

Signed-off-by: Alexandre Janniaux <ajanni at videolabs.io>

- - - - -
b7fadee3 by Marvin Scholz at 2022-04-28T08:08:15+00:00
vout/caopengllayer: rewrite most of the module

Rewrites most of the layer vout code to have the same features as the
view based vout. Additionally fixes laggy resizing, fixes CGL context
creation bugs, adds support for CI filters and fixes various memory
management errors.

The CAOpenGLLayer based API is special and different from all other APIs
provided on other OSes as it is not a push-model API but a pull one,
where the OS calls a specific method when a new frame should be rendered.
This makes integration into VLC relatively tricky and the code a bit
harder to follow.
While the API is a pull-model, we can kind of trick it by just forcing
a re-display of the layer in the vouts display function. With views this
would be forbidden as views are supposed to be accessed from the main
thread only, but with layers this is possible if some care is taken.
When forcing the layer to render from a different thread, the implicitly
created CATransaction has to be flushed explicitly, as we do not have a
main loop at the end of which it would be flushed.

We do not force rendering all the time though, as doing that would break
resize animations given that VLC can not know the right time when display
refresh will happen, so resizing would look laggy and have glitches, as
during a resize both the OS and VLC would drive the rendering of the
layer, resulting in unexpected result.
To prevent that, when live resizing starts (the user resizing by dragging
a windows corner), the layer is set into asynchronous rendering mode
which makes the OS drive the rendering loop completely not only for
drawing the resize change. While the layer is in asynchronous mode, we
ignore all update requests from the core, as the layer is anyway updated
continuously by the OS and forcing rendering from another thread would
lead to artifacts. Additionally while in live resize, we do not report
the size changes to the core, as the event takes too long to reach the
vout Control() function, resulting in the layer content being displayed
at the wrong (old) size. Instead we take the current viewport size
as the size and display using that.

Another unusual thing compared to other vouts is that the VLC OpenGL
display functions to update the viewport and aspect ratio are not
called in the Control event handling callback, thats because before
the render callback is called, the OS sets the OpenGL viewport to match
the layer backing store size. So setting it in the Control callback
is useless as it does not make any difference.

Forward-port from 3.0.x branch.
Cherry-picked from commit 879ca61f8c25af9f95da72ee0fc3366c2ffd1d47.

Signed-off-by: Alexandre Janniaux <ajanni at videolabs.io>

Noticeable changes:
 - vlc_object_release has been changed into vlc_object_delete

- - - - -
93c12089 by Marvin Scholz at 2022-04-28T08:08:15+00:00
vout/caopengllayer: fix build with macOS SDKs < 10.14

Forward-port from 3.0.x branch.
Cherry-picked from commit baf1b28219f9803a68a384b6fbdb2975c01dbd8b.

Signed-off-by: Alexandre Janniaux <ajanni at videolabs.io>

- - - - -
489e62d1 by Marvin Scholz at 2022-04-28T08:08:15+00:00
vout/caopengllayer: fix scaling when changing displays

Changing from a retina screen to a non-retina screen changes the
scale of the layer, so from point of view of the OpenGL code, the
size of the surface changes. Therefore we need to signal this change
so that the size is adapted correctly.

This is not a perfect solution as the size event will arrive too late
so for a few frames the wrong size can still be seen before it switches
to the new size. Ideally this would be handled like the live resize
but so far I have not found a good solution for how to do that.

Forward-port from 3.0.x branch.
Cherry-picked from commit 57401f907ca98bfd37e73577014642fd16c123bb.

Signed-off-by: Alexandre Janniaux <ajanni at videolabs.io>

- - - - -
9fe5c444 by Marvin Scholz at 2022-04-28T08:08:15+00:00
vout/caopengllayer: Fallback to legacy vout on < 10.14

Forward-port from 3.0.x branch.
Cherry-picked from commit 2272cd45c0b5bde35388399c838d29bd78e018be.

Signed-off-by: Alexandre Janniaux <ajanni at videolabs.io>

- - - - -
7a659c60 by David Fuhrmann at 2022-04-28T08:08:15+00:00
caopengllayer: Forward mouse events to other views as well

Other views might need the mouse events as well. Do the analog
logic as in the old macosx vout module.
Fixes steering of fullscreen panel.

fixes #24560

Signed-off-by: Marvin Scholz <epirat07 at gmail.com>

Forward-port from 3.0.x branch.
Cherry-picked from commit 4d98f418ee4e3ec40fec55e74b51f747356eda74.

Signed-off-by: Alexandre Janniaux <ajanni at videolabs.io>

- - - - -
ad5f35b1 by Marvin Scholz at 2022-04-28T08:08:15+00:00
caopengllayer: fix resize handling

use layoutSublayers instead of resizeWithOldSuperlayerSize: as the
resizeWithOldSuperlayerSize: method is not called when compiled with
10.13 SDK, possibly because then the layer is not in a full layer tree
so has no superlayer eventually. So using layoutSublayers feels more
correct to be called when a layout change occurs.

Fix #24559

Forward-port from 3.0.x branch.
Cherry-picked from commit 11591b11c06938f21b7b5fcfa24d9da37768f911.

Signed-off-by: Alexandre Janniaux <ajanni at videolabs.io>

- - - - -
a446af46 by Felix Paul Kühne at 2022-04-28T08:08:15+00:00
caopengllayer: fix moving window with mouse

This fixes #24646

Forward-port from 3.0.x branch.
Cherry-picked from commit 7c74e968f48879f615b43c1be9d555b78098773d.

Signed-off-by: Alexandre Janniaux <ajanni at videolabs.io>

- - - - -
ee1f1720 by Alexandre Janniaux at 2022-04-28T08:08:15+00:00
caopengllayer: remove sys->embed

We don't create an internal window anymore, so we can always use
vd->cfg->window where needed.

- - - - -
689b64b1 by Alexandre Janniaux at 2022-04-28T08:08:15+00:00
caopengllayer: inline place picture 

Store the current modified vout_display_cfg_t and inline the calls to
vout_display_PlacePicture.

Refs #26845
Refs #25264

- - - - -
bd24889a by Alexandre Janniaux at 2022-04-28T08:08:15+00:00
caopengllayer: ignore DISPLAY_SIZE controls

DISPLAY_SIZE controls are emitted by the windowing system to control the
display state, but the caopengllayer display module is handling its own
windowing state without window and doesn't need to react to those events.

Instead, store the new size into the internal vout_display_cfg_t and
avoid trying to report the size to itself.

Fixes #26845
Refs #25264

- - - - -
8de94040 by Alexandre Janniaux at 2022-04-28T08:08:15+00:00
caopengllayer: remove drawable code

The display currently requires an NSObject window which is will be
fetched from a drawable window module in next patch. Fetching the
drawable from the display is not necessary anymore.

- - - - -
514954e2 by Alexandre Janniaux at 2022-04-28T08:08:15+00:00
window_macosx: add embedded drawable submodule

Add an NSObject-drawable window implementation initializing the
vout_window properties from libvlc.

Fixes #26864

- - - - -
6bc300f3 by Marvin Scholz at 2022-04-28T08:08:15+00:00
macosx vouts: Adjust module priorities

This and the previous commit ensure that the new CAOpenGLLayer based
video output is used on macOS 10.14 and higher if the output selection
is set to automatic.

Forward-port from 3.0.x branch.
Cherry-picked from commit bbd1dca5dfb6c189db34831d0ca281e25672ed17.

Signed-off-by: Alexandre Janniaux <ajanni at videolabs.io>

- - - - -


3 changed files:

- modules/video_output/caopengllayer.m
- modules/video_output/macosx.m
- modules/video_output/window_macosx.m


Changes:

=====================================
modules/video_output/caopengllayer.m
=====================================
@@ -7,6 +7,8 @@
  *          Felix Paul Kühne <fkuehne at videolan dot org>
  *          Pierre d'Herbemont <pdherbemont at videolan dot org>
  *
+ * Some of the code is based on mpv's video_layer.swift by "der richter"
+ *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation; either version 2.1 of the License, or
@@ -34,83 +36,152 @@
 #include <vlc_plugin.h>
 #include <vlc_vout_display.h>
 #include <vlc_opengl.h>
+#include <vlc_atomic.h>
 
 #import <QuartzCore/QuartzCore.h>
 #import <Cocoa/Cocoa.h>
 #import <OpenGL/OpenGL.h>
-#import <dlfcn.h>               /* dlsym */
+#import <dlfcn.h>
 
 #include "opengl/renderer.h"
 #include "opengl/vout_helper.h"
 
-#define OSX_SIERRA_AND_HIGHER (NSAppKitVersionNumber >= 1485)
-
 /*****************************************************************************
  * Vout interface
  *****************************************************************************/
 static int Open(vout_display_t *vd, video_format_t *fmt, vlc_video_context *context);
 static void Close(vout_display_t *vd);
 
-vlc_module_begin()
-    set_description(N_("Core Animation OpenGL Layer (Mac OS X)"))
-    set_subcategory(SUBCAT_VIDEO_VOUT)
-    set_callback_display(Open, 0)
-
-    add_opengl_submodule_renderer()
-vlc_module_end()
-
 static void PictureRender   (vout_display_t *vd, picture_t *pic, subpicture_t *subpicture,
                              vlc_tick_t date);
 static void PictureDisplay  (vout_display_t *vd, picture_t *pic);
 static int Control          (vout_display_t *vd, int);
 
-static void *OurGetProcAddress (vlc_gl_t *gl, const char *name);
-static int OpenglLock         (vlc_gl_t *gl);
-static void OpenglUnlock       (vlc_gl_t *gl);
-static void OpenglSwap         (vlc_gl_t *gl);
-
- at protocol VLCCoreAnimationVideoLayerEmbedding <NSObject>
-- (void)addVoutLayer:(CALayer *)aLayer;
-- (void)removeVoutLayer:(CALayer *)aLayer;
-- (CGSize)currentOutputSize;
+/**
+ * Protocol declaration that drawable-nsobject should follow
+ */
+ at protocol VLCOpenGLVideoViewEmbedding <NSObject>
+- (void)addVoutSubview:(NSView *)view;
+- (void)removeVoutSubview:(NSView *)view;
 @end
 
+/**
+ * Layer subclass that handles OpenGL video rendering
+ */
 @interface VLCCAOpenGLLayer : CAOpenGLLayer
+{
+    NSLock *_displayLock;
+    vout_display_t *_voutDisplay; // All accesses to this must be @synchronized(self)
+                                  // unless you can be sure it won't be called in teardown
+    CGLContextObj _glContext;
+}
 
- at property (nonatomic, readwrite) vout_display_t* voutDisplay;
- at property (nonatomic, readwrite) CGLContextObj glContext;
-
+- (instancetype)initWithVoutDisplay:(vout_display_t *)vd;
+- (void)displayFromVout;
+- (void)reportCurrentLayerSize;
+- (void)reportCurrentLayerSizeWithScale:(CGFloat)scale;
+- (void)vlcClose;
 @end
 
+/**
+ * View subclass which is backed by a VLCCAOpenGLLayer
+ */
+#if __MAC_OS_X_VERSION_MAX_ALLOWED < 101400
+// macOS SDKs lower than 10.14 did not have a NSViewLayerContentScaleDelegate
+// protocol definition, but its not needed, it will work fine without it as the
+// delegate method even existed before, just not the protocol.
+ at interface VLCVideoLayerView : NSView <CALayerDelegate>
+#else
+ at interface VLCVideoLayerView : NSView <CALayerDelegate, NSViewLayerContentScaleDelegate>
+#endif
+{
+    vout_display_t *_vlc_vd; // All accesses to this must be @synchronized(self)
+}
+
+- (instancetype)initWithVoutDisplay:(vout_display_t *)vd;
+- (void)vlcClose;
+ at end
 
 typedef struct vout_display_sys_t {
 
-    CALayer <VLCCoreAnimationVideoLayerEmbedding> *container;
-    vout_window_t *embed;
-    VLCCAOpenGLLayer *cgLayer;
+    id<VLCOpenGLVideoViewEmbedding> container;
+
+    VLCVideoLayerView *videoView; // Layer-backed view that creates videoLayer
+    VLCCAOpenGLLayer *videoLayer; // Backing layer of videoView
 
     vlc_gl_t *gl;
     vout_display_opengl_t *vgl;
 
     vout_display_place_t place;
+    vout_display_cfg_t cfg;
 
     bool  b_frame_available;
+    atomic_bool is_ready;
 } vout_display_sys_t;
 
-struct gl_sys
+#pragma mark -
+#pragma mark OpenGL context helpers
+
+/**
+ * Create a new CGLContextObj for use by VLC
+ * This function may try various pixel formats until it finds a suitable/compatible
+ * one that works on the given hardware.
+ * \return CGLContextObj or NULL in case of error
+ */
+CGLContextObj vlc_CreateCGLContext()
 {
-    CGLContextObj locked_ctx;
-    VLCCAOpenGLLayer *cgLayer;
+    CGLError err;
+    GLint npix = 0;
+    CGLPixelFormatObj pix;
+    CGLContextObj ctx;
+
+    CGLPixelFormatAttribute attribs[12] = {
+        kCGLPFAAllowOfflineRenderers,
+        kCGLPFADoubleBuffer,
+        kCGLPFAAccelerated,
+        kCGLPFANoRecovery,
+        kCGLPFAColorSize, 24,
+        kCGLPFAAlphaSize, 8,
+        kCGLPFADepthSize, 24,
+        0, // If ever extending this list, adjust the offset below!
+        0
+    };
+
+    if (@available(macOS 10.8, *)) {
+        // Enable automatic graphics switching support, important on Macs
+        // with dedicated GPUs, as it allows to not always use the dedicated
+        // GPU which has more power consumption
+        attribs[10] = kCGLPFASupportsAutomaticGraphicsSwitching;
+    }
+
+    err = CGLChoosePixelFormat(attribs, &pix, &npix);
+    if (err != kCGLNoError || pix == NULL) {
+        return NULL;
+    }
+
+    err = CGLCreateContext(pix, NULL, &ctx);
+    if (err != kCGLNoError || ctx == NULL) {
+        return NULL;
+    }
+
+    CGLDestroyPixelFormat(pix);
+    return ctx;
+}
+
+struct vlc_gl_sys
+{
+    CGLContextObj cgl; // The CGL context managed by us
+    CGLContextObj cgl_prev; // The previously current CGL context, if any
 };
 
 static int SetViewpoint(vout_display_t *vd, const vlc_viewpoint_t *vp)
 {
     vout_display_sys_t *sys = vd->sys;
-    if (OpenglLock(sys->gl))
+    if (vlc_gl_MakeCurrent(sys->gl) != VLC_SUCCESS)
         return VLC_EGENERIC;
 
     int ret = vout_display_opengl_SetViewpoint(sys->vgl, vp);
-    OpenglUnlock(sys->gl);
+    vlc_gl_ReleaseCurrent(sys->gl);
     return ret;
 }
 
@@ -118,6 +189,91 @@ static const struct vlc_display_operations ops = {
     Close, PictureRender, PictureDisplay, Control, NULL, SetViewpoint,
 };
 
+/**
+ * Flush the OpenGL context
+ * In case of double-buffering swaps the back buffer with the front buffer.
+ * \note This function implicitly calls \c glFlush() before it returns.
+ */
+static void gl_cb_Swap(vlc_gl_t *vlc_gl)
+{
+    struct vlc_gl_sys *sys = vlc_gl->sys;
+    // Copies a double-buffered contexts back buffer to front buffer, calling
+    // glFlush before this is not needed and discouraged for performance reasons.
+    // An implicit glFlush happens before CGLFlushDrawable returns.
+    CGLFlushDrawable(sys->cgl);
+}
+
+/**
+ * Make the OpenGL context the current one
+ * Makes the CGL context the current context, if it is not already the current one,
+ * and locks it.
+ */
+static int gl_cb_MakeCurrent(vlc_gl_t *vlc_gl)
+{
+    CGLError err;
+    struct vlc_gl_sys *sys = vlc_gl->sys;
+
+    sys->cgl_prev = CGLGetCurrentContext();
+
+    if (sys->cgl_prev != sys->cgl) {
+        err = CGLSetCurrentContext(sys->cgl);
+        if (err != kCGLNoError) {
+            msg_Err(vlc_gl, "Failure setting current CGLContext: %s", CGLErrorString(err));
+            return VLC_EGENERIC;
+        }
+    }
+
+    err = CGLLockContext(sys->cgl);
+    if (err != kCGLNoError) {
+        msg_Err(vlc_gl, "Failure locking CGLContext: %s", CGLErrorString(err));
+        return VLC_EGENERIC;
+    }
+
+    return VLC_SUCCESS;
+}
+
+/**
+ * Make the OpenGL context no longer current one.
+ * Makes the previous context the current one and unlocks the CGL context.
+ */
+static void gl_cb_ReleaseCurrent(vlc_gl_t *vlc_gl)
+{
+    CGLError err;
+    struct vlc_gl_sys *sys = vlc_gl->sys;
+
+    assert(CGLGetCurrentContext() == sys->cgl);
+
+    err = CGLUnlockContext(sys->cgl);
+    if (err != kCGLNoError) {
+        msg_Err(vlc_gl, "Failure unlocking CGLContext: %s", CGLErrorString(err));
+        abort();
+    }
+
+    if (sys->cgl_prev != sys->cgl) {
+        err = CGLSetCurrentContext(sys->cgl_prev);
+        if (err != kCGLNoError) {
+            msg_Err(vlc_gl, "Failure restoring previous CGLContext: %s", CGLErrorString(err));
+            abort();
+        }
+    }
+
+    sys->cgl_prev = NULL;
+}
+
+/**
+ * Look up OpenGL symbols by name
+ */
+static void *gl_cb_GetProcAddress(vlc_gl_t *vlc_gl, const char *name)
+{
+    VLC_UNUSED(vlc_gl);
+
+    return dlsym(RTLD_DEFAULT, name);
+}
+
+
+#pragma mark -
+#pragma mark Module functions
+
 /*****************************************************************************
  * Open: This function allocates and initializes the OpenGL vout method.
  *****************************************************************************/
@@ -125,111 +281,125 @@ static int Open (vout_display_t *vd,
                  video_format_t *fmt, vlc_video_context *context)
 {
     vout_display_sys_t *sys;
-
     if (vd->cfg->window->type != VOUT_WINDOW_TYPE_NSOBJECT)
         return VLC_EGENERIC;
 
-    /* Allocate structure */
-    vd->sys = sys = calloc(1, sizeof(vout_display_sys_t));
-    if (sys == NULL)
-        return VLC_EGENERIC;
-
     @autoreleasepool {
-        id container = var_CreateGetAddress(vd, "drawable-nsobject");
-        if (!container) {
-            sys->embed = vd->cfg->window;
-            container = sys->embed->handle.nsobject;
+        vout_display_sys_t *sys;
+
+        vd->sys = sys = vlc_obj_calloc(vd, 1, sizeof(*sys));
+        if (sys == NULL)
+            return VLC_ENOMEM;
+
+        // Only use this video output on macOS 10.14 or higher
+        // currently, as it has some issues on at least macOS 10.7
+        // and the old NSView based output still works fine on old
+        // macOS versions.
+        if (@available(macOS 10.14, *)) {
+            // This is intentionally left empty, as the check
+            // can not be negated or combined with other conditions!
+        } else {
+            if (!vd->obj.force)
+                return VLC_EGENERIC;
+        }
 
-            if (!container) {
-                msg_Err(vd, "No drawable-nsobject found!");
-                goto bailout;
-            }
+        id container = vd->cfg->window->handle.nsobject;
+        if (!container) {
+            msg_Err(vd, "No drawable-nsobject found!");
+            goto error;
         }
 
-        /* store for later, released in Close() */
+        // Retain container, released in Close
         sys->container = [container retain];
-
-        [CATransaction begin];
-        sys->cgLayer = [[VLCCAOpenGLLayer alloc] init];
-        [sys->cgLayer setVoutDisplay:vd];
-
-        [sys->cgLayer performSelectorOnMainThread:@selector(display)
-                                       withObject:nil
-                                    waitUntilDone:YES];
-
-        if ([container respondsToSelector:@selector(addVoutLayer:)]) {
-            msg_Dbg(vd, "container implements implicit protocol");
-            [container addVoutLayer:sys->cgLayer];
-        } else if ([container respondsToSelector:@selector(addSublayer:)] ||
-                   [container isKindOfClass:[CALayer class]]) {
-            msg_Dbg(vd, "container doesn't implement implicit protocol, fallback mode used");
-            [container addSublayer:sys->cgLayer];
-        } else {
-            msg_Err(vd, "Provided NSObject container isn't compatible");
-            [sys->cgLayer release];
-            sys->cgLayer = nil;
-            [CATransaction commit];
-            goto bailout;
+    
+        // Create the CGL context
+        CGLContextObj cgl_ctx = vlc_CreateCGLContext();
+        if (cgl_ctx == NULL) {
+            msg_Err(vd, "Failure to create CGL context!");
+            goto error;
         }
-        [CATransaction commit];
-
-        if (!sys->cgLayer)
-            goto bailout;
 
-        if (![sys->cgLayer glContext])
-            msg_Warn(vd, "we might not have an OpenGL context yet");
-
-        /* Initialize common OpenGL video display */
+        // Create a pseudo-context object which provides needed callbacks
+        // for VLC to deal with the CGL context. Usually this should be done
+        // by a proper opengl provider module, but we do not have that currently.
         sys->gl = vlc_object_create(vd, sizeof(*sys->gl));
         if (unlikely(!sys->gl))
-            goto bailout;
-        sys->gl->make_current = OpenglLock;
-        sys->gl->release_current = OpenglUnlock;
-        sys->gl->swap = OpenglSwap;
-        sys->gl->get_proc_address = OurGetProcAddress;
+            goto error;
+
+        sys->gl->make_current = gl_cb_MakeCurrent;
+        sys->gl->release_current = gl_cb_ReleaseCurrent;
+        sys->gl->swap = gl_cb_Swap;
+        sys->gl->get_proc_address = gl_cb_GetProcAddress;
         sys->gl->api_type = VLC_OPENGL;
 
-        struct gl_sys *glsys = sys->gl->sys = malloc(sizeof(*glsys));
-        if (!sys->gl->sys)
-            goto bailout;
-        glsys->locked_ctx = NULL;
-        glsys->cgLayer = sys->cgLayer;
-
-        const vlc_fourcc_t *subpicture_chromas;
-        if (!OpenglLock(sys->gl)) {
-            sys->vgl = vout_display_opengl_New(fmt, &subpicture_chromas,
-                                               sys->gl, &vd->cfg->viewpoint, context);
-            OpenglUnlock(sys->gl);
-        } else
-            sys->vgl = NULL;
-        if (!sys->vgl) {
-            msg_Err(vd, "Error while initializing opengl display.");
-            goto bailout;
+        struct vlc_gl_sys *glsys = sys->gl->sys = malloc(sizeof(*glsys));
+        if (unlikely(!glsys)) {
+            Close(vd);
+            return VLC_ENOMEM;
         }
+        glsys->cgl = cgl_ctx;
+        glsys->cgl_prev = NULL;
+
+        dispatch_sync(dispatch_get_main_queue(), ^{
+            // Reverse vertical alignment as the GL tex are Y inverted
+           sys->cfg = *vd->cfg;
+           if (sys->cfg.align.vertical == VLC_VIDEO_ALIGN_TOP)
+               sys->cfg.align.vertical = VLC_VIDEO_ALIGN_BOTTOM;
+           else if (sys->cfg.align.vertical == VLC_VIDEO_ALIGN_BOTTOM)
+               sys->cfg.align.vertical = VLC_VIDEO_ALIGN_TOP;
+
+            // Create video view
+            sys->videoView = [[VLCVideoLayerView alloc] initWithVoutDisplay:vd];
+            sys->videoLayer = (VLCCAOpenGLLayer*)[[sys->videoView layer] retain];
+            // Add video view to container
+            if ([container respondsToSelector:@selector(addVoutSubview:)]) {
+                [container addVoutSubview:sys->videoView];
+            } else if ([container isKindOfClass:[NSView class]]) {
+                NSView *containerView = container;
+                [containerView addSubview:sys->videoView];
+                [sys->videoView setFrame:containerView.bounds];
+                [sys->videoLayer reportCurrentLayerSize];
+            } else {
+                [sys->videoView release];
+                [sys->videoLayer release];
+                sys->videoView = nil;
+                sys->videoLayer = nil;
+            }
 
-        /* setup vout display */
-        vd->info.subpicture_chromas = subpicture_chromas;
+            vout_display_PlacePicture(&sys->place, vd->source, &sys->cfg);
+        });
 
-        vd->ops = &ops;
+        if (sys->videoView == nil) {
+            msg_Err(vd,
+                    "Invalid drawable-nsobject object, must either be an NSView "
+                    "or comply with the VLCOpenGLVideoViewEmbedding protocol");
+            goto error;
+        }
 
-        if (OSX_SIERRA_AND_HIGHER) {
-            /* request our screen's HDR mode (introduced in OS X 10.11, but correctly supported in 10.12 only) */
-            if ([sys->cgLayer respondsToSelector:@selector(setWantsExtendedDynamicRangeContent:)]) {
-                [sys->cgLayer setWantsExtendedDynamicRangeContent:YES];
-            }
+
+        // Initialize OpenGL video display
+        const vlc_fourcc_t *spu_chromas;
+
+        if (vlc_gl_MakeCurrent(sys->gl))
+            goto error;
+
+        sys->vgl = vout_display_opengl_New(fmt, &spu_chromas, sys->gl,
+                                           &vd->cfg->viewpoint, context);
+        vlc_gl_ReleaseCurrent(sys->gl);
+
+        if (sys->vgl == NULL) {
+            msg_Err(vd, "Error while initializing OpenGL display");
+            goto error;
         }
 
-        /* setup initial state */
-        CGSize outputSize;
-        if ([container respondsToSelector:@selector(currentOutputSize)])
-            outputSize = [container currentOutputSize];
-        else
-            outputSize = [sys->container visibleRect].size;
-        vout_window_ReportSize(sys->embed, (int)outputSize.width, (int)outputSize.height);
+        vd->info.subpicture_chromas = spu_chromas;
+
+        vd->ops = &ops;
 
+        atomic_init(&sys->is_ready, false);
         return VLC_SUCCESS;
 
-    bailout:
+    error:
         Close(vd);
         return VLC_EGENERIC;
     }
@@ -239,37 +409,45 @@ static void Close(vout_display_t *vd)
 {
     vout_display_sys_t *sys = vd->sys;
 
-    if (sys->cgLayer) {
-        if ([sys->container respondsToSelector:@selector(removeVoutLayer:)])
-            [sys->container removeVoutLayer:sys->cgLayer];
-        else
-            [sys->cgLayer removeFromSuperlayer];
+    atomic_store(&sys->is_ready, false);
+    [sys->videoView vlcClose];
 
-        if ([sys->cgLayer glContext])
-            CGLReleaseContext([sys->cgLayer glContext]);
-
-        [sys->cgLayer release];
+    if (sys->vgl && !vlc_gl_MakeCurrent(sys->gl)) {
+        vout_display_opengl_Delete(sys->vgl);
+        vlc_gl_ReleaseCurrent(sys->gl);
     }
 
-    if (sys->container)
-        [sys->container release];
 
-    if (sys->vgl != NULL && !OpenglLock(sys->gl)) {
-        vout_display_opengl_Delete(sys->vgl);
-        OpenglUnlock(sys->gl);
-    }
+    if (sys->gl) {
+        struct vlc_gl_sys *glsys = sys->gl->sys;
 
-    if (sys->gl != NULL)
-    {
-        if (sys->gl->sys != NULL)
-        {
-            assert(((struct gl_sys *)sys->gl->sys)->locked_ctx == NULL);
-            free(sys->gl->sys);
-        }
+        // It should never happen that the context is destroyed and we
+        // still have a previous context set, as it would mean non-balanced
+        // calls to MakeCurrent/ReleaseCurrent.
+        assert(glsys->cgl_prev == NULL);
+
+        CGLReleaseContext(glsys->cgl);
         vlc_object_delete(sys->gl);
+        free(glsys);
     }
 
-    free(sys);
+    // Copy pointers out of sys, as sys can be gone already
+    // when the dispatch_async block is run!
+    id container = sys->container;
+    VLCVideoLayerView *videoView = sys->videoView;
+    VLCCAOpenGLLayer *videoLayer = sys->videoLayer;
+
+    dispatch_async(dispatch_get_main_queue(), ^{
+        // Remove vout subview from container
+        if ([container respondsToSelector:@selector(removeVoutSubview:)]) {
+            [container removeVoutSubview:videoView];
+        }
+        [videoView removeFromSuperview];
+
+        [videoView release];
+        [container release];
+        [videoLayer release];
+    });
 }
 
 static void PictureRender (vout_display_t *vd, picture_t *pic, subpicture_t *subpicture,
@@ -278,16 +456,12 @@ static void PictureRender (vout_display_t *vd, picture_t *pic, subpicture_t *sub
     VLC_UNUSED(date);
     vout_display_sys_t *sys = vd->sys;
 
-    if (pic == NULL) {
-        msg_Warn(vd, "invalid pic, skipping frame");
-        return;
-    }
+    if (vlc_gl_MakeCurrent(sys->gl) == VLC_SUCCESS)
+    {
+        vout_display_opengl_Prepare(sys->vgl, pic, subpicture);
+        vlc_gl_ReleaseCurrent(sys->gl);
 
-    @synchronized (sys->cgLayer) {
-        if (!OpenglLock(sys->gl)) {
-            vout_display_opengl_Prepare(sys->vgl, pic, subpicture);
-            OpenglUnlock(sys->gl);
-        }
+        atomic_store(&sys->is_ready, true);
     }
 }
 
@@ -296,15 +470,8 @@ static void PictureDisplay (vout_display_t *vd, picture_t *pic)
     vout_display_sys_t *sys = vd->sys;
     VLC_UNUSED(pic);
 
-    @synchronized (sys->cgLayer) {
-        sys->b_frame_available = YES;
 
-        /* Calling display on the non-main thread is not officially supported, but
-         * its suggested at several places and works fine here. Flush is thread-safe
-         * and makes sure the picture is actually displayed. */
-        [sys->cgLayer display];
-        [CATransaction flush];
-    }
+    [sys->videoLayer displayFromVout];
 }
 
 static int Control (vout_display_t *vd, int query)
@@ -317,36 +484,34 @@ static int Control (vout_display_t *vd, int query)
     switch (query)
     {
         case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
+            return VLC_SUCCESS;
+
         case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
         case VOUT_DISPLAY_CHANGE_ZOOM:
         case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
         case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
         {
-            /* we always use our current frame here */
-            vout_display_cfg_t cfg_tmp = *vd->cfg;
-            [CATransaction lock];
-            CGRect bounds = [sys->cgLayer visibleRect];
-            [CATransaction unlock];
-            cfg_tmp.display.width = bounds.size.width;
-            cfg_tmp.display.height = bounds.size.height;
-
-            /* Reverse vertical alignment as the GL tex are Y inverted */
-            if (cfg_tmp.align.vertical == VLC_VIDEO_ALIGN_TOP)
-                cfg_tmp.align.vertical = VLC_VIDEO_ALIGN_BOTTOM;
-            else if (cfg_tmp.align.vertical == VLC_VIDEO_ALIGN_BOTTOM)
-                cfg_tmp.align.vertical = VLC_VIDEO_ALIGN_TOP;
-
-            vout_display_place_t place;
-            vout_display_PlacePicture(&place, vd->source, &cfg_tmp);
-            if (unlikely(OpenglLock(sys->gl)))
-                // don't return an error or we need to handle reset_pictures
-                return VLC_SUCCESS;
-
-            vout_display_opengl_SetOutputSize(sys->vgl, place.width, place.height);
-            OpenglUnlock(sys->gl);
-
-            sys->place = place;
+            @synchronized(sys->videoLayer)
+            {
+                vout_display_cfg_t cfg = *vd->cfg;
+                cfg.display.width = sys->cfg.display.width;
+                cfg.display.height = sys->cfg.display.height;
+
+                // Reverse vertical alignment as the GL tex are Y inverted
+                if (cfg.align.vertical == VLC_VIDEO_ALIGN_TOP)
+                    cfg.align.vertical = VLC_VIDEO_ALIGN_BOTTOM;
+                else if (cfg.align.vertical == VLC_VIDEO_ALIGN_BOTTOM)
+                    cfg.align.vertical = VLC_VIDEO_ALIGN_TOP;
+                sys->cfg = cfg;
+
+                vout_display_PlacePicture(&sys->place, vd->source, &cfg);
+            }
 
+            // Note!
+            // No viewport or aspect ratio is set here, as that needs to be set
+            // when rendering. The viewport is always set to match the layer
+            // size by the OS right before the OpenGL render callback, so
+            // setting it here has no effect.
             return VLC_SUCCESS;
         }
 
@@ -359,191 +524,408 @@ static int Control (vout_display_t *vd, int query)
 }
 
 #pragma mark -
-#pragma mark OpenGL callbacks
+#pragma mark VLCVideoLayerView
+
+ at implementation VLCVideoLayerView
 
-static int OpenglLock (vlc_gl_t *gl)
+- (instancetype)initWithVoutDisplay:(vout_display_t *)vd
 {
-    struct gl_sys *sys = gl->sys;
-    assert(sys->locked_ctx == NULL);
+    self = [super init];
+    if (self) {
+        _vlc_vd = vd;
 
-    CGLContextObj ctx = [sys->cgLayer glContext];
-    if(!ctx) {
-        return 1;
+        self.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
+        self.wantsLayer = YES;
     }
+    return self;
+}
 
-    CGLError err = CGLLockContext(ctx);
-    if (kCGLNoError == err) {
-        sys->locked_ctx = ctx;
-        CGLSetCurrentContext(ctx);
-        return 0;
+/**
+ * Invalidates VLC objects (notably _vlc_vd)
+ * This method must be called in VLCs module Close (or indirectly by the View)
+ * to ensure all critical VLC resources that might be gone when the module is
+ * closed are properly NULLed. This is necessary as dealloc is only called later
+ * as it has to be done async on the main thread, because NSView must be
+ * dealloc'ed on the main thread and the view own the layer, so the layer
+ * will stay valid until the view is gone, and might still use _vlc_vd
+ * even after the VLC module is gone and the resources would be invalid.
+ */
+- (void)vlcClose
+{
+    @synchronized (self) {
+        [(VLCCAOpenGLLayer *)self.layer vlcClose];
+        _vlc_vd = NULL;
     }
-    return 1;
 }
 
-static void OpenglUnlock (vlc_gl_t *gl)
+- (void)viewWillStartLiveResize
 {
-    struct gl_sys *sys = gl->sys;
-    CGLUnlockContext(sys->locked_ctx);
-    sys->locked_ctx = NULL;
+    [(VLCCAOpenGLLayer *)self.layer setAsynchronous:YES];
 }
 
-static void OpenglSwap (vlc_gl_t *gl)
+- (void)viewDidEndLiveResize
 {
-    glFlush();
+    [(VLCCAOpenGLLayer *)self.layer setAsynchronous:NO];
+
+    // After a live resize we need to tell the core about our new size
+    [(VLCCAOpenGLLayer *)self.layer reportCurrentLayerSize];
 }
 
-static void *OurGetProcAddress (vlc_gl_t *gl, const char *name)
+- (CALayer *)makeBackingLayer
 {
-    VLC_UNUSED(gl);
+    @synchronized(self) {
+        NSAssert(_vlc_vd != NULL, @"Cannot create backing layer without vout display!");
 
-    return dlsym(RTLD_DEFAULT, name);
+        VLCCAOpenGLLayer *layer = [[VLCCAOpenGLLayer alloc] initWithVoutDisplay:_vlc_vd];
+        layer.delegate = self;
+        return [layer autorelease];
+    }
 }
 
+/* Layer delegate method that ensures the layer always get the
+ * correct contentScale based on whether the view is on a HiDPI
+ * display or not, and when it is moved between displays.
+ */
+- (BOOL)layer:(CALayer *)layer
+shouldInheritContentsScale:(CGFloat)newScale
+   fromWindow:(NSWindow *)window
+{
+    // If the scale changes, from the OpenGL point of view
+    // the size changes, so we need to indicate a resize
+    if (layer == self.layer) {
+        [(VLCCAOpenGLLayer *)self.layer
+            reportCurrentLayerSizeWithScale:newScale];
+        // FIXME
+        // For a brief moment the old image with a wrong scale
+        // is still visible, thats because the resize event is not
+        // processed immediately. Ideally this would be handled similar
+        // to how the live resize is done, to avoid this.
+    }
+    return YES;
+}
+
+/*
+ * General properties
+ */
+
+- (BOOL)isOpaque
+{
+    return YES;
+}
+
+- (BOOL)acceptsFirstResponder
+{
+    return YES;
+}
+
+- (BOOL)mouseDownCanMoveWindow
+{
+    return YES;
+}
+
+
+#pragma mark View mouse events
+
+/* Left mouse button down */
+- (void)mouseDown:(NSEvent *)event
+{
+    @synchronized(self) {
+        if (!_vlc_vd) {
+            [super mouseDown:event];
+            return;
+        }
+
+        if (event.type == NSLeftMouseDown &&
+            !(event.modifierFlags & NSControlKeyMask) &&
+            event.clickCount == 1) {
+            vout_display_SendEventMousePressed(_vlc_vd, MOUSE_BUTTON_LEFT);
+        }
+    }
+
+    [super mouseDown:event];
+}
+
+/* Left mouse button up */
+- (void)mouseUp:(NSEvent *)event
+{
+    @synchronized(self) {
+        if (!_vlc_vd) {
+            [super mouseUp:event];
+            return;
+        }
+
+        if (event.type == NSLeftMouseUp) {
+            vout_display_SendEventMouseReleased(_vlc_vd, MOUSE_BUTTON_LEFT);
+        }
+    }
+
+    [super mouseUp:event];
+}
+
+/* Middle mouse button down */
+- (void)otherMouseDown:(NSEvent *)event
+{
+    @synchronized(self) {
+        if (_vlc_vd)
+            vout_display_SendEventMousePressed(_vlc_vd, MOUSE_BUTTON_CENTER);
+    }
+
+    [super otherMouseDown:event];
+}
+
+/* Middle mouse button up */
+- (void)otherMouseUp:(NSEvent *)event
+{
+    @synchronized(self) {
+        if (_vlc_vd)
+            vout_display_SendEventMouseReleased(_vlc_vd, MOUSE_BUTTON_CENTER);
+    }
+
+    [super otherMouseUp:event];
+}
+
+- (void)mouseMovedInternal:(NSEvent *)event
+{
+    @synchronized(self) {
+        if (!_vlc_vd) {
+            return;
+        }
+
+        vout_display_sys_t *sys = _vlc_vd->sys;
+
+        // Convert window-coordinate point to view space
+        NSPoint pointInView = [self convertPoint:event.locationInWindow fromView:nil];
+
+        // Convert to pixels
+        NSPoint pointInBacking = [self convertPointToBacking:pointInView];
+
+        vout_display_SendMouseMovedDisplayCoordinates(_vlc_vd, pointInBacking.x, pointInBacking.y);
+    }
+}
+
+/* Mouse moved */
+- (void)mouseMoved:(NSEvent *)event
+{
+    [self mouseMovedInternal:event];
+    [super mouseMoved:event];
+}
+
+/* Mouse moved while clicked */
+- (void)mouseDragged:(NSEvent *)event
+{
+    [self mouseMovedInternal:event];
+    [super mouseDragged:event];
+}
+
+/* Mouse moved while center-clicked */
+- (void)otherMouseDragged:(NSEvent *)event
+{
+    [self mouseMovedInternal:event];
+    [super otherMouseDragged:event];
+}
+
+/* Mouse moved while right-clicked */
+- (void)rightMouseDragged:(NSEvent *)event
+{
+    [self mouseMovedInternal:event];
+    [super rightMouseDragged:event];
+}
+
+ at end
+
 #pragma mark -
-#pragma mark CA layer
+#pragma mark VLCCAOpenGLLayer
 
-/*****************************************************************************
- * @implementation VLCCAOpenGLLayer
- *****************************************************************************/
 @implementation VLCCAOpenGLLayer
 
-- (id)init {
-
+- (instancetype)initWithVoutDisplay:(vout_display_t *)vd
+{
     self = [super init];
     if (self) {
+        _displayLock = [[NSLock alloc] init];
+        _voutDisplay = vd;
+
+        vout_display_sys_t *sys = vd->sys;
+        struct vlc_gl_sys *glsys = sys->gl->sys;
+        _glContext = CGLRetainContext(glsys->cgl);
+
         [CATransaction lock];
         self.needsDisplayOnBoundsChange = YES;
         self.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
         self.asynchronous = NO;
+        self.opaque = 1.0;
+        self.hidden = NO;
         [CATransaction unlock];
     }
 
     return self;
 }
 
-- (void)setVoutDisplay:(vout_display_t *)aVd
+/**
+ * Invalidates VLC objects (notably _voutDisplay)
+ * This method must be called in VLCs module Close (or indirectly by the View).
+ */
+- (void)vlcClose
 {
-    _voutDisplay = aVd;
+    @synchronized (self) {
+        _voutDisplay = NULL;
+    }
 }
 
-- (void)resizeWithOldSuperlayerSize:(CGSize)size
+- (void)dealloc
 {
-    [super resizeWithOldSuperlayerSize: size];
+    CGLReleaseContext(_glContext);
+    [_displayLock release];
+    [super dealloc];
+}
 
-    CGSize boundsSize = self.visibleRect.size;
 
-    if (_voutDisplay)
-    {
-        vout_display_sys_t *sys = _voutDisplay->sys;
-        vout_window_ReportSize(sys->embed, boundsSize.width, boundsSize.height);
+- (void)layoutSublayers
+{
+    [super layoutSublayers];
+
+    if (self.asynchronous) {
+        // During live resize, the size is updated in the
+        // OpenGL draw callback, to ensure atomic size changes
+        // that are in sync with the real layer size.
+        // This bypasses the core but is needed for resizing
+        // without glitches or lags.
+        return;
     }
+
+    [self reportCurrentLayerSize];
 }
 
-- (BOOL)canDrawInCGLContext:(CGLContextObj)glContext pixelFormat:(CGLPixelFormatObj)pixelFormat forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp
+- (void)reportCurrentLayerSizeWithScale:(CGFloat)scale
 {
-    /* Only draw the frame if we have a frame that was previously rendered */
-    if (!_voutDisplay)
-        return false;
+    CGSize newSize = self.visibleRect.size;
 
-    vout_display_sys_t *sys = _voutDisplay->sys;
+    // Calculate pixel values
+    newSize.width *= scale;
+    newSize.height *= scale;
 
-    return sys->b_frame_available;
+    @synchronized(self) {
+        if (!_voutDisplay)
+            return;
+        vout_display_sys_t *sys = _voutDisplay->sys;
+        @synchronized(sys->videoLayer) {
+            sys->cfg.display.width = newSize.width;
+            sys->cfg.display.height = newSize.height;
+        }
+    }
 }
 
-- (void)drawInCGLContext:(CGLContextObj)glContext pixelFormat:(CGLPixelFormatObj)pixelFormat forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp
+- (void)reportCurrentLayerSize
 {
-    if (!_voutDisplay)
-        return;
-    vout_display_sys_t *sys = _voutDisplay->sys;
-
-    if (!sys->vgl)
-        return;
+    CGFloat scale = self.contentsScale;
+    [self reportCurrentLayerSizeWithScale:scale];
+}
 
-    CGRect bounds = [self visibleRect];
+- (void)display
+{
+    [_displayLock lock];
 
-    // x / y are top left corner, but we need the lower left one
-    vout_display_opengl_Viewport(sys->vgl, sys->place.x,
-                                 bounds.size.height - (sys->place.y + sys->place.height),
-                                 sys->place.width, sys->place.height);
+    [super display];
+    [CATransaction flush];
 
-    // flush is also done by this method, no need to call super
-    vout_display_opengl_Display(sys->vgl);
-    sys->b_frame_available = NO;
+    [_displayLock unlock];
 }
 
--(CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask
+- (void)displayFromVout
 {
-    // The default is fine for this demonstration.
-    return [super copyCGLPixelFormatForDisplayMask:mask];
+    if (self.asynchronous) {
+        // During live resizing we do not take updates
+        // from the vout, as those would interfere with
+        // the rendering currently happening on the main
+        // thread for the resize. Rendering anyway happens
+        // triggered by the OS every display refresh, so
+        // forcing an update here would be useless anyway.
+        return;
+    }
+
+    [self display];
 }
 
-- (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat
+- (BOOL)canDrawInCGLContext:(CGLContextObj)glContext
+                pixelFormat:(CGLPixelFormatObj)pixelFormat
+               forLayerTime:(CFTimeInterval)timeInterval
+                displayTime:(const CVTimeStamp *)timeStamp
 {
-    // Only one opengl context is allowed for the module lifetime
-    if(_glContext) {
-        msg_Dbg(_voutDisplay, "Return existing context: %p", _glContext);
-        return _glContext;
+    @synchronized(self) {
+        if (!_voutDisplay)
+            return NO;
+         vout_display_sys_t *sys = _voutDisplay->sys;
+
+        return (atomic_load(&sys->is_ready));
     }
+}
 
-    CGLContextObj context = [super copyCGLContextForPixelFormat:pixelFormat];
+- (void)drawInCGLContext:(CGLContextObj)glContext
+             pixelFormat:(CGLPixelFormatObj)pixelFormat
+            forLayerTime:(CFTimeInterval)timeInterval
+             displayTime:(const CVTimeStamp *)timeStamp
+{
+    @synchronized(self) {
+        if (!_voutDisplay)
+            return;
 
-    // Swap buffers only during the vertical retrace of the monitor.
-    //http://developer.apple.com/documentation/GraphicsImaging/
-    //Conceptual/OpenGL/chap5/chapter_5_section_44.html /
+        vout_display_sys_t *sys = _voutDisplay->sys;
 
-    GLint params = 1;
-    CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval,
-                     &params );
+        if (vlc_gl_MakeCurrent(sys->gl))
+            return;
 
-    @synchronized (self) {
-        _glContext = context;
-    }
+        if (self.asynchronous) {
+            GLint dims[4] = { 0, 0, 0, 0 };
+            glGetIntegerv(GL_VIEWPORT, dims);
+            NSSize newSize = NSMakeSize(dims[2], dims[3]);
 
-    return context;
-}
+            if (NSEqualSizes(newSize, NSZeroSize)) {
+                newSize = self.bounds.size;
+                CGFloat scale = self.contentsScale;
+                newSize.width *= scale;
+                newSize.height *= scale;
+            }
 
-- (void)releaseCGLContext:(CGLContextObj)glContext
-{
-    // do not release anything here, we do that when closing the module
-}
+            @synchronized(sys->videoView)
+            {
+                sys->cfg.display.width = newSize.width;
+                sys->cfg.display.height = newSize.height;
 
-- (void)mouseButtonDown:(int)buttonNumber
-{
-    @synchronized (self) {
-        if (_voutDisplay) {
-            if (buttonNumber == 0)
-                vout_display_SendEventMousePressed (_voutDisplay, MOUSE_BUTTON_LEFT);
-            else if (buttonNumber == 1)
-                vout_display_SendEventMousePressed (_voutDisplay, MOUSE_BUTTON_RIGHT);
-            else
-                vout_display_SendEventMousePressed (_voutDisplay, MOUSE_BUTTON_CENTER);
+                vout_display_PlacePicture(&sys->place, _voutDisplay->source, &sys->cfg);
+            }
         }
+
+        // Ensure viewport and aspect ratio is correct
+        vout_display_opengl_Viewport(sys->vgl, sys->place.x, sys->place.y,
+                                     sys->place.width, sys->place.height);
+        vout_display_opengl_SetOutputSize(sys->vgl, sys->place.width, sys->place.height);
+
+        vout_display_opengl_Display(sys->vgl);
+        vlc_gl_ReleaseCurrent(sys->gl);
     }
 }
 
-- (void)mouseButtonUp:(int)buttonNumber
+- (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask
 {
-    @synchronized (self) {
-        if (_voutDisplay) {
-            if (buttonNumber == 0)
-                vout_display_SendEventMouseReleased (_voutDisplay, MOUSE_BUTTON_LEFT);
-            else if (buttonNumber == 1)
-                vout_display_SendEventMouseReleased (_voutDisplay, MOUSE_BUTTON_RIGHT);
-            else
-                vout_display_SendEventMouseReleased (_voutDisplay, MOUSE_BUTTON_CENTER);
-        }
-    }
+    CGLPixelFormatObj fmt = CGLGetPixelFormat(_glContext);
+    
+    return (fmt) ? CGLRetainPixelFormat(fmt) : NULL;
 }
 
-- (void)mouseMovedToX:(double)xValue Y:(double)yValue
+- (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat
 {
-    @synchronized (self) {
-        if (_voutDisplay) {
-            vout_display_SendMouseMovedDisplayCoordinates (_voutDisplay,
-                                                           xValue,
-                                                           yValue);
-        }
-    }
+    return CGLRetainContext(_glContext);
 }
 
 @end
+
+/*
+ * Module descriptor
+ */
+vlc_module_begin()
+    set_description(N_("Core Animation OpenGL Layer (Mac OS X)"))
+    set_subcategory(SUBCAT_VIDEO_VOUT)
+    set_callback_display(Open, 300)
+
+    add_opengl_submodule_renderer()
+vlc_module_end()


=====================================
modules/video_output/macosx.m
=====================================
@@ -75,7 +75,7 @@ vlc_module_begin ()
     set_shortname ("Mac OS X")
     set_description (N_("Mac OS X OpenGL video output"))
     set_subcategory (SUBCAT_VIDEO_VOUT)
-    set_callback_display(Open, 300)
+    set_callback_display(Open, 250)
     add_shortcut ("macosx", "vout_macosx")
     add_glopts ()
 


=====================================
modules/video_output/window_macosx.m
=====================================
@@ -580,6 +580,30 @@ int Open(vout_window_t *wnd)
     }
 }
 
+static void DrawableClose(vout_window_t *wnd)
+{
+    id drawable = (__bridge_transfer id)wnd->handle.nsobject;
+    wnd->handle.nsobject = nil;
+    (void)drawable;
+}
+
+static const struct vout_window_operations drawable_ops =
+{
+    .destroy = DrawableClose,
+};
+
+static int DrawableOpen(vout_window_t *wnd)
+{
+    id drawable = (__bridge id)
+        var_InheritAddress(wnd, "drawable-nsobject");
+    if (drawable == nil)
+        return VLC_EGENERIC;
+
+    wnd->ops = &drawable_ops;
+    wnd->handle.nsobject = (__bridge_retained void*)drawable;
+    return VLC_SUCCESS;
+}
+
 /*
  * Module declaration
  */
@@ -587,4 +611,11 @@ vlc_module_begin()
     set_description("macOS Video Output Window")
     set_capability("vout window", 1000)
     set_callback(Open)
+
+    add_submodule()
+    set_shortname(N_("NSObject Drawable"))
+    set_subcategory(SUBCAT_VIDEO_VOUT)
+    set_capability("vout window", 10000)
+    set_callback(DrawableOpen)
+    add_shortcut("embed-nsobject")
 vlc_module_end()



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/fe7a43f967d2f44ba96ed0a8356f85128a4775c7...6bc300f3f2522cd7c5c0df4f429f7b1cd34a84e4

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/fe7a43f967d2f44ba96ed0a8356f85128a4775c7...6bc300f3f2522cd7c5c0df4f429f7b1cd34a84e4
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list