macos: Add wrapper API to run a NSApplication in the main thread
authorPiotr Brzeziński <piotr@centricular.com>
Tue, 13 Dec 2022 17:42:11 +0000 (18:42 +0100)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Tue, 13 Dec 2022 17:50:32 +0000 (17:50 +0000)
On macOS, a Cocoa event loop is needed in the main thread to ensure
things like opening a GL window work correctly. In the past, this was
patched into glib via Cerbero, but that prevented us from updating it.
This workaround simply runs an NSApplication and then calls the
main function on a secondary thread, allowing GStreamer to correctly
display windows and/or system permission prompts, for example.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3532>

24 files changed:
subprojects/gst-docs/examples/tutorials/basic-tutorial-1.c
subprojects/gst-docs/examples/tutorials/basic-tutorial-12.c
subprojects/gst-docs/examples/tutorials/basic-tutorial-13.c
subprojects/gst-docs/examples/tutorials/basic-tutorial-15.c
subprojects/gst-docs/examples/tutorials/basic-tutorial-2.c
subprojects/gst-docs/examples/tutorials/basic-tutorial-3.c
subprojects/gst-docs/examples/tutorials/basic-tutorial-4.c
subprojects/gst-docs/examples/tutorials/basic-tutorial-5.c
subprojects/gst-docs/examples/tutorials/basic-tutorial-6.c
subprojects/gst-docs/examples/tutorials/basic-tutorial-7.c
subprojects/gst-docs/examples/tutorials/basic-tutorial-8.c
subprojects/gst-docs/examples/tutorials/basic-tutorial-9.c
subprojects/gst-docs/examples/tutorials/playback-tutorial-1.c
subprojects/gst-docs/examples/tutorials/playback-tutorial-2.c
subprojects/gst-docs/examples/tutorials/playback-tutorial-3.c
subprojects/gst-docs/examples/tutorials/playback-tutorial-4.c
subprojects/gst-docs/examples/tutorials/playback-tutorial-5.c
subprojects/gst-docs/examples/tutorials/playback-tutorial-6.c
subprojects/gst-docs/examples/tutorials/playback-tutorial-7.c
subprojects/gstreamer/gst/gst.h
subprojects/gstreamer/gst/gstmacos.h [new file with mode: 0644]
subprojects/gstreamer/gst/gstmacos.m [new file with mode: 0644]
subprojects/gstreamer/gst/meson.build
subprojects/gstreamer/meson.build

index 573d4b2e90955a8b4488c96e023cac82351a837c..35ae853e0b7f14d2cc76e8abeb34dc19b4848418 100644 (file)
@@ -1,7 +1,11 @@
 #include <gst/gst.h>
 
+#ifdef __APPLE__
+#include <TargetConditionals.h>
+#endif
+
 int
-main (int argc, char *argv[])
+tutorial_main (int argc, char *argv[])
 {
   GstElement *pipeline;
   GstBus *bus;
@@ -38,3 +42,13 @@ main (int argc, char *argv[])
   gst_object_unref (pipeline);
   return 0;
 }
+
+int
+main (int argc, char *argv[])
+{
+#if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
+  return gst_macos_main (tutorial_main, argc, argv, NULL);
+#else
+  return tutorial_main (argc, argv);
+#endif
+}
index d3f0eb9f187dbd64086fec90a5060ba8507fb143..f01f60fac41f0083f9c68bf687c1531d9a51f1ff 100644 (file)
@@ -1,6 +1,10 @@
 #include <gst/gst.h>
 #include <string.h>
 
+#ifdef __APPLE__
+#include <TargetConditionals.h>
+#endif
+
 typedef struct _CustomData
 {
   gboolean is_live;
@@ -59,7 +63,7 @@ cb_message (GstBus * bus, GstMessage * msg, CustomData * data)
 }
 
 int
-main (int argc, char *argv[])
+tutorial_main (int argc, char *argv[])
 {
   GstElement *pipeline;
   GstBus *bus;
@@ -106,3 +110,13 @@ main (int argc, char *argv[])
   gst_object_unref (pipeline);
   return 0;
 }
