예제 #1
0
static void
gst_gl_buffer_pool_finalize (GObject * object)
{
  GstGLBufferPool *pool = GST_GL_BUFFER_POOL_CAST (object);
  GstGLBufferPoolPrivate *priv = pool->priv;

  GST_LOG_OBJECT (pool, "finalize GL buffer pool %p", pool);

  if (priv->caps)
    gst_caps_unref (priv->caps);

  G_OBJECT_CLASS (gst_gl_buffer_pool_parent_class)->finalize (object);

  /* only release the context once all our memory have been deleted */
  if (pool->context) {
    gst_object_unref (pool->context);
    pool->context = NULL;
  }

  if (priv->allocator) {
    gst_object_unref (priv->allocator);
    priv->allocator = NULL;
  }

  if (priv->gl_params)
    gst_gl_allocation_params_free ((GstGLAllocationParams *) priv->gl_params);
  priv->gl_params = NULL;
}
예제 #2
0
static void
init (gpointer data)
{
  GstGLContext *context = data;
  GError *error = NULL;
  GstVideoInfo v_info;
  GstGLMemoryAllocator *allocator;
  GstGLVideoAllocationParams *params;

  gst_video_info_set_format (&v_info, GST_VIDEO_FORMAT_RGBA, 320, 240);
  allocator = gst_gl_memory_allocator_get_default (context);
  params =
      gst_gl_video_allocation_params_new (context, NULL, &v_info, 0, NULL,
      GST_GL_TEXTURE_TARGET_2D, GST_GL_RGBA);

  /* has to be called in the thread that is going to use the framebuffer */
  fbo = gst_gl_framebuffer_new_with_default_depth (context, 320, 240);

  fail_if (fbo == NULL, "failed to create framebuffer object");

  gl_tex =
      (GstGLMemory *) gst_gl_base_memory_alloc ((GstGLBaseMemoryAllocator *)
      allocator, (GstGLAllocationParams *) params);
  gl_tex2 =
      (GstGLMemory *) gst_gl_base_memory_alloc ((GstGLBaseMemoryAllocator *)
      allocator, (GstGLAllocationParams *) params);
  gst_object_unref (allocator);
  gst_gl_allocation_params_free ((GstGLAllocationParams *) params);
  fail_if (gl_tex == NULL, "failed to create texture");

  shader = gst_gl_shader_new_default (context, &error);
  fail_if (shader == NULL, "failed to create shader object: %s",
      error->message);

  shader_attr_position_loc =
      gst_gl_shader_get_attribute_location (shader, "a_position");
  shader_attr_texture_loc =
      gst_gl_shader_get_attribute_location (shader, "a_texcoord");
}
예제 #3
0
static gboolean
_gl_mem_create (GstGLMemoryPBO * gl_mem, GError ** error)
{
  GstGLContext *context = gl_mem->mem.mem.context;
  GstGLBaseMemoryAllocatorClass *alloc_class;

  alloc_class = GST_GL_BASE_MEMORY_ALLOCATOR_CLASS (parent_class);
  if (!alloc_class->create ((GstGLBaseMemory *) gl_mem, error))
    return FALSE;

  if (CONTEXT_SUPPORTS_PBO_DOWNLOAD (context)
      || CONTEXT_SUPPORTS_PBO_UPLOAD (context)) {
    GstAllocationParams alloc_params =
        { 0, GST_MEMORY_CAST (gl_mem)->align, 0, 0 };
    GstGLBaseMemoryAllocator *buf_allocator;
    GstGLBufferAllocationParams *params;

    buf_allocator =
        GST_GL_BASE_MEMORY_ALLOCATOR (gst_allocator_find
        (GST_GL_BUFFER_ALLOCATOR_NAME));
    params =
        gst_gl_buffer_allocation_params_new (context,
        GST_MEMORY_CAST (gl_mem)->size, &alloc_params, GL_PIXEL_UNPACK_BUFFER,
        GL_STREAM_DRAW);

    /* FIXME: lazy init this for resource constrained platforms
     * Will need to fix pbo detection based on the existence of the mem.id then */
    gl_mem->pbo = (GstGLBuffer *) gst_gl_base_memory_alloc (buf_allocator,
        (GstGLAllocationParams *) params);

    gst_gl_allocation_params_free ((GstGLAllocationParams *) params);
    gst_object_unref (buf_allocator);

    GST_CAT_LOG (GST_CAT_GL_MEMORY, "generated pbo %u", gl_mem->pbo->id);
  }

  return TRUE;
}
static void
test_transfer_allocator (const gchar * allocator_name)
{
  GstAllocator *gl_allocator;
  GstGLBaseMemoryAllocator *base_mem_alloc;
  GstVideoInfo v_info;
  GstMemory *mem, *mem2, *mem3;
  GstMapInfo map_info;
  GstGLVideoAllocationParams *params;

  gl_allocator = gst_allocator_find (allocator_name);
  fail_if (gl_allocator == NULL);
  base_mem_alloc = GST_GL_BASE_MEMORY_ALLOCATOR (gl_allocator);

  gst_video_info_set_format (&v_info, GST_VIDEO_FORMAT_RGBA, 1, 1);

  params = gst_gl_video_allocation_params_new (context, NULL, &v_info, 0,
      NULL, GST_GL_TEXTURE_TARGET_2D, GST_VIDEO_GL_TEXTURE_TYPE_RGBA);

  /* texture creation */
  mem = (GstMemory *) gst_gl_base_memory_alloc (base_mem_alloc,
      (GstGLAllocationParams *) params);
  gst_gl_allocation_params_free ((GstGLAllocationParams *) params);
  fail_unless (!GST_MEMORY_FLAG_IS_SET (mem,
          GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD));
  fail_unless (!GST_MEMORY_FLAG_IS_SET (mem,
          GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD));

  /* test wrapping raw data */
  params = gst_gl_video_allocation_params_new_wrapped_data (context, NULL,
      &v_info, 0, NULL, GST_GL_TEXTURE_TARGET_2D,
      GST_VIDEO_GL_TEXTURE_TYPE_RGBA, rgba_pixel, NULL, NULL);
  mem2 =
      (GstMemory *) gst_gl_base_memory_alloc (base_mem_alloc,
      (GstGLAllocationParams *) params);
  gst_gl_allocation_params_free ((GstGLAllocationParams *) params);
  fail_if (mem == NULL);

  fail_unless (GST_MEMORY_FLAG_IS_SET (mem2,
          GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD));
  fail_unless (!GST_MEMORY_FLAG_IS_SET (mem2,
          GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD));

  /* wrapped texture creation */
  params = gst_gl_video_allocation_params_new_wrapped_texture (context, NULL,
      &v_info, 0, NULL, GST_GL_TEXTURE_TARGET_2D,
      GST_VIDEO_GL_TEXTURE_TYPE_RGBA, ((GstGLMemory *) mem)->tex_id, NULL,
      NULL);
  mem3 =
      (GstMemory *) gst_gl_base_memory_alloc (base_mem_alloc,
      (GstGLAllocationParams *) params);
  gst_gl_allocation_params_free ((GstGLAllocationParams *) params);
  fail_unless (!GST_MEMORY_FLAG_IS_SET (mem3,
          GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD));
  fail_unless (GST_MEMORY_FLAG_IS_SET (mem3,
          GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD));

  /* check data/flags are correct */
  fail_unless (gst_memory_map (mem2, &map_info, GST_MAP_READ));

  fail_unless (GST_MEMORY_FLAG_IS_SET (mem2,
          GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD));
  fail_unless (!GST_MEMORY_FLAG_IS_SET (mem2,
          GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD));

  fail_unless (memcmp (map_info.data, rgba_pixel,
          G_N_ELEMENTS (rgba_pixel)) == 0,
      "0x%02x%02x%02x%02x != 0x%02x%02x%02x%02x", map_info.data[0],
      map_info.data[1], map_info.data[2], map_info.data[3],
      (guint8) rgba_pixel[0], (guint8) rgba_pixel[1], (guint8) rgba_pixel[2],
      (guint8) rgba_pixel[3]);

  gst_memory_unmap (mem2, &map_info);

  fail_unless (GST_MEMORY_FLAG_IS_SET (mem2,
          GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD));
  fail_unless (!GST_MEMORY_FLAG_IS_SET (mem2,
          GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD));

  fail_unless (gst_memory_map (mem2, &map_info, GST_MAP_READ | GST_MAP_GL));

  fail_unless (!GST_MEMORY_FLAG_IS_SET (mem2,
          GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD));
  fail_unless (!GST_MEMORY_FLAG_IS_SET (mem2,
          GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD));

  /* test texture copy */
  fail_unless (gst_gl_memory_copy_into ((GstGLMemory *) mem2,
          ((GstGLMemory *) mem)->tex_id, GST_GL_TEXTURE_TARGET_2D,
          GST_VIDEO_GL_TEXTURE_TYPE_RGBA, 1, 1));
  GST_MINI_OBJECT_FLAG_SET (mem, GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD);

  fail_unless (!GST_MEMORY_FLAG_IS_SET (mem2,
          GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD));
  fail_unless (!GST_MEMORY_FLAG_IS_SET (mem2,
          GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD));
  fail_unless (!GST_MEMORY_FLAG_IS_SET (mem,
          GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD));
  fail_unless (GST_MEMORY_FLAG_IS_SET (mem,
          GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD));

  gst_memory_unmap (mem2, &map_info);

  /* test download of copied texture */
  fail_unless (gst_memory_map (mem, &map_info, GST_MAP_READ));

  fail_unless (memcmp (map_info.data, rgba_pixel,
          G_N_ELEMENTS (rgba_pixel)) == 0,
      "0x%02x%02x%02x%02x != 0x%02x%02x%02x%02x", (guint8) map_info.data[0],
      (guint8) map_info.data[1], (guint8) map_info.data[2],
      (guint8) map_info.data[3], (guint8) rgba_pixel[0], (guint8) rgba_pixel[1],
      (guint8) rgba_pixel[2], (guint8) rgba_pixel[3]);

  gst_memory_unmap (mem, &map_info);

  /* test download of wrapped copied texture */
  fail_unless (gst_memory_map (mem3, &map_info, GST_MAP_READ));

  fail_unless (!GST_MEMORY_FLAG_IS_SET (mem,
          GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD));
  fail_unless (!GST_MEMORY_FLAG_IS_SET (mem,
          GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD));

  fail_unless (memcmp (map_info.data, rgba_pixel,
          G_N_ELEMENTS (rgba_pixel)) == 0,
      "0x%02x%02x%02x%02x != 0x%02x%02x%02x%02x", (guint8) map_info.data[0],
      (guint8) map_info.data[1], (guint8) map_info.data[2],
      (guint8) map_info.data[3], (guint8) rgba_pixel[0], (guint8) rgba_pixel[1],
      (guint8) rgba_pixel[2], (guint8) rgba_pixel[3]);

  gst_memory_unmap (mem3, &map_info);

  /* test upload flag */
  fail_unless (gst_memory_map (mem3, &map_info, GST_MAP_WRITE));
  gst_memory_unmap (mem3, &map_info);

  fail_unless (GST_MEMORY_FLAG_IS_SET (mem3,
          GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD));
  fail_unless (!GST_MEMORY_FLAG_IS_SET (mem3,
          GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD));

  /* test download flag */
  fail_unless (gst_memory_map (mem3, &map_info, GST_MAP_WRITE | GST_MAP_GL));
  gst_memory_unmap (mem3, &map_info);

  fail_unless (!GST_MEMORY_FLAG_IS_SET (mem3,
          GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD));
  fail_unless (GST_MEMORY_FLAG_IS_SET (mem3,
          GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD));

  gst_memory_unref (mem);
  gst_memory_unref (mem2);
  gst_memory_unref (mem3);
  gst_object_unref (gl_allocator);
}
예제 #5
0
static gboolean
gst_gl_buffer_pool_set_config (GstBufferPool * pool, GstStructure * config)
{
  GstGLBufferPool *glpool = GST_GL_BUFFER_POOL_CAST (pool);
  GstGLBufferPoolPrivate *priv = glpool->priv;
  GstVideoInfo info;
  GstCaps *caps = NULL;
  guint min_buffers, max_buffers;
  guint max_align, n;
  GstAllocator *allocator = NULL;
  GstAllocationParams alloc_params;
  GstGLTextureTarget tex_target;
  gboolean ret = TRUE;
  gint p;

  if (!gst_buffer_pool_config_get_params (config, &caps, NULL, &min_buffers,
          &max_buffers))
    goto wrong_config;

  if (caps == NULL)
    goto no_caps;

  /* now parse the caps from the config */
  if (!gst_video_info_from_caps (&info, caps))
    goto wrong_caps;

  GST_LOG_OBJECT (pool, "%dx%d, caps %" GST_PTR_FORMAT, info.width, info.height,
      caps);

  if (!gst_buffer_pool_config_get_allocator (config, &allocator, &alloc_params))
    goto wrong_config;

  gst_caps_replace (&priv->caps, caps);

  if (priv->allocator)
    gst_object_unref (priv->allocator);

  if (allocator) {
    if (!GST_IS_GL_MEMORY_ALLOCATOR (allocator)) {
      gst_object_unref (allocator);
      goto wrong_allocator;
    } else {
      priv->allocator = gst_object_ref (allocator);
    }
  } else {
    priv->allocator =
        GST_ALLOCATOR (gst_gl_memory_allocator_get_default (glpool->context));
    g_assert (priv->allocator);
  }

  priv->add_videometa = gst_buffer_pool_config_has_option (config,
      GST_BUFFER_POOL_OPTION_VIDEO_META);
  priv->add_glsyncmeta = gst_buffer_pool_config_has_option (config,
      GST_BUFFER_POOL_OPTION_GL_SYNC_META);

  if (priv->gl_params)
    gst_gl_allocation_params_free ((GstGLAllocationParams *) priv->gl_params);
  priv->gl_params = (GstGLVideoAllocationParams *)
      gst_buffer_pool_config_get_gl_allocation_params (config);
  if (!priv->gl_params)
    priv->gl_params = gst_gl_video_allocation_params_new (glpool->context,
        &alloc_params, &info, -1, NULL, 0, 0);

  max_align = alloc_params.align;

  if (gst_buffer_pool_config_has_option (config,
          GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT)) {
    priv->add_videometa = TRUE;

    gst_buffer_pool_config_get_video_alignment (config,
        priv->gl_params->valign);

    for (n = 0; n < GST_VIDEO_MAX_PLANES; ++n)
      max_align |= priv->gl_params->valign->stride_align[n];

    for (n = 0; n < GST_VIDEO_MAX_PLANES; ++n)
      priv->gl_params->valign->stride_align[n] = max_align;

    gst_video_info_align (priv->gl_params->v_info, priv->gl_params->valign);

    gst_buffer_pool_config_set_video_alignment (config,
        priv->gl_params->valign);
  }

  if (alloc_params.align < max_align) {
    GST_WARNING_OBJECT (pool, "allocation params alignment %u is smaller "
        "than the max specified video stride alignment %u, fixing",
        (guint) alloc_params.align, max_align);

    alloc_params.align = max_align;
    gst_buffer_pool_config_set_allocator (config, allocator, &alloc_params);
    if (priv->gl_params->parent.alloc_params)
      gst_allocation_params_free (priv->gl_params->parent.alloc_params);
    priv->gl_params->parent.alloc_params =
        gst_allocation_params_copy (&alloc_params);
  }

  {
    GstStructure *s = gst_caps_get_structure (caps, 0);
    const gchar *target_str = gst_structure_get_string (s, "texture-target");
    gboolean multiple_texture_targets = FALSE;

    tex_target = priv->gl_params->target;
    if (target_str)
      tex_target = gst_gl_texture_target_from_string (target_str);

    if (gst_buffer_pool_config_has_option (config,
            GST_BUFFER_POOL_OPTION_GL_TEXTURE_TARGET_2D)) {
      if (tex_target && tex_target != GST_GL_TEXTURE_TARGET_2D)
        multiple_texture_targets = TRUE;
      tex_target = GST_GL_TEXTURE_TARGET_2D;
    }
    if (gst_buffer_pool_config_has_option (config,
            GST_BUFFER_POOL_OPTION_GL_TEXTURE_TARGET_RECTANGLE)) {
      if (tex_target && tex_target != GST_GL_TEXTURE_TARGET_RECTANGLE)
        multiple_texture_targets = TRUE;
      tex_target = GST_GL_TEXTURE_TARGET_RECTANGLE;
    }
    if (gst_buffer_pool_config_has_option (config,
            GST_BUFFER_POOL_OPTION_GL_TEXTURE_TARGET_EXTERNAL_OES)) {
      if (tex_target && tex_target != GST_GL_TEXTURE_TARGET_EXTERNAL_OES)
        multiple_texture_targets = TRUE;
      tex_target = GST_GL_TEXTURE_TARGET_EXTERNAL_OES;
    }

    if (!tex_target)
      tex_target = GST_GL_TEXTURE_TARGET_2D;

    if (multiple_texture_targets) {
      GST_WARNING_OBJECT (pool, "Multiple texture targets configured either "
          "through caps or buffer pool options");
      ret = FALSE;
    }

    priv->gl_params->target = tex_target;
  }

  /* Recalulate the size and offset as we don't add padding between planes. */
  priv->gl_params->v_info->size = 0;
  for (p = 0; p < GST_VIDEO_INFO_N_PLANES (priv->gl_params->v_info); p++) {
    priv->gl_params->v_info->offset[p] = priv->gl_params->v_info->size;
    priv->gl_params->v_info->size +=
        gst_gl_get_plane_data_size (priv->gl_params->v_info,
        priv->gl_params->valign, p);
  }

  gst_buffer_pool_config_set_params (config, caps,
      priv->gl_params->v_info->size, min_buffers, max_buffers);

  return GST_BUFFER_POOL_CLASS (parent_class)->set_config (pool, config) && ret;

  /* ERRORS */
wrong_config:
  {
    GST_WARNING_OBJECT (pool, "invalid config");
    return FALSE;
  }
no_caps:
  {
    GST_WARNING_OBJECT (pool, "no caps in config");
    return FALSE;
  }
wrong_caps:
  {
    GST_WARNING_OBJECT (pool,
        "failed getting geometry from caps %" GST_PTR_FORMAT, caps);
    return FALSE;
  }
wrong_allocator:
  {
    GST_WARNING_OBJECT (pool, "Incorrect allocator type for this pool");
    return FALSE;
  }
}
static void
gst_gl_composition_overlay_upload (GstGLCompositionOverlay * overlay,
    GstBuffer * buf)
{
  GstGLMemory *comp_gl_memory = NULL;
  GstBuffer *comp_buffer = NULL;
  GstBuffer *overlay_buffer = NULL;
  GstVideoInfo vinfo;
  GstVideoMeta *vmeta;
  GstVideoFrame *comp_frame;
  GstVideoFrame gl_frame;

  comp_buffer =
      gst_video_overlay_rectangle_get_pixels_unscaled_argb (overlay->rectangle,
      GST_VIDEO_OVERLAY_FORMAT_FLAG_PREMULTIPLIED_ALPHA);

  comp_frame = g_slice_new (GstVideoFrame);

  vmeta = gst_buffer_get_video_meta (comp_buffer);
  gst_video_info_set_format (&vinfo, vmeta->format, vmeta->width,
      vmeta->height);
  vinfo.stride[0] = vmeta->stride[0];

  if (gst_video_frame_map (comp_frame, &vinfo, comp_buffer, GST_MAP_READ)) {
    GstGLVideoAllocationParams *params;
    GstGLBaseMemoryAllocator *mem_allocator;
    GstAllocator *allocator;

    allocator =
        GST_ALLOCATOR (gst_gl_memory_allocator_get_default (overlay->context));
    mem_allocator = GST_GL_BASE_MEMORY_ALLOCATOR (allocator);

    gst_gl_composition_overlay_add_transformation (overlay, buf);

    params = gst_gl_video_allocation_params_new_wrapped_data (overlay->context,
        NULL, &comp_frame->info, 0, NULL, GST_GL_TEXTURE_TARGET_2D,
        GST_GL_RGBA, comp_frame->data[0], comp_frame,
        _video_frame_unmap_and_free);

    comp_gl_memory =
        (GstGLMemory *) gst_gl_base_memory_alloc (mem_allocator,
        (GstGLAllocationParams *) params);

    gst_gl_allocation_params_free ((GstGLAllocationParams *) params);
    gst_object_unref (allocator);

    overlay_buffer = gst_buffer_new ();
    gst_buffer_append_memory (overlay_buffer, (GstMemory *) comp_gl_memory);

    if (!gst_video_frame_map (&gl_frame, &comp_frame->info, overlay_buffer,
            GST_MAP_READ | GST_MAP_GL)) {
      gst_buffer_unref (overlay_buffer);
      _video_frame_unmap_and_free (comp_frame);
      GST_WARNING_OBJECT (overlay, "Cannot upload overlay texture");
      return;
    }

    gst_memory_ref ((GstMemory *) comp_gl_memory);
    overlay->gl_memory = comp_gl_memory;
    overlay->texture_id = comp_gl_memory->tex_id;

    gst_buffer_unref (overlay_buffer);
    gst_video_frame_unmap (&gl_frame);

    GST_DEBUG ("uploaded overlay texture %d", overlay->texture_id);
  } else {
    g_slice_free (GstVideoFrame, comp_frame);
  }
}
static void
check_conversion (TestFrame * frames, guint size)
{
  GstGLBaseMemoryAllocator *base_mem_alloc;
  gint i, j, k, l;
  gint ref_count = 0;

  base_mem_alloc =
      GST_GL_BASE_MEMORY_ALLOCATOR (gst_allocator_find
      (GST_GL_MEMORY_ALLOCATOR_NAME));

  for (i = 0; i < size; i++) {
    GstBuffer *inbuf;
    GstVideoInfo in_info;
    gint in_width = frames[i].width;
    gint in_height = frames[i].height;
    GstVideoFormat in_v_format = frames[i].v_format;
    GstVideoFrame in_frame;
    GstCaps *in_caps;

    gst_video_info_set_format (&in_info, in_v_format, in_width, in_height);
    in_caps = gst_video_info_to_caps (&in_info);
    gst_caps_set_features (in_caps, 0,
        gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY));

    /* create GL buffer */
    inbuf = gst_buffer_new ();
    for (j = 0; j < GST_VIDEO_INFO_N_PLANES (&in_info); j++) {
      GstVideoGLTextureType tex_type = gst_gl_texture_type_from_format (context,
          GST_VIDEO_INFO_FORMAT (&in_info), j);
      GstGLVideoAllocationParams *params;
      GstGLBaseMemory *mem;

      ref_count++;
      params = gst_gl_video_allocation_params_new_wrapped_data (context, NULL,
          &in_info, j, NULL, GST_GL_TEXTURE_TARGET_2D, tex_type,
          frames[i].data[j], &ref_count, _frame_unref);

      mem = gst_gl_base_memory_alloc (base_mem_alloc,
          (GstGLAllocationParams *) params);
      gst_buffer_append_memory (inbuf, GST_MEMORY_CAST (mem));

      gst_gl_allocation_params_free ((GstGLAllocationParams *) params);
    }

    fail_unless (gst_video_frame_map (&in_frame, &in_info, inbuf,
            GST_MAP_READ));

    /* sanity check that the correct values were wrapped */
    for (j = 0; j < GST_VIDEO_INFO_N_PLANES (&in_info); j++) {
      for (k = 0; k < _video_info_plane_size (&in_info, j); k++) {
        if (frames[i].data[j][k] != IGNORE_MAGIC)
          fail_unless (((gchar *) in_frame.data[j])[k] == frames[i].data[j][k]);
      }
    }

    for (j = 0; j < size; j++) {
      GstBuffer *outbuf;
      GstVideoInfo out_info;
      gint out_width = frames[j].width;
      gint out_height = frames[j].height;
      GstVideoFormat out_v_format = frames[j].v_format;
      gchar *out_data[GST_VIDEO_MAX_PLANES] = { 0 };
      GstVideoFrame out_frame;
      GstCaps *out_caps;

      gst_video_info_set_format (&out_info, out_v_format, out_width,
          out_height);
      out_caps = gst_video_info_to_caps (&out_info);
      gst_caps_set_features (out_caps, 0,
          gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY));

      for (k = 0; k < GST_VIDEO_INFO_N_PLANES (&out_info); k++) {
        out_data[k] = frames[j].data[k];
      }

      gst_gl_color_convert_set_caps (convert, in_caps, out_caps);

      /* convert the data */
      outbuf = gst_gl_color_convert_perform (convert, inbuf);
      if (outbuf == NULL) {
        const gchar *in_str = gst_video_format_to_string (in_v_format);
        const gchar *out_str = gst_video_format_to_string (out_v_format);
        GST_WARNING ("failed to convert from %s to %s", in_str, out_str);
      }

      fail_unless (gst_video_frame_map (&out_frame, &out_info, outbuf,
              GST_MAP_READ));

      /* check that the converted values are correct */
      for (k = 0; k < GST_VIDEO_INFO_N_PLANES (&out_info); k++) {
        for (l = 0; l < _video_info_plane_size (&out_info, k); l++) {
          gchar out_pixel = ((gchar *) out_frame.data[k])[l];
          if (out_data[k][l] != IGNORE_MAGIC && out_pixel != IGNORE_MAGIC)
            fail_unless (out_pixel == out_data[k][l]);
          /* FIXME: check alpha clobbering */
        }
      }

      gst_caps_unref (out_caps);
      gst_video_frame_unmap (&out_frame);
      gst_buffer_unref (outbuf);
    }

    gst_caps_unref (in_caps);
    gst_video_frame_unmap (&in_frame);
    gst_buffer_unref (inbuf);

    fail_unless_equals_int (ref_count, 0);
  }

  gst_object_unref (base_mem_alloc);
}