1/* 2 * GStreamer 3 * Copyright (C) 2019 Matthew Waters <matthew@centricular.com> 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Library General Public 7 * License as published by the Free Software Foundation; either 8 * version 2 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Library General Public License for more details. 14 * 15 * You should have received a copy of the GNU Library General Public 16 * License along with this library; if not, write to the 17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 18 * Boston, MA 02110-1301, USA. 19 */ 20 21#ifdef HAVE_CONFIG_H 22#include "config.h" 23#endif 24 25#include <QuartzCore/QuartzCore.h> 26 27#include <gst/gst.h> 28 29#include <gst/vulkan/vulkan.h> 30 31#include "gstvkwindow_ios.h" 32#include "gstvkdisplay_ios.h" 33 34#include "gstvkios_utils.h" 35 36#define GET_PRIV(o) gst_vulkan_window_ios_get_instance_private (o) 37 38#define GST_CAT_DEFAULT gst_vulkan_window_ios_debug 39GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); 40 41static void 42_init_debug (void) 43{ 44 static gsize _init = 0; 45 46 if (g_once_init_enter (&_init)) { 47 GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkanwindowios", 0, 48 "Vulkan iOS Window"); 49 g_once_init_leave (&_init, 1); 50 } 51} 52 53enum 54{ 55 PROP_0, 56}; 57 58struct _GstVulkanWindowIosPrivate 59{ 60 gpointer internal_view; 61 gpointer internal_layer; 62 gpointer external_view; 63 64 gint preferred_width; 65 gint preferred_height; 66 67 GMutex lock; 68 GCond cond; 69}; 70 71#define gst_vulkan_window_ios_parent_class parent_class 72G_DEFINE_TYPE_WITH_CODE (GstVulkanWindowIos, gst_vulkan_window_ios, 73 GST_TYPE_VULKAN_WINDOW, G_ADD_PRIVATE (GstVulkanWindowIos) _init_debug ()); 74 75static VkSurfaceKHR gst_vulkan_window_ios_get_surface (GstVulkanWindow * window, 76 GError ** error); 77static gboolean gst_vulkan_window_ios_get_presentation_support (GstVulkanWindow 78 * window, GstVulkanDevice * device, guint32 queue_family_idx); 79static gboolean gst_vulkan_window_ios_open (GstVulkanWindow * window, 80 GError ** error); 81static void gst_vulkan_window_ios_close (GstVulkanWindow * window); 82static void gst_vulkan_window_ios_set_window_handle (GstVulkanWindow * window, 83 guintptr window_handle); 84 85static void 86gst_vulkan_window_ios_finalize (GObject * object) 87{ 88 GstVulkanWindowIos *window_ios = GST_VULKAN_WINDOW_IOS (object); 89 GstVulkanWindowIosPrivate *priv = GET_PRIV (window_ios); 90 91 g_mutex_clear (&priv->lock); 92 g_cond_clear (&priv->cond); 93 94 G_OBJECT_CLASS (parent_class)->finalize (object); 95} 96 97static void 98gst_vulkan_window_ios_class_init (GstVulkanWindowIosClass * klass) 99{ 100 GObjectClass *obj_class = G_OBJECT_CLASS (klass); 101 GstVulkanWindowClass *window_class = (GstVulkanWindowClass *) klass; 102 103 obj_class->finalize = gst_vulkan_window_ios_finalize; 104 105 window_class->open = GST_DEBUG_FUNCPTR (gst_vulkan_window_ios_open); 106 window_class->close = GST_DEBUG_FUNCPTR (gst_vulkan_window_ios_close); 107 window_class->get_surface = gst_vulkan_window_ios_get_surface; 108 window_class->get_presentation_support = 109 gst_vulkan_window_ios_get_presentation_support; 110 window_class->set_window_handle = 111 gst_vulkan_window_ios_set_window_handle; 112} 113 114static void 115gst_vulkan_window_ios_init (GstVulkanWindowIos * window_ios) 116{ 117 GstVulkanWindowIosPrivate *priv = GET_PRIV (window_ios); 118 119 priv->preferred_width = 320; 120 priv->preferred_height = 240; 121 122 g_mutex_init (&priv->lock); 123 g_cond_init (&priv->cond); 124} 125 126/* Must be called in the gl thread */ 127GstVulkanWindowIos * 128gst_vulkan_window_ios_new (GstVulkanDisplay * display) 129{ 130 GstVulkanWindowIos *window; 131 132 _init_debug (); 133 134 if ((gst_vulkan_display_get_handle_type (display) & 135 GST_VULKAN_DISPLAY_TYPE_IOS) 136 == GST_VULKAN_DISPLAY_TYPE_NONE) { 137 GST_INFO ("Wrong display type %u for this window type %u", display->type, 138 GST_VULKAN_DISPLAY_TYPE_IOS); 139 return NULL; 140 } 141 142 window = g_object_new (GST_TYPE_VULKAN_WINDOW_IOS, NULL); 143 gst_object_ref_sink (window); 144 145 return window; 146} 147 148static void 149_create_window (GstVulkanWindowIos * window_ios) 150{ 151 GstVulkanWindowIosPrivate *priv = GET_PRIV (window_ios); 152 UIView *external_view = (__bridge UIView *) priv->external_view; 153 CGRect rect = CGRectMake (0, 0, external_view.frame.size.width, external_view.frame.size.height); 154 GstVulkanUIView *view; 155 156 view = [[GstVulkanUIView alloc] initWithFrame:rect]; 157 [view setGstWindow:window_ios]; 158 view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 159 view.contentMode = UIViewContentModeRedraw; 160 161 g_mutex_lock (&priv->lock); 162 163 priv->internal_view = (__bridge_retained gpointer) view; 164 [external_view addSubview:view]; 165 priv->internal_layer = (__bridge_retained gpointer) [view layer]; 166 167 g_cond_broadcast (&priv->cond); 168 g_mutex_unlock (&priv->lock); 169} 170 171gboolean 172gst_vulkan_window_ios_create_window (GstVulkanWindowIos * window_ios) 173{ 174 GstVulkanWindowIosPrivate *priv = GET_PRIV (window_ios); 175 176 if (!priv->external_view) { 177 GST_WARNING_OBJECT(window_ios, "No external UIView provided"); 178 return FALSE; 179 } 180 181 _invoke_on_main ((GstVulkanWindowFunc) _create_window, 182 gst_object_ref (window_ios), gst_object_unref); 183 184 /* XXX: Maybe we need an async create_window/get_surface()? */ 185 g_mutex_lock (&priv->lock); 186 while (!priv->internal_view) 187 g_cond_wait (&priv->cond, &priv->lock); 188 g_mutex_unlock (&priv->lock); 189 190 return TRUE; 191} 192 193static VkSurfaceKHR 194gst_vulkan_window_ios_get_surface (GstVulkanWindow * window, GError ** error) 195{ 196 GstVulkanWindowIos *window_ios = GST_VULKAN_WINDOW_IOS (window); 197 GstVulkanWindowIosPrivate *priv = GET_PRIV (window_ios); 198 VkIOSSurfaceCreateInfoMVK info = { 0, }; 199 VkSurfaceKHR ret; 200 VkResult err; 201 202 if (!priv->internal_layer) { 203 g_set_error_literal (error, GST_VULKAN_ERROR, 204 VK_ERROR_INITIALIZATION_FAILED, 205 "No layer to retrieve surface for. Has create_window() been called?"); 206 return VK_NULL_HANDLE; 207 } 208 209 info.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK; 210 info.pNext = NULL; 211 info.flags = 0; 212 info.pView = priv->internal_layer; 213 214 if (!window_ios->CreateIOSSurface) 215 window_ios->CreateIOSSurface = 216 gst_vulkan_instance_get_proc_address (window->display->instance, 217 "vkCreateIOSSurfaceMVK"); 218 if (!window_ios->CreateIOSSurface) { 219 g_set_error_literal (error, GST_VULKAN_ERROR, VK_ERROR_FEATURE_NOT_PRESENT, 220 "Could not retrieve \"vkCreateIOSSurfaceMVK\" function pointer"); 221 return VK_NULL_HANDLE; 222 } 223 224 err = 225 window_ios->CreateIOSSurface (window->display->instance->instance, &info, 226 NULL, &ret); 227 if (gst_vulkan_error_to_g_error (err, error, "vkCreateIOSSurfaceMVK") < 0) 228 return VK_NULL_HANDLE; 229 230 return ret; 231} 232 233static gboolean 234gst_vulkan_window_ios_get_presentation_support (GstVulkanWindow * window, 235 GstVulkanDevice * device, guint32 queue_family_idx) 236{ 237 return TRUE; 238} 239 240static gboolean 241gst_vulkan_window_ios_open (GstVulkanWindow * window, GError ** error) 242{ 243 GstVulkanWindowIos *window_ios = GST_VULKAN_WINDOW_IOS (window); 244 245 if (!GST_VULKAN_WINDOW_CLASS (parent_class)->open (window, error)) 246 return FALSE; 247 248 return gst_vulkan_window_ios_create_window (window_ios); 249} 250 251static void 252gst_vulkan_window_ios_close (GstVulkanWindow * window) 253{ 254 GstVulkanWindowIos *window_ios = GST_VULKAN_WINDOW_IOS (window); 255 GstVulkanWindowIosPrivate *priv = GET_PRIV (window_ios); 256 GstVulkanUIView *view = (__bridge GstVulkanUIView *) priv->internal_view; 257 258 [view setGstWindow:NULL]; 259 CFBridgingRelease (priv->internal_view); 260 priv->internal_view = NULL; 261 CFBridgingRelease (priv->internal_layer); 262 priv->internal_layer = NULL; 263 264 GST_VULKAN_WINDOW_CLASS (parent_class)->close (window); 265} 266 267static void 268gst_vulkan_window_ios_set_window_handle (GstVulkanWindow * window, 269 guintptr window_handle) 270{ 271 GstVulkanWindowIos *window_ios = GST_VULKAN_WINDOW_IOS (window); 272 GstVulkanWindowIosPrivate *priv = GET_PRIV (window_ios); 273 gpointer view = (gpointer) window_handle; 274 275 g_return_if_fail (view != NULL); 276 277 if (priv->external_view && priv->external_view != view) { 278 GST_FIXME_OBJECT (window_ios, "View changes are not implemented"); 279 return; 280 } 281 282 priv->external_view = view; 283} 284 285@implementation GstVulkanUIView { 286 GstVulkanWindowIos * window_ios; 287 288 guint width; 289 guint height; 290}; 291 292+(Class) layerClass 293{ 294 return [CAMetalLayer class]; 295} 296 297-(void) setGstWindow:(GstVulkanWindowIos *) window 298{ 299 window_ios = window; 300} 301 302-(void) layoutSubViews 303{ 304 [super layoutSubviews]; 305 CGSize rect = self.bounds.size; 306 GST_ERROR ("%ix%i", (int) rect.width, (int) rect.height); 307 gboolean resize = self->width != rect.width || self->height != rect.height; 308 self->width = rect.width; 309 self->height = rect.height; 310 if (resize && self->window_ios) { 311 gst_vulkan_window_resize (GST_VULKAN_WINDOW (self->window_ios), rect.width, rect.height); 312 } 313} 314 315@end 316 317void 318_invoke_on_main (GstVulkanWindowFunc func, gpointer data, GDestroyNotify notify) 319{ 320 if ([NSThread isMainThread]) { 321 func (data); 322 if (notify) 323 notify (data); 324 } else { 325 dispatch_async (dispatch_get_main_queue (), ^{ 326 func (data); 327 if (notify) 328 notify (data); 329 }); 330 } 331} 332 333