+
+int
+main (int argc, char *argv[])
+{
+#if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
+  return gst_macos_main (tutorial_main, argc, argv, NULL);
+#else
+  return tutorial_main (argc, argv);
+#endif
+}
index 880702c32d6c4c571b2c73db5a61e81c61cd7d9e..b7640f643e0822a81abebc44248005e977e84fa0 100644 (file)
@@ -2,6 +2,10 @@
 #include <stdio.h>
 #include <gst/gst.h>
 
+#ifdef __APPLE__
+#include <TargetConditionals.h>
+#endif
+
 typedef struct _CustomData
 {
   GstElement *pipeline;
@@ -103,7 +107,7 @@ handle_keyboard (GIOChannel * source, GIOCondition cond, CustomData * data)
 }
 
 int
-main (int argc, char *argv[])
+tutorial_main (int argc, char *argv[])
 {
   CustomData data;
   GstStateChangeReturn ret;
@@ -160,3 +164,13 @@ main (int argc, char *argv[])
   gst_object_unref (data.pipeline);
   return 0;
 }
+
+int
+main (int argc, char *argv[])
+{
+#if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
+  return gst_macos_main (tutorial_main, argc, argv, NULL);
+#else
+  return tutorial_main (argc, argv);
+#endif
+}
index dcf2db1a13e444cc8e807db6289e151b1a1c797f..882a9d6e42f41c98cdef468d95b407e1a0202ce4 100644 (file)
@@ -1,5 +1,9 @@
 #include <clutter-gst/clutter-gst.h>
 
+#ifdef __APPLE__
+#include <TargetConditionals.h>
+#endif
+
 /* Setup the video texture once its size is known */
 void
 size_change (ClutterActor * texture, gint width, gint height,
@@ -42,7 +46,7 @@ size_change (ClutterActor * texture, gint width, gint height,
 }
 
 int
-main (int argc, char *argv[])
+tutorial_main (int argc, char *argv[])
 {
   GstElement *pipeline, *sink;
   ClutterTimeline *timeline;
@@ -106,3 +110,13 @@ main (int argc, char *argv[])
   gst_object_unref (pipeline);
   return 0;
 }
+
+int
+main (int argc, char *argv[])
+{
+#if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
+  return gst_macos_main (tutorial_main, argc, argv, NULL);
+#else
+  return tutorial_main (argc, argv);
+#endif
+}
index 4ccea592e2b3869ff7a37b4c8926157cc1adecdd..6f795ca59c9f89d7c2635239dd869d8d9faa27e1 100644 (file)
@@ -1,7 +1,11 @@
 #include <gst/gst.h>
 
+#ifdef __APPLE__
+#include <TargetConditionals.h>
+#endif
+
 int
-main (int argc, char *argv[])
+tutorial_main (int argc, char *argv[])
 {
   GstElement *pipeline, *source, *sink;
   GstBus *bus;
@@ -80,3 +84,13 @@ main (int argc, char *argv[])
   gst_object_unref (pipeline);
   return 0;
 }
+
+int
+main (int argc, char *argv[])
+{
+#if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
+  return gst_macos_main (tutorial_main, argc, argv, NULL);
+#else
+  return tutorial_main (argc, argv);
+#endif
+}
index 320382d5787ae2ef047fc0ef1684d3c002ba9871..722c44a1891bce6a18a919462a7c3940ff7253c3 100644 (file)
@@ -1,5 +1,9 @@
 #include <gst/gst.h>
 
+#ifdef __APPLE__
+#include <TargetConditionals.h>
+#endif
+
 /* Structure to contain all our information, so we can pass it to callbacks */
 typedef struct _CustomData
 {
@@ -15,7 +19,7 @@ static void pad_added_handler (GstElement * src, GstPad * pad,
     CustomData * data);
 
 int
-main (int argc, char *argv[])
+tutorial_main (int argc, char *argv[])
 {
   CustomData data;
   GstBus *bus;
@@ -166,3 +170,13 @@ exit:
   /* Unreference the sink pad */
   gst_object_unref (sink_pad);
 }
+
+int
+main (int argc, char *argv[])
+{
+#if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
+  return gst_macos_main (tutorial_main, argc, argv, NULL);
+#else
+  return tutorial_main (argc, argv);
+#endif
+}
index b3a522982fba504dd13051cacbb8439426766749..6b5b92dbf73685dc68c42ef37e7097ae8d46e6e0 100644 (file)
@@ -1,5 +1,9 @@
 #include <gst/gst.h>
 
+#ifdef __APPLE__
+#include <TargetConditionals.h>
+#endif
+
 /* Structure to contain all our information, so we can pass it around */
 typedef struct _CustomData
 {
@@ -15,7 +19,7 @@ typedef struct _CustomData
 static void handle_message (CustomData * data, GstMessage * msg);
 
 int
-main (int argc, char *argv[])
+tutorial_main (int argc, char *argv[])
 {
   CustomData data;
   GstBus *bus;
@@ -170,3 +174,13 @@ handle_message (CustomData * data, GstMessage * msg)
   }
   gst_message_unref (msg);
 }
+
+int
+main (int argc, char *argv[])
+{
+#if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
+  return gst_macos_main (tutorial_main, argc, argv, NULL);
+#else
+  return tutorial_main (argc, argv);
+#endif
+}
index 34c75c978f12c7dbb14acc89825ca242731092ac..7aab34c1b78c89da8b5852679ddce55db2e9c23e 100644 (file)
@@ -4,6 +4,10 @@
 #include <gst/gst.h>
 #include <gst/video/videooverlay.h>
 
+#ifdef __APPLE__
+#include <TargetConditionals.h>
+#endif
+
 #include <gdk/gdk.h>
 #if defined (GDK_WINDOWING_X11)
 #include <gdk/gdkx.h>
@@ -372,7 +376,7 @@ application_cb (GstBus * bus, GstMessage * msg, CustomData * data)
 }
 
 int
-main (int argc, char *argv[])
+tutorial_main (int argc, char *argv[])
 {
   CustomData data;
   GstStateChangeReturn ret;
@@ -443,3 +447,13 @@ main (int argc, char *argv[])
   gst_object_unref (data.playbin);
   return 0;
 }
+
+int
+main (int argc, char *argv[])
+{
+#if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
+  return gst_macos_main (tutorial_main, argc, argv, NULL);
+#else
+  return tutorial_main (argc, argv);
+#endif
+}
index 2c06b3a9cc64428fc0c4bf275d82708ffabb3608..3b6cb69803fa81fe0391ee42b1f5dc33e81f239e 100644 (file)
@@ -1,5 +1,9 @@
 #include <gst/gst.h>
 
+#ifdef __APPLE__
+#include <TargetConditionals.h>
+#endif
+
 /* Functions below print the Capabilities in a human-friendly format */
 static gboolean
 print_field (GQuark field, const GValue * value, gpointer pfx)
@@ -110,7 +114,7 @@ print_pad_capabilities (GstElement * element, gchar * pad_name)
 }
 
 int
