• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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