gl: Add support for multiple views in upload, colour convert and download
authorJan Schmidt <jan@centricular.com>
Fri, 29 May 2015 16:15:51 +0000 (02:15 +1000)
committerJan Schmidt <jan@centricular.com>
Thu, 11 Jun 2015 14:39:15 +0000 (00:39 +1000)
Support multiple attached views on input/output buffers
by processing each one, not just the first.

gst-libs/gst/gl/gstglcolorconvert.c
gst-libs/gst/gl/gstgldownload.c
gst-libs/gst/gl/gstgldownload.h
gst-libs/gst/gl/gstglmemory.c
gst-libs/gst/gl/gstglupload.c

index 62e938c0f5148b35a2796abf9bbdab9f1e7c716d..da903f4d71cc7583e2598b4829e0a67aabf5beba 100644 (file)
@@ -614,6 +614,9 @@ _gst_gl_color_convert_set_caps_unlocked (GstGLColorConvert * convert,
   g_return_val_if_fail (in_caps, FALSE);
   g_return_val_if_fail (out_caps, FALSE);
 
+  GST_LOG_OBJECT (convert, "Setting caps in %" GST_PTR_FORMAT
+      " out %" GST_PTR_FORMAT, in_caps, out_caps);
+
   if (!gst_video_info_from_caps (&in_info, in_caps))
     g_assert_not_reached ();
 
@@ -1507,38 +1510,27 @@ _init_convert_fbo (GstGLColorConvert * convert)
   return TRUE;
 }
 