-main (int argc, char *argv[])
+tutorial_main (int argc, char *argv[])
 {
   GstElement *pipeline, *source, *sink;
   GstElementFactory *source_factory, *sink_factory;
@@ -222,3 +226,13 @@ main (int argc, char *argv[])
   gst_object_unref (sink_factory);
   return 0;
 }
+
+int
+main (int argc, char *argv[])
+{
+#if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
+  return gst_macos_main (tutorial_main, argc, argv, NULL);
+#else
+  return tutorial_main (argc, argv);
+#endif
+}
index 7cb9c1372f1a90435cff3910d35e2e92e20f449d..d2b24134bfa4dcc6022e330400d054be0511b009 100644 (file)
@@ -1,7 +1,11 @@
 #include <gst/gst.h>
 
+#ifdef __APPLE__
+#include <TargetConditionals.h>
+#endif
+
 int
-main (int argc, char *argv[])
+tutorial_main (int argc, char *argv[])
 {
   GstElement *pipeline, *audio_source, *tee, *audio_queue, *audio_convert,
       *audio_resample, *audio_sink;
@@ -96,3 +100,13 @@ main (int argc, char *argv[])
   gst_object_unref (pipeline);
   return 0;
 }
+
+int
+main (int argc, char *argv[])
+{
+#if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
+  return gst_macos_main (tutorial_main, argc, argv, NULL);
+#else
+  return tutorial_main (argc, argv);
+#endif
+}
index 27749f95b7cb2c6d7585a5d50d6d47ec6fc86e8c..50b620547f775f805a7f89c9065673f5745f35e8 100644 (file)
@@ -2,6 +2,10 @@
 #include <gst/audio/audio.h>
 #include <string.h>
 
+#ifdef __APPLE__
+#include <TargetConditionals.h>
+#endif
+
 #define CHUNK_SIZE 1024         /* Amount of bytes we are sending in each buffer */
 #define SAMPLE_RATE 44100       /* Samples per second we are sending */
 
@@ -134,7 +138,7 @@ error_cb (GstBus * bus, GstMessage * msg, CustomData * data)
 }
 
 int
-main (int argc, char *argv[])
+tutorial_main (int argc, char *argv[])
 {
   CustomData data;
   GstPad *tee_audio_pad, *tee_video_pad, *tee_app_pad;
@@ -268,3 +272,13 @@ main (int argc, char *argv[])
   gst_object_unref (data.pipeline);
   return 0;
 }
+
+int
+main (int argc, char *argv[])
+{
+#if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
+  return gst_macos_main (tutorial_main, argc, argv, NULL);
+#else
+  return tutorial_main (argc, argv);
+#endif
+}
index a1ed3b950590b66a93b9c3a4426752b566d10e64..f252f4f04443c92d798f5d1e88eef2b8b846c73e 100644 (file)
@@ -2,6 +2,10 @@
 #include <gst/gst.h>
 #include <gst/pbutils/pbutils.h>
 
+#ifdef __APPLE__
+#include <TargetConditionals.h>
+#endif
+
 /* Structure to contain all our information, so we can pass it around */
 typedef struct _CustomData
 {
@@ -181,7 +185,7 @@ on_finished_cb (GstDiscoverer * discoverer, CustomData * data)
 }
 
 int
-main (int argc, char **argv)
+tutorial_main (int argc, char **argv)
 {
   CustomData data;
   GError *err = NULL;
@@ -238,3 +242,13 @@ main (int argc, char **argv)
 
   return 0;
 }
+
+int
+main (int argc, char *argv[])
+{
+#if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
+  return gst_macos_main (tutorial_main, argc, argv, NULL);
+#else
+  return tutorial_main (argc, argv);
+#endif
+}
index b9daae3e02067ac88dd5fee7310e704aedb11efc..b2f783eef8646b761c36b87c2b5e2c28ec98e520 100644 (file)
@@ -1,6 +1,10 @@
 #include <stdio.h>
 #include <gst/gst.h>
 
+#ifdef __APPLE__
+#include <TargetConditionals.h>
+#endif
+
 /* Structure to contain all our information, so we can pass it around */
 typedef struct _CustomData
 {
@@ -32,7 +36,7 @@ static gboolean handle_keyboard (GIOChannel * source, GIOCondition cond,
     CustomData * data);
 
 int
-main (int argc, char *argv[])
+tutorial_main (int argc, char *argv[])
 {
   CustomData data;
   GstBus *bus;
@@ -240,3 +244,13 @@ handle_keyboard (GIOChannel * source, GIOCondition cond, CustomData * data)
   g_free (str);
   return TRUE;
 }
+
+int
+main (int argc, char *argv[])
+{
+#if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
+  return gst_macos_main (tutorial_main, argc, argv, NULL);
+#else
+  return tutorial_main (argc, argv);
+#endif
+}
index 5e3688ffd6cac7bace032f5b850b3a7329417110..69837db5e7113d00905367781c408119899a6daf 100644 (file)
@@ -1,6 +1,10 @@
 #include <stdio.h>
 #include <gst/gst.h>
 
+#ifdef __APPLE__
+#include <TargetConditionals.h>
+#endif
+
 /* Structure to contain all our information, so we can pass it around */
 typedef struct _CustomData
 {
@@ -32,7 +36,7 @@ static gboolean handle_keyboard (GIOChannel * source, GIOCondition cond,
     CustomData * data);
 
 int
-main (int argc, char *argv[])
+tutorial_main (int argc, char *argv[])
 {
   CustomData data;
   GstBus *bus;
@@ -244,3 +248,13 @@ handle_keyboard (GIOChannel * source, GIOCondition cond, CustomData * data)
   g_free (str);
   return TRUE;
 }
+
+int
+main (int argc, char *argv[])
+{
+#if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
+  return gst_macos_main (tutorial_main, argc, argv, NULL);
+#else
+  return tutorial_main (argc, argv);
+#endif
+}
index b29c8e00a7e837479aafb5971b46d70a36459d81..60a049b5a2160a178c72ded084ad7b084003c27a 100644 (file)
@@ -2,6 +2,10 @@
 #include <gst/audio/audio.h>
 #include <string.h>
 
+#ifdef __APPLE__
+#include <TargetConditionals.h>
+#endif
+
 #define CHUNK_SIZE 1024         /* Amount of bytes we are sending in each buffer */
 #define SAMPLE_RATE 44100       /* Samples per second we are sending */
 
@@ -133,7 +137,7 @@ source_setup (GstElement * pipeline, GstElement * source, CustomData * data)
 }
 
 int
-main (int argc, char *argv[])
+tutorial_main (int argc, char *argv[])
 {
   CustomData data;
   GstBus *bus;
@@ -170,3 +174,13 @@ main (int argc, char *argv[])
   gst_object_unref (data.pipeline);
   return 0;
 }
+
+int
+main (int argc, char *argv[])
+{
+#if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
+  return gst_macos_main (tutorial_main, argc, argv, NULL);
+#else
+  return tutorial_main (argc, argv);
+#endif
+}
index bba675120fba97cf9a5e17061baa720a2e22897b..abbfd92b9b25161f68324389a41e7865241f845f 100644 (file)
@@ -1,6 +1,10 @@
 #include <gst/gst.h>
 #include <string.h>
 
+#ifdef __APPLE__
+#include <TargetConditionals.h>
+#endif
+
 #define GRAPH_LENGTH 78
 
 /* playbin flags */
@@ -122,7 +126,7 @@ refresh_ui (CustomData * data)
 }
 
 int
-main (int argc, char *argv[])
+tutorial_main (int argc, char *argv[])
 {
   GstElement *pipeline;
   GstBus *bus;
@@ -186,3 +190,13 @@ main (int argc, char *argv[])
   g_print ("\n");
   return 0;
 }
+
+int
+main (int argc, char *argv[])
+{
+#if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
+  return gst_macos_main (tutorial_main, argc, argv, NULL);
+#else
+  return tutorial_main (argc, argv);
+#endif
+}
index dc65ae4d1ec244a917f295ad58338417d8255dd6..e397b4d14690b314156636cf954ebaec1b3123d3 100644 (file)
@@ -3,6 +3,10 @@
 #include <gst/gst.h>
 #include <gst/video/colorbalance.h>
 
+#ifdef __APPLE__
+#include <TargetConditionals.h>
+#endif
+
 typedef struct _CustomData
 {
   GstElement *pipeline;
@@ -109,7 +113,7 @@ handle_keyboard (GIOChannel * source, GIOCondition cond, CustomData * data)
 }
 
 int
-main (int argc, char *argv[])
+tutorial_main (int argc, char *argv[])
 {
   CustomData data;
   GstStateChangeReturn ret;
@@ -163,3 +167,13 @@ main (int argc, char *argv[])
   gst_object_unref (data.pipeline);
   return 0;
 }
+
+int
+main (int argc, char *argv[])
+{
+#if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
+  return gst_macos_main (tutorial_main, argc, argv, NULL);
+#else
+  return tutorial_main (argc, argv);
+#endif
+}
index fac6c7f1afcc24aa3f0d9ecbd51a1acc69c9ec7c..052004068ab9a49960b1918081e7e8f6ba7337fd 100644 (file)
@@ -1,5 +1,9 @@
 #include <gst/gst.h>
 
+#ifdef __APPLE__
+#include <TargetConditionals.h>
+#endif
+
 /* playbin2 flags */
 typedef enum
 {
@@ -22,7 +26,7 @@ filter_vis_features (GstPluginFeature * feature, gpointer data)
 }
 
 int
-main (int argc, char *argv[])
+tutorial_main (int argc, char *argv[])
 {
   GstElement *pipeline, *vis_plugin;
   GstBus *bus;
@@ -99,3 +103,13 @@ main (int argc, char *argv[])
   gst_object_unref (pipeline);
   return 0;
 }