-/* Called by the idle function in the gl thread */
-void
-_do_convert (GstGLContext * context, GstGLColorConvert * convert)
+static gboolean
+_do_convert_one_view (GstGLContext * context, GstGLColorConvert * convert,
+    guint view_num)
 {
   guint in_width, in_height, out_width, out_height;
   struct ConvertInfo *c_info = &convert->priv->convert_info;
   GstMapInfo out_info[GST_VIDEO_MAX_PLANES], in_info[GST_VIDEO_MAX_PLANES];
   gboolean res = TRUE;
   gint i, j = 0;
+  const gint in_plane_offset = view_num * c_info->in_n_textures;
+  const gint out_plane_offset = view_num * c_info->out_n_textures;
 
   out_width = GST_VIDEO_INFO_WIDTH (&convert->out_info);
   out_height = GST_VIDEO_INFO_HEIGHT (&convert->out_info);
   in_width = GST_VIDEO_INFO_WIDTH (&convert->in_info);
   in_height = GST_VIDEO_INFO_HEIGHT (&convert->in_info);
 
-  convert->outbuf = NULL;
-
-  if (!_init_convert (convert)) {
-    convert->priv->result = FALSE;
-    return;
-  }
-
-  convert->outbuf = gst_buffer_new ();
-  if (!gst_gl_memory_setup_buffer (convert->context, NULL, &convert->out_info,
-          NULL, convert->outbuf)) {
-    convert->priv->result = FALSE;
-    return;
-  }
-
   for (i = 0; i < c_info->in_n_textures; i++) {
     convert->priv->in_tex[i] =
-        (GstGLMemory *) gst_buffer_peek_memory (convert->inbuf, i);
+        (GstGLMemory *) gst_buffer_peek_memory (convert->inbuf,
+        i + in_plane_offset);
     if (!gst_is_gl_memory ((GstMemory *) convert->priv->in_tex[i])) {
       GST_ERROR_OBJECT (convert, "input must be GstGLMemory");
       res = FALSE;
@@ -1555,7 +1547,8 @@ _do_convert (GstGLContext * context, GstGLColorConvert * convert)
 
   for (j = 0; j < c_info->out_n_textures; j++) {
     GstGLMemory *out_tex =
-        (GstGLMemory *) gst_buffer_peek_memory (convert->outbuf, j);
+        (GstGLMemory *) gst_buffer_peek_memory (convert->outbuf,
+        j + out_plane_offset);
     gint mem_width, mem_height;
 
     if (!gst_is_gl_memory ((GstMemory *) out_tex)) {
@@ -1608,7 +1601,8 @@ _do_convert (GstGLContext * context, GstGLColorConvert * convert)
 out:
   for (j--; j >= 0; j--) {
     GstGLMemory *out_tex =
-        (GstGLMemory *) gst_buffer_peek_memory (convert->outbuf, j);
+        (GstGLMemory *) gst_buffer_peek_memory (convert->outbuf,
+        j + out_plane_offset);
     gint mem_width, mem_height;
 
     gst_memory_unmap ((GstMemory *) convert->priv->out_tex[j], &out_info[j]);
@@ -1647,17 +1641,54 @@ out:
 
   /* YV12 the same as I420 except planes 1+2 swapped */
   if (GST_VIDEO_INFO_FORMAT (&convert->out_info) == GST_VIDEO_FORMAT_YV12) {
-    GstMemory *mem1 = gst_buffer_get_memory (convert->outbuf, 1);
-    GstMemory *mem2 = gst_buffer_get_memory (convert->outbuf, 2);
+    GstMemory *mem1 =
+        gst_buffer_get_memory (convert->outbuf, 1 + out_plane_offset);
+    GstMemory *mem2 =
+        gst_buffer_get_memory (convert->outbuf, 2 + out_plane_offset);
 
-    gst_buffer_replace_memory (convert->outbuf, 1, mem2);
-    gst_buffer_replace_memory (convert->outbuf, 2, mem1);
+    gst_buffer_replace_memory (convert->outbuf, 1 + out_plane_offset, mem2);
+    gst_buffer_replace_memory (convert->outbuf, 2 + out_plane_offset, mem1);
   }
 
   for (i--; i >= 0; i--) {
     gst_memory_unmap ((GstMemory *) convert->priv->in_tex[i], &in_info[i]);
   }
 
+  return res;
+}
+
+/* Called by the idle function in the gl thread */
+void
+_do_convert (GstGLContext * context, GstGLColorConvert * convert)
+{
+  GstVideoInfo *in_info = &convert->in_info;
+  gboolean res = TRUE;
+  gint views, v;
+
+  convert->outbuf = NULL;
+
+  if (!_init_convert (convert)) {
+    convert->priv->result = FALSE;
+    return;
+  }
+
+  convert->outbuf = gst_buffer_new ();
+  if (!gst_gl_memory_setup_buffer (convert->context, NULL, &convert->out_info,
+          NULL, convert->outbuf)) {
+    convert->priv->result = FALSE;
+    return;
+  }
+
+  if (GST_VIDEO_INFO_MULTIVIEW_MODE (in_info) ==
+      GST_VIDEO_MULTIVIEW_MODE_SEPARATED)
+    views = GST_VIDEO_INFO_VIEWS (in_info);
+  else
+    views = 1;
+
+  /* Handle all views on input and output one at a time */
+  for (v = 0; res && v < views; v++)
+    res = _do_convert_one_view (context, convert, v);
+
   if (!res) {
     gst_buffer_unref (convert->outbuf);
     convert->outbuf = NULL;
index 1ae49b8afbbe0b13c4766eaaf1ded952fa260292..4bb00599c5e9dbfe5a72cc3ed57a7323198706d0 100644 (file)
@@ -43,8 +43,7 @@
 #define USING_GLES2(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 2, 0))
 #define USING_GLES3(context) (gst_gl_context_check_gl_version (context, GST_GL_API_GLES2, 3, 0))
 
-static gboolean _do_download (GstGLDownload * download, guint texture_id,
-    gpointer data[GST_VIDEO_MAX_PLANES]);
+static gboolean _do_download (GstGLDownload * download, GstBuffer * inbuf);
 static gboolean _init_download (GstGLDownload * download);
 static gboolean _gst_gl_download_perform_with_data_unlocked (GstGLDownload *
     download, GLuint texture_id, GLuint texture_target,
@@ -52,6 +51,9 @@ static gboolean _gst_gl_download_perform_with_data_unlocked (GstGLDownload *
 static void gst_gl_download_reset (GstGLDownload * download);
 
 /* *INDENT-ON* */
+/* Define the maximum number of planes we can handle - max 2 views per buffer */
+#define GST_GL_DOWNLOAD_MAX_VIEWS 2
+#define GST_GL_DOWNLOAD_MAX_PLANES (GST_VIDEO_MAX_PLANES * GST_GL_DOWNLOAD_MAX_VIEWS)
 
 struct _GstGLDownloadPrivate
 {
@@ -61,7 +63,12 @@ struct _GstGLDownloadPrivate
   const gchar *ARGB;
   const gchar *vert_shader;
 
-  GstGLMemory *in_tex[GST_VIDEO_MAX_PLANES];
+  GstBuffer *inbuf;
+  /* Temporary wrapped texture for perform_with_data download */
+  GstGLMemory *in_tex;
+
+  /* Output data planes */
+  gpointer out_data[GST_GL_DOWNLOAD_MAX_PLANES];
 };
 
 GST_DEBUG_CATEGORY_STATIC (gst_gl_download_debug);
@@ -138,13 +145,9 @@ gst_gl_download_finalize (GObject * object)
 static void
 gst_gl_download_reset (GstGLDownload * download)
 {
-  guint i;
-
-  for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) {
-    if (download->priv->in_tex[i]) {
-      gst_memory_unref ((GstMemory *) download->priv->in_tex[i]);
-      download->priv->in_tex[i] = NULL;
-    }
+  if (download->priv->in_tex) {
+    gst_memory_unref ((GstMemory *) download->priv->in_tex);
+    download->priv->in_tex = NULL;
   }
 }
 
@@ -250,7 +253,9 @@ gst_gl_download_transform_caps (GstGLContext * context,
  * @data: (out): where the downloaded data should go
  *
  * Downloads @texture_id into @data. @data size and format is specified by
- * the #GstVideoFormat passed to gst_gl_download_set_format() 
+ * the #GstVideoFormat passed to gst_gl_download_set_format()
+ *
+ * This method can only be used for download a single view.
  *
  * Returns: whether the download was successful
  */
@@ -272,12 +277,16 @@ gst_gl_download_perform_with_data (GstGLDownload * download,
   return ret;
 }
 
+/* This method only supports one input texture */
 static gboolean
 _gst_gl_download_perform_with_data_unlocked (GstGLDownload * download,
     GLuint texture_id, GLuint texture_target,
     gpointer data[GST_VIDEO_MAX_PLANES])
 {
   guint i;
+  gboolean res;
+  GstBuffer *inbuf;
+  guint out_width, out_height;
 
   g_return_val_if_fail (download != NULL, FALSE);
   g_return_val_if_fail (texture_id > 0, FALSE);
@@ -290,36 +299,54 @@ _gst_gl_download_perform_with_data_unlocked (GstGLDownload * download,
     g_return_val_if_fail (data[i] != NULL, FALSE);
   }
 
-  if (!download->priv->in_tex[0]) {
+  if (!download->priv->in_tex) {
     GstVideoInfo temp_info;
 
     gst_video_info_set_format (&temp_info, GST_VIDEO_FORMAT_RGBA,
         GST_VIDEO_INFO_WIDTH (&download->info),
         GST_VIDEO_INFO_HEIGHT (&download->info));
 
-    download->priv->in_tex[0] =
+    download->priv->in_tex =
         gst_gl_memory_wrapped_texture (download->context,
         texture_id, texture_target, &temp_info, 0, NULL, NULL, NULL);
   }
 
-  download->priv->in_tex[0]->tex_id = texture_id;
 
-  return _do_download (download, texture_id, data);
+  out_width = GST_VIDEO_INFO_WIDTH (&download->info);
+  out_height = GST_VIDEO_INFO_HEIGHT (&download->info);
+
+  GST_TRACE ("doing download of texture:%u (%ux%u)",
+      download->priv->in_tex->tex_id, out_width, out_height);
+
+  download->priv->in_tex->tex_id = texture_id;
+
+  inbuf = gst_buffer_new ();
+  gst_buffer_append_memory (inbuf,
+      gst_memory_ref ((GstMemory *) download->priv->in_tex));
+
+  for (i = 0; i < GST_VIDEO_MAX_PLANES; i++)
+    download->priv->out_data[i] = data[i];
+  /* Clear remaining planes for safety */
+  while (i < GST_GL_DOWNLOAD_MAX_PLANES)
+    download->priv->out_data[i++] = NULL;
+
+  res = _do_download (download, inbuf);
+
+  download->priv->inbuf = NULL;
+  gst_buffer_unref (inbuf);
+
+  return res;
 }
 
 static gboolean
 _init_download (GstGLDownload * download)
 {
   GstVideoFormat v_format;
-  guint out_width, out_height;
-  GstVideoInfo in_info;
   GstCaps *in_caps, *out_caps;
-  GstCapsFeatures *in_gl_features, *out_gl_features;
+  GstCapsFeatures *out_gl_features;
   gboolean res;
 
   v_format = GST_VIDEO_INFO_FORMAT (&download->info);
-  out_width = GST_VIDEO_INFO_WIDTH (&download->info);
-  out_height = GST_VIDEO_INFO_HEIGHT (&download->info);
 
   if (download->initted)
     return TRUE;
@@ -336,18 +363,14 @@ _init_download (GstGLDownload * download)
     }
   }
 
-  in_gl_features =
-      gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY);
-  gst_video_info_set_format (&in_info, GST_VIDEO_FORMAT_RGBA, out_width,
-      out_height);
-  in_caps = gst_video_info_to_caps (&in_info);
-  gst_caps_set_features (in_caps, 0, in_gl_features);
-
   out_gl_features =
       gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY);
   out_caps = gst_video_info_to_caps (&download->info);
   gst_caps_set_features (out_caps, 0, out_gl_features);
 
+  in_caps = gst_caps_copy (out_caps);
+  gst_caps_set_simple (in_caps, "format", G_TYPE_STRING, "RGBA", NULL);
+
   res = gst_gl_color_convert_set_caps (download->convert, in_caps, out_caps);
 
   gst_caps_unref (in_caps);
@@ -357,35 +380,38 @@ _init_download (GstGLDownload * download)
 }
 
 static gboolean
-_do_download (GstGLDownload * download, guint texture_id,
-    gpointer data[GST_VIDEO_MAX_PLANES])
+_do_download (GstGLDownload * download, GstBuffer * inbuf)
 {
-  guint out_width, out_height;
-  GstBuffer *inbuf, *outbuf;
+  GstBuffer *outbuf;
   GstMapInfo map_info;
   gboolean ret = TRUE;
   gint i;
-
-  out_width = GST_VIDEO_INFO_WIDTH (&download->info);
-  out_height = GST_VIDEO_INFO_HEIGHT (&download->info);
+  GstVideoInfo *info;
+  guint views, out_planes;
+  gpointer *data = download->priv->out_data;
 
   if (!download->initted) {
-    if (!_init_download (download))
+    if (!_init_download (download)) {
+      GST_DEBUG_OBJECT (download, "Failed to initialise");
       return FALSE;
+    }
   }
 
-  GST_TRACE ("doing download of texture:%u (%ux%u)",
-      download->priv->in_tex[0]->tex_id, out_width, out_height);
-
-  inbuf = gst_buffer_new ();
-  gst_buffer_append_memory (inbuf,
-      gst_memory_ref ((GstMemory *) download->priv->in_tex[0]));
-
   outbuf = gst_gl_color_convert_perform (download->convert, inbuf);
-  if (!outbuf)
+  if (!outbuf) {
+    GST_DEBUG_OBJECT (download, "Failed to colour convert for output");
     return FALSE;
+  }
 
-  for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&download->info); i++) {
+  info = &download->info;
+  if (GST_VIDEO_INFO_MULTIVIEW_MODE (info) ==
+      GST_VIDEO_MULTIVIEW_MODE_SEPARATED)
+    views = GST_VIDEO_INFO_VIEWS (info);
+  else
+    views = 1;
+  out_planes = GST_VIDEO_INFO_N_PLANES (info) * views;
+
+  for (i = 0; i < out_planes; i++) {
     GstMemory *out_mem = gst_buffer_peek_memory (outbuf, i);
     gpointer temp_data = ((GstGLMemory *) out_mem)->data;
     ((GstGLMemory *) out_mem)->data = data[i];
@@ -400,8 +426,80 @@ _do_download (GstGLDownload * download, guint texture_id,
     ((GstGLMemory *) out_mem)->data = temp_data;
   }
 
-  gst_buffer_unref (inbuf);
   gst_buffer_unref (outbuf);
 
   return ret;
 }
+
+static gboolean
+_gst_gl_download_perform_unlocked (GstGLDownload * download,
+    GstBuffer * inbuf, GstBuffer * outbuf)
+{
+  guint i;
+  gboolean res = FALSE;
+  guint out_width, out_height;
+  GstVideoFrame out_frame;
+
+  g_return_val_if_fail (download != NULL, FALSE);
+  g_return_val_if_fail (GST_VIDEO_INFO_FORMAT (&download->info) !=
+      GST_VIDEO_FORMAT_UNKNOWN
+      && GST_VIDEO_INFO_FORMAT (&download->info) != GST_VIDEO_FORMAT_ENCODED,
+      FALSE);
+
+  out_width = GST_VIDEO_INFO_WIDTH (&download->info);
+  out_height = GST_VIDEO_INFO_HEIGHT (&download->info);
+
+  GST_TRACE_OBJECT (download, "doing download of buffer %" GST_PTR_FORMAT
+      " (%ux%u)", inbuf, out_width, out_height);
+
+  /* FIXME: Map multiple views */
+  if (!gst_video_frame_map (&out_frame, &download->info, outbuf, GST_MAP_WRITE))
+    return FALSE;
+
+  for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&download->info); i++) {
+    if (out_frame.data[i] == NULL)
+      goto fail;
+    download->priv->out_data[i] = out_frame.data[i];
+  }
+  while (i < GST_GL_DOWNLOAD_MAX_PLANES)
+    download->priv->out_data[i++] = NULL;
+
+  res = _do_download (download, inbuf);
+
+fail:
+  gst_video_frame_unmap (&out_frame);
+  download->priv->inbuf = NULL;
+
+  return res;
+}
+
+/**
+ * gst_gl_download_perform:
+ * @download: a #GstGLDownload
+ * @inbuf: (transfer none): a #GstBuffer input buffer
+ * @outbuf: (transfer none) (out): a #GstBuffer output buffer
+ *
+ * Downloads the contents of @inbuf into @outbuf.
+ *
+ * The output buffer contents must match the #GstVideoFormat passed
+ * to gst_gl_download_set_format(), and the input buffer must
+ * contain #GstGLMemory memory items.
+ *
+ * This method supports downloading multiple views.
+ *
+ * Returns: whether the download was successful
+ */
+gboolean
+gst_gl_download_perform (GstGLDownload * download,
+    GstBuffer * inbuf, GstBuffer * outbuf)
+{
+  gboolean ret;
+
+  g_return_val_if_fail (download != NULL, FALSE);
+
+  GST_OBJECT_LOCK (download);
+  ret = _gst_gl_download_perform_unlocked (download, inbuf, outbuf);
+  GST_OBJECT_UNLOCK (download);
+
+  return ret;
+}
index 2eeccf476e17a19c79eea502115531b6f7b91af7..d476854d580bf04ea2a641653953e29c3b20aa31 100644 (file)
@@ -81,6 +81,8 @@ GstCaps * gst_gl_download_transform_caps       (GstGLContext * convert,
 gboolean gst_gl_download_perform_with_data          (GstGLDownload * download,
                                                      GLuint texture_id, GLuint texture_target,
                                                      gpointer data[GST_VIDEO_MAX_PLANES]);
+gboolean gst_gl_download_perform (GstGLDownload * download,
+                                  GstBuffer *inbuf, GstBuffer *outbuf);
 
 G_END_DECLS
 
index b93ad5ef86bc6a143ff06ba584744ffedfc60fa6..2deede49f2b9519fa747895c74fd22ba0c5a7205 100644 (file)
@@ -1422,22 +1422,31 @@ gst_gl_memory_setup_buffer (GstGLContext * context,
     GstVideoAlignment * valign, GstBuffer * buffer)
 {
   GstGLMemory *gl_mem[GST_VIDEO_MAX_PLANES] = { NULL, };
-  guint n_mem, i;
+  guint n_mem, i, v, views;
 
   n_mem = GST_VIDEO_INFO_N_PLANES (info);
 
-  for (i = 0; i < n_mem; i++) {
-    gl_mem[i] =
-        (GstGLMemory *) gst_gl_memory_alloc (context, params, info, i, valign);
-    if (gl_mem[i] == NULL)
-      return FALSE;
+  if (GST_VIDEO_INFO_MULTIVIEW_MODE (info) ==
+      GST_VIDEO_MULTIVIEW_MODE_SEPARATED)
+    views = info->views;
+  else
+    views = 1;
 
-    gst_buffer_append_memory (buffer, (GstMemory *) gl_mem[i]);
-  }
+  for (v = 0; v < views; v++) {
+    for (i = 0; i < n_mem; i++) {
+      gl_mem[i] =
+          (GstGLMemory *) gst_gl_memory_alloc (context, params, info, i,
+          valign);
+      if (gl_mem[i] == NULL)
+        return FALSE;
 
-  gst_buffer_add_video_meta_full (buffer, 0,
-      GST_VIDEO_INFO_FORMAT (info), GST_VIDEO_INFO_WIDTH (info),
-      GST_VIDEO_INFO_HEIGHT (info), n_mem, info->offset, info->stride);
+      gst_buffer_append_memory (buffer, (GstMemory *) gl_mem[i]);
+    }
+
+    gst_buffer_add_video_meta_full (buffer, v,
+        GST_VIDEO_INFO_FORMAT (info), GST_VIDEO_INFO_WIDTH (info),
+        GST_VIDEO_INFO_HEIGHT (info), n_mem, info->offset, info->stride);
+  }
 
   return TRUE;
 }
index e5f1ff0efd83632e0d1c7d478cfdc8274480891b..e089a13c4a28d818243f63c6eab471d0c8675fba 100644 (file)
@@ -50,6 +50,9 @@
 GST_DEBUG_CATEGORY_STATIC (gst_gl_upload_debug);
 #define GST_CAT_DEFAULT gst_gl_upload_debug
 
+/* Define the maximum number of planes we can upload - handle 2 views per buffer */
+#define GST_GL_UPLOAD_MAX_PLANES (GST_VIDEO_MAX_PLANES * 2)
+
 typedef struct _UploadMethod UploadMethod;
 
 struct _GstGLUploadPrivate
@@ -162,12 +165,18 @@ _gl_memory_upload_accept (gpointer impl, GstBuffer * buffer, GstCaps * in_caps,
     return FALSE;
 
   if (buffer) {
-    if (gst_buffer_n_memory (buffer) !=
-        GST_VIDEO_INFO_N_PLANES (&upload->upload->priv->in_info))
+    GstVideoInfo *in_info = &upload->upload->priv->in_info;
+    guint expected_memories = GST_VIDEO_INFO_N_PLANES (in_info);
+
+    /* Support stereo views for separated multiview mode */
+    if (GST_VIDEO_INFO_MULTIVIEW_MODE (in_info) ==
+        GST_VIDEO_MULTIVIEW_MODE_SEPARATED)
+      expected_memories *= GST_VIDEO_INFO_VIEWS (in_info);
+
+    if (gst_buffer_n_memory (buffer) != expected_memories)
       return FALSE;
 
-    for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&upload->upload->priv->in_info);
-        i++) {
+    for (i = 0; i < expected_memories; i++) {
       GstMemory *mem = gst_buffer_peek_memory (buffer, i);
 
       if (!gst_is_gl_memory (mem))
@@ -250,9 +259,10 @@ _gl_memory_upload_perform (gpointer impl, GstBuffer * buffer,
 {
   struct GLMemoryUpload *upload = impl;
   GstGLMemory *gl_mem;
-  int i;
+  int i, n;
 
-  for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&upload->upload->priv->in_info); i++) {
+  n = gst_buffer_n_memory (buffer);
+  for (i = 0; i < n; i++) {
     GstMemory *mem = gst_buffer_peek_memory (buffer, i);
 
     gl_mem = (GstGLMemory *) mem;
@@ -357,12 +367,18 @@ _egl_image_upload_accept (gpointer impl, GstBuffer * buffer, GstCaps * in_caps,
     return FALSE;
 
   if (buffer) {
-    if (gst_buffer_n_memory (buffer) !=
-        GST_VIDEO_INFO_N_PLANES (&image->upload->priv->in_info))
+    GstVideoInfo *in_info = &image->upload->priv->in_info;
+    guint expected_memories = GST_VIDEO_INFO_N_PLANES (in_info);
+
+    /* Support stereo views for separated multiview mode */
+    if (GST_VIDEO_INFO_MULTIVIEW_MODE (in_info) ==
+        GST_VIDEO_MULTIVIEW_MODE_SEPARATED)
+      expected_memories *= GST_VIDEO_INFO_VIEWS (in_info);
+
+    if (gst_buffer_n_memory (buffer) != expected_memories)
       return FALSE;
 
-    for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&image->upload->priv->in_info);
-        i++) {
+    for (i = 0; i < expected_memories; i++) {
       GstMemory *mem = gst_buffer_peek_memory (buffer, i);
 
       if (!gst_is_egl_image_memory (mem))
@@ -395,14 +411,15 @@ static void
 _egl_image_upload_perform_gl_thread (GstGLContext * context,
     struct EGLImageUpload *image)
 {
-  guint i;
+  guint i, n;
 
   /* FIXME: buffer pool */
   *image->outbuf = gst_buffer_new ();
   gst_gl_memory_setup_buffer (image->upload->context,
       NULL, &image->upload->priv->out_info, NULL, *image->outbuf);
 
-  for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&image->upload->priv->in_info); i++) {
+  n = gst_buffer_n_memory (image->buffer);
+  for (i = 0; i < n; i++) {
     GstMemory *mem = gst_buffer_peek_memory (image->buffer, i);
     GstGLMemory *out_gl_mem =
         (GstGLMemory *) gst_buffer_peek_memory (*image->outbuf, i);
@@ -474,7 +491,7 @@ struct GLUploadMeta
 
   gboolean result;
   GstVideoGLTextureUploadMeta *meta;
-  guint texture_ids[GST_VIDEO_MAX_PLANES];
+  guint texture_ids[GST_GL_UPLOAD_MAX_PLANES];
 };
 
 static gpointer
@@ -606,6 +623,13 @@ _upload_meta_upload_perform (gpointer impl, GstBuffer * buffer,
 {
   struct GLUploadMeta *upload = impl;
   int i;
+  GstVideoInfo *in_info = &upload->upload->priv->in_info;
+  guint max_planes = GST_VIDEO_INFO_N_PLANES (in_info);
+
+  /* Support stereo views for separated multiview mode */
+  if (GST_VIDEO_INFO_MULTIVIEW_MODE (in_info) ==
+      GST_VIDEO_MULTIVIEW_MODE_SEPARATED)
+    max_planes *= GST_VIDEO_INFO_VIEWS (in_info);
 
   GST_LOG_OBJECT (upload, "Attempting upload with GstVideoGLTextureUploadMeta");
 
@@ -616,10 +640,10 @@ _upload_meta_upload_perform (gpointer impl, GstBuffer * buffer,
   gst_gl_memory_setup_buffer (upload->upload->context,
       NULL, &upload->upload->priv->in_info, NULL, *outbuf);
 
-  for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) {
+  for (i = 0; i < GST_GL_UPLOAD_MAX_PLANES; i++) {
     guint tex_id = 0;
 
-    if (i < GST_VIDEO_INFO_N_PLANES (&upload->upload->priv->in_info)) {
+    if (i < max_planes) {
       GstMemory *mem = gst_buffer_peek_memory (*outbuf, i);
       tex_id = ((GstGLMemory *) mem)->tex_id;
     }
@@ -627,9 +651,12 @@ _upload_meta_upload_perform (gpointer impl, GstBuffer * buffer,
     upload->texture_ids[i] = tex_id;
   }
 
-  GST_LOG ("Uploading with GLTextureUploadMeta with textures %i,%i,%i,%i",
-      upload->texture_ids[0], upload->texture_ids[1], upload->texture_ids[2],
-      upload->texture_ids[3]);
+  GST_LOG ("Uploading with GLTextureUploadMeta with textures "
+      "%i,%i,%i,%i / %i,%i,%i,%i",
+      upload->texture_ids[0], upload->texture_ids[1],
+      upload->texture_ids[2], upload->texture_ids[3],
+      upload->texture_ids[4], upload->texture_ids[5],
+      upload->texture_ids[6], upload->texture_ids[7]);
 
   gst_gl_context_thread_add (upload->upload->context,
       (GstGLContextThreadFunc) _do_upload_with_meta, upload);
@@ -653,7 +680,7 @@ _upload_meta_upload_free (gpointer impl)
 
   g_return_if_fail (impl != NULL);
 
-  for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) {
+  for (i = 0; i < GST_GL_UPLOAD_MAX_PLANES; i++) {
     if (upload->texture_ids[i])
       gst_gl_context_del_texture (upload->upload->context,
           &upload->texture_ids[i]);
@@ -750,14 +777,21 @@ static GstGLUploadReturn
 _raw_data_upload_perform (gpointer impl, GstBuffer * buffer,
     GstBuffer ** outbuf)
 {
-  GstGLMemory *in_tex[GST_VIDEO_MAX_PLANES] = { 0, };
+  GstGLMemory *in_tex[GST_GL_UPLOAD_MAX_PLANES] = { 0, };
   struct RawUpload *raw = impl;
   int i;
+  GstVideoInfo *in_info = &raw->upload->priv->in_info;
+  guint max_planes = GST_VIDEO_INFO_N_PLANES (in_info);
+
+  /* Support stereo views for separated multiview mode */
+  if (GST_VIDEO_INFO_MULTIVIEW_MODE (in_info) ==
+      GST_VIDEO_MULTIVIEW_MODE_SEPARATED)
+    max_planes *= GST_VIDEO_INFO_VIEWS (in_info);
 
   gst_gl_memory_setup_wrapped (raw->upload->context,
       &raw->upload->priv->in_info, NULL, raw->in_frame.data, in_tex);
 
-  for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) {
+  for (i = 0; i < GST_GL_UPLOAD_MAX_PLANES; i++) {
     if (in_tex[i]) {
       in_tex[i]->data = raw->in_frame.data[i];
       GST_GL_MEMORY_FLAG_SET (in_tex[i], GST_GL_MEMORY_FLAG_NEED_UPLOAD);
@@ -765,7 +799,7 @@ _raw_data_upload_perform (gpointer impl, GstBuffer * buffer,
   }
 
   *outbuf = gst_buffer_new ();
-  for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&raw->upload->priv->in_info); i++) {
+  for (i = 0; i < max_planes; i++) {
     gst_buffer_append_memory (*outbuf, (GstMemory *) in_tex[i]);
   }