+
+int
+main (int argc, char *argv[])
+{
+#if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
+  return gst_macos_main (tutorial_main, argc, argv, NULL);
+#else
+  return tutorial_main (argc, argv);
+#endif
+}
index cd5e061fa40ea9f3ecfbee9e06017eadaf447a1e..58a7b821f2709eb13e0b53c3f58c3444c790c4d9 100644 (file)
@@ -1,7 +1,11 @@
 #include <gst/gst.h>
 
+#ifdef __APPLE__
+#include <TargetConditionals.h>
+#endif
+
 int
-main (int argc, char *argv[])
+tutorial_main (int argc, char *argv[])
 {
   GstElement *pipeline, *bin, *equalizer, *convert, *sink;
   GstPad *pad, *ghost_pad;
@@ -60,3 +64,13 @@ main (int argc, char *argv[])
   gst_object_unref (pipeline);
   return 0;
 }
+
+int
+main (int argc, char *argv[])
+{
+#if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
+  return gst_macos_main (tutorial_main, argc, argv, NULL);
+#else
+  return tutorial_main (argc, argv);
+#endif
+}
index 4423813aec7c0783067e4d5fbccbb8890f22210c..7bd59cd8a4d5ebc18869e0fa84a9a76e0ae73de5 100644 (file)
 /* API compatibility stuff */
 #include <gst/gstcompat.h>
 
+#ifdef __APPLE__
+#      include <TargetConditionals.h>
+#      if TARGET_OS_MAC && !TARGET_OS_IPHONE
+#       include <gst/gstmacos.h>
+#      endif
+#endif
+
 G_BEGIN_DECLS
 
 GST_API
diff --git a/subprojects/gstreamer/gst/gstmacos.h b/subprojects/gstreamer/gst/gstmacos.h
new file mode 100644 (file)
index 0000000..8e1f21a
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef __GST_MACOS_H__
+#define __GST_MACOS_H__
+
+#include <gst/gstconfig.h>
+#include <gst/glib-compat.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GstMainFunc:
+ * @argc: the amount of arguments passed in @argv
+ * @argv: (array length=argc): an array of arguments passed to the main function
+ * @user_data: (nullable): user data passed to the main function
+ *
+ * Represents a simple pointer to the main() function of a program.
+ * It is used to pass that function along with its arguments to
+ * gst_macos_main(), which ensures correct behaviour of various
+ * GStreamer elements (e.g glimagesink) on macOS.
+ */
+typedef int (*GstMainFunc) (int argc, char** argv, gpointer user_data);
+
+/**
+ * GstMainFuncSimple:
+ * @user_data: (nullable): user data passed to the main function
+ *
+ * Simplified version of #GstMainFunc, meant to be used with
+ * gst_macos_main_simple(), which does not require argc/argv to be passed.
+ */
+typedef int (*GstMainFuncSimple) (gpointer user_data);
+
+GST_API
+int              gst_macos_main                   (GstMainFunc main_func,
+                                                   int argc,
+                                                   char *argv[],
+                                                   gpointer user_data);
+
+GST_API
+int              gst_macos_main_simple            (GstMainFuncSimple main_func,
+                                                   gpointer user_data);
+
+G_END_DECLS
+
+#endif /* __GST_MACOS_H__ */
diff --git a/subprojects/gstreamer/gst/gstmacos.m b/subprojects/gstreamer/gst/gstmacos.m
new file mode 100644 (file)
index 0000000..e0cd09b
--- /dev/null
@@ -0,0 +1,101 @@
+#include "gstmacos.h"
+#include <Cocoa/Cocoa.h>
+
+typedef struct _ThreadArgs ThreadArgs;
+
+struct _ThreadArgs {
+  void* main_func;
+  int argc;
+  char **argv;
+  gpointer user_data;
+  gboolean is_simple;
+};
+
+int
+gst_thread_func (ThreadArgs *args)
+{
+  int ret;
+  if (args->is_simple) {
+    ret = ((GstMainFuncSimple) args->main_func) (args->user_data);
+  } else {
+    ret = ((GstMainFunc) args->main_func) (args->argc, args->argv, args->user_data);
+  }
+
+  [NSApp terminate: nil];
+  return ret;
+}
+
+int
+run_main_with_nsapp (ThreadArgs args)
+{
+  GThread *gst_thread;
+
+  [NSApplication sharedApplication]; 
+  gst_thread = g_thread_new ("macos-gst-thread", (GThreadFunc) gst_thread_func, &args);
+  [NSApp run];
+
+  return GPOINTER_TO_INT (g_thread_join (gst_thread));
+}
+
+/**
+ * gst_macos_main:
+ * @main_func: (scope async): pointer to the main function to be called
+ * @argc: the amount of arguments passed in @argv
+ * @argv: (array length=argc): an array of arguments to be passed to the main function
+ * @user_data: (nullable): user data to be passed to the main function
+ *
+ * Starts an NSApplication on the main thread before calling 
+ * the provided main() function on a secondary thread. 
+ * 
+ * This ensures that GStreamer can correctly perform actions 
+ * such as creating a GL window, which require a Cocoa main loop 
+ * to be running on the main thread.
+ *
+ * Do not call this function more than once - especially while
+ * another one is still running - as that will cause unpredictable
+ * behaviour and most likely completely fail.
+ *
+ * Returns: the return value of the provided main_func
+ *
+ * Since: 1.22
+ */
+int
+gst_macos_main (GstMainFunc main_func, int argc, char **argv, gpointer user_data)
+{
+  ThreadArgs args;
+
+  args.argc = argc;
+  args.argv = argv;
+  args.main_func = main_func;
+  args.user_data = user_data;
+  args.is_simple = FALSE;
+
+  return run_main_with_nsapp (args);
+}
+
+/**
+ * gst_macos_main_simple:
+ * @main_func: (scope async): pointer to the main function to be called
+ * @user_data: (nullable): user data to be passed to the main function
+ *
+ * Simplified variant of gst_macos_main(), meant to be used with bindings
+ * for languages which do not have to pass argc and argv like C does.
+ * See gst_macos_main() for a more detailed description.
+ *
+ * Returns: the return value of the provided main_func
+ *
+ * Since: 1.22
+ */
+int 
+gst_macos_main_simple (GstMainFuncSimple main_func, gpointer user_data)
+{
+  ThreadArgs args;
+
+  args.argc = 0;
+  args.argv = NULL;
+  args.main_func = main_func;
+  args.user_data = user_data;
+  args.is_simple = TRUE;
+
+  return run_main_with_nsapp (args);
+}
index 3356b848d4e21b16489a583f47f9586373656391..4293c78e4dceb0f8a5a981a4e5a5a589c368e51a 100644 (file)
@@ -148,6 +148,11 @@ gst_headers = files(
   'gstparse.h',
   'math-compat.h',
 )
+if host_system == 'darwin'
+  gst_headers += 'gstmacos.h'
+  gst_sources += 'gstmacos.m'
+endif
+
 install_headers(gst_headers, subdir : 'gstreamer-1.0/gst')
 
 extra_deps = []
@@ -156,6 +161,10 @@ if host_system == 'android'
   extra_deps += cc.find_library('log')
 endif
 
+if host_system == 'darwin'
+  extra_deps += dependency('appleframeworks', modules : ['Cocoa'])
+endif
+
 gst_registry = get_option('registry')
 if gst_registry
   gst_registry_sources = files('gstregistrybinary.c')
index 7331c661504e27271c828ce98f4eb7827caaadfd..32cb7aef9a364b4fd8b8ba59e94a766722fec08c 100644 (file)
@@ -16,8 +16,6 @@ else
 endif
 gst_version_is_dev = gst_version_minor % 2 == 1 and gst_version_micro < 90
 
-host_system = host_machine.system()
-
 apiversion = '1.0'
 soversion = 0
 # maintaining compatibility with the previous libtool versioning
@@ -33,6 +31,19 @@ libexecdir = get_option('libexecdir')
 helpers_install_dir = join_paths(libexecdir, 'gstreamer-1.0')
 
 cc = meson.get_compiler('c')
+host_system = host_machine.system()
+
+if host_system == 'darwin'
+  ios_test_code = '''#include <TargetConditionals.h>
+  #if ! TARGET_OS_IPHONE
+  #error "Not iOS/tvOS/watchOS/iPhoneSimulator"
+  #endif'''
+  if cc.compiles(ios_test_code, name : 'building for iOS')
+    host_system = 'ios'
+  endif
+
+  add_languages('objc', native: false, required: true)
+endif
 
 cdata = configuration_data()