diff --git a/ext/gl/gstglimagesink.c b/ext/gl/gstglimagesink.c index 6d1c8d046c..c06f580bcc 100644 --- a/ext/gl/gstglimagesink.c +++ b/ext/gl/gstglimagesink.c @@ -620,6 +620,11 @@ gst_glimage_sink_stop (GstBaseSink * bsink) glimage_sink->pool = NULL; } + if (glimage_sink->gl_caps) { + gst_caps_unref (glimage_sink->gl_caps); + glimage_sink->gl_caps = NULL; + } + return TRUE; } @@ -764,6 +769,7 @@ gst_glimage_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) GstVideoInfo vinfo; GstStructure *structure; GstBufferPool *newpool, *oldpool; + GstCapsFeatures *gl_features; GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps); @@ -848,7 +854,16 @@ gst_glimage_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) gst_object_unref (glimage_sink->upload); glimage_sink->upload = gst_gl_upload_new (glimage_sink->context); - gst_gl_upload_set_format (glimage_sink->upload, &vinfo); + gl_features = + gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY); + if (glimage_sink->gl_caps) + gst_caps_unref (glimage_sink->gl_caps); + glimage_sink->gl_caps = gst_caps_copy (caps); + gst_caps_set_simple (glimage_sink->gl_caps, "format", G_TYPE_STRING, "RGBA", + NULL); + gst_caps_set_features (glimage_sink->gl_caps, 0, gl_features); + + gst_gl_upload_set_caps (glimage_sink->upload, caps, glimage_sink->gl_caps); glimage_sink->caps_change = TRUE; return TRUE; @@ -859,6 +874,8 @@ gst_glimage_sink_prepare (GstBaseSink * bsink, GstBuffer * buf) { GstGLImageSink *glimage_sink; GstBuffer *next_buffer = NULL; + GstVideoFrame gl_frame; + GstVideoInfo gl_info; glimage_sink = GST_GLIMAGE_SINK (bsink); @@ -873,12 +890,24 @@ gst_glimage_sink_prepare (GstBaseSink * bsink, GstBuffer * buf) return GST_FLOW_NOT_NEGOTIATED; if (!gst_gl_upload_perform_with_buffer (glimage_sink->upload, buf, - &glimage_sink->next_tex, &next_buffer)) + &next_buffer)) goto upload_failed; + gst_video_info_from_caps (&gl_info, glimage_sink->gl_caps); + + if (!gst_video_frame_map (&gl_frame, &gl_info, next_buffer, + GST_MAP_READ | GST_MAP_GL)) { + gst_buffer_unref (next_buffer); + goto upload_failed; + } + + glimage_sink->next_tex = *(guint *) gl_frame.data[0]; + gst_buffer_replace (&glimage_sink->next_buffer, next_buffer); gst_buffer_unref (next_buffer); + gst_video_frame_unmap (&gl_frame); + if (glimage_sink->window_id != glimage_sink->new_window_id) { GstGLWindow *window = gst_gl_context_get_window (glimage_sink->context); diff --git a/ext/gl/gstglimagesink.h b/ext/gl/gstglimagesink.h index 4c1842dbac..c6fd4d8ae3 100644 --- a/ext/gl/gstglimagesink.h +++ b/ext/gl/gstglimagesink.h @@ -61,6 +61,7 @@ struct _GstGLImageSink //caps GstVideoInfo info; + GstCaps *gl_caps; GstGLDisplay *display; GstGLContext *context; diff --git a/ext/gl/gstglmixer.c b/ext/gl/gstglmixer.c index a10d10d37c..cd77aaf90c 100644 --- a/ext/gl/gstglmixer.c +++ b/ext/gl/gstglmixer.c @@ -1064,23 +1064,55 @@ gst_gl_mixer_process_textures (GstGLMixer * mix, GstBuffer * outbuf) walk = g_list_next (walk); if (vaggpad->buffer != NULL) { - guint in_tex; + GstBuffer *gl_buf; + GstCaps *gl_caps; + GstCapsFeatures *gl_features; + GstVideoInfo gl_info; + GstVideoFrame gl_frame; + + gst_video_info_set_format (&gl_info, + GST_VIDEO_FORMAT_RGBA, + GST_VIDEO_INFO_WIDTH (&vaggpad->info), + GST_VIDEO_INFO_HEIGHT (&vaggpad->info)); + gl_features = + gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY); + + gl_caps = gst_video_info_to_caps (&gl_info); + gst_caps_set_features (gl_caps, 0, gl_features); if (!pad->upload) { + GstCaps *in_caps = gst_pad_get_current_caps (GST_PAD (pad)); + pad->upload = gst_gl_upload_new (mix->context); - gst_gl_upload_set_format (pad->upload, &vaggpad->info); + gst_gl_upload_set_caps (pad->upload, in_caps, gl_caps); + + gst_caps_unref (in_caps); } if (!gst_gl_upload_perform_with_buffer (pad->upload, - vaggpad->buffer, &in_tex, NULL)) { + vaggpad->buffer, &gl_buf)) { ++array_index; pad->mapped = FALSE; + gst_caps_unref (gl_caps); + continue; + } + + if (!gst_video_frame_map (&gl_frame, &gl_info, gl_buf, + GST_MAP_READ | GST_MAP_GL)) { + ++array_index; + pad->mapped = FALSE; + gst_buffer_unref (gl_buf); + gst_caps_unref (gl_caps); continue; } pad->mapped = TRUE; - frame->texture = in_tex; + frame->texture = *(guint *) gl_frame.data[0]; + + gst_caps_unref (gl_caps); + gst_video_frame_unmap (&gl_frame); + gst_buffer_unref (gl_buf); } ++array_index; } diff --git a/gst-libs/gst/gl/egl/gsteglimagememory.c b/gst-libs/gst/gl/egl/gsteglimagememory.c index 5a023a52fa..c78bed1bea 100644 --- a/gst-libs/gst/gl/egl/gsteglimagememory.c +++ b/gst-libs/gst/gl/egl/gsteglimagememory.c @@ -25,27 +25,10 @@ #endif #include "gsteglimagememory.h" -#include "gstglcontext_egl.h" GST_DEBUG_CATEGORY_STATIC (GST_CAT_EGL_IMAGE_MEMORY); #define GST_CAT_DEFAULT GST_CAT_EGL_IMAGE_MEMORY -typedef void (*GstEGLImageDestroyNotify) (GstGLContextEGL * context, - gpointer data); - -typedef struct -{ - GstMemory parent; - - GstGLContextEGL *context; - EGLImageKHR image; - GstVideoGLTextureType type; - GstVideoGLTextureOrientation orientation; - - gpointer user_data; - GstEGLImageDestroyNotify user_data_destroy; -} GstEGLImageMemory; - #define GST_EGL_IMAGE_MEMORY(mem) ((GstEGLImageMemory*)(mem)) gboolean @@ -317,13 +300,7 @@ gst_eglimage_to_gl_texture_upload_meta (GstVideoGLTextureUploadMeta * gl = GST_GL_CONTEXT (GST_EGL_IMAGE_MEMORY (mem)->context)->gl_vtable; - if (i == 0) - gl->ActiveTexture (GL_TEXTURE0); - else if (i == 1) - gl->ActiveTexture (GL_TEXTURE1); - else if (i == 2) - gl->ActiveTexture (GL_TEXTURE2); - + gl->ActiveTexture (GL_TEXTURE0 + i); gl->BindTexture (GL_TEXTURE_2D, texture_id[i]); gl->EGLImageTargetTexture2D (GL_TEXTURE_2D, gst_egl_image_memory_get_image (mem)); diff --git a/gst-libs/gst/gl/egl/gsteglimagememory.h b/gst-libs/gst/gl/egl/gsteglimagememory.h index bfcbaaa1dc..ce1fd4493e 100644 --- a/gst-libs/gst/gl/egl/gsteglimagememory.h +++ b/gst-libs/gst/gl/egl/gsteglimagememory.h @@ -29,13 +29,32 @@ #include #include +#include "gstglcontext_egl.h" G_BEGIN_DECLS +typedef void (*GstEGLImageDestroyNotify) (GstGLContextEGL * context, + gpointer data); + +typedef struct _GstEGLImageMemory GstEGLImageMemory; + #define GST_EGL_IMAGE_MEMORY_TYPE "EGLImage" #define GST_CAPS_FEATURE_MEMORY_EGL_IMAGE "memory:EGLImage" +struct _GstEGLImageMemory +{ + GstMemory parent; + + GstGLContextEGL *context; + EGLImageKHR image; + GstVideoGLTextureType type; + GstVideoGLTextureOrientation orientation; + + gpointer user_data; + GstEGLImageDestroyNotify user_data_destroy; +}; + void gst_egl_image_memory_init (void); gboolean gst_egl_image_memory_setup_buffer (GstGLContext * context, GstVideoInfo * info, GstBuffer * buffer); gboolean gst_is_egl_image_memory (GstMemory * mem); diff --git a/gst-libs/gst/gl/gstglfilter.c b/gst-libs/gst/gl/gstglfilter.c index 79688ad494..42445e4513 100644 --- a/gst-libs/gst/gl/gstglfilter.c +++ b/gst-libs/gst/gl/gstglfilter.c @@ -298,6 +298,11 @@ gst_gl_filter_reset (GstGLFilter * filter) if (filter->context) gst_object_unref (filter->context); filter->context = NULL; + + if (filter->in_gl_caps) { + gst_caps_unref (filter->in_gl_caps); + filter->in_gl_caps = NULL; + } } static gboolean @@ -1185,7 +1190,8 @@ gst_gl_filter_filter_texture (GstGLFilter * filter, GstBuffer * inbuf, { GstGLFilterClass *filter_class; guint in_tex, out_tex; - GstVideoFrame out_frame; + GstVideoFrame gl_frame, out_frame; + GstVideoInfo gl_info; gboolean ret; gboolean to_download = gst_caps_features_is_equal (GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY, @@ -1199,9 +1205,21 @@ gst_gl_filter_filter_texture (GstGLFilter * filter, GstBuffer * inbuf, filter->uploaded_buffer = NULL; } - if (!gst_gl_upload_perform_with_buffer (filter->upload, inbuf, &in_tex, - &filter->uploaded_buffer)) - return FALSE; + if (!gst_gl_upload_perform_with_buffer (filter->upload, inbuf, + &filter->uploaded_buffer)) { + ret = FALSE; + goto upload_error; + } + + gst_video_info_from_caps (&gl_info, filter->in_gl_caps); + + if (!gst_video_frame_map (&gl_frame, &gl_info, filter->uploaded_buffer, + GST_MAP_READ | GST_MAP_GL)) { + ret = FALSE; + goto upload_error; + } + + in_tex = *(guint *) gl_frame.data[0]; to_download |= !gst_is_gl_memory (gst_buffer_peek_memory (outbuf, 0)); @@ -1240,11 +1258,11 @@ gst_gl_filter_filter_texture (GstGLFilter * filter, GstBuffer * inbuf, GST_ELEMENT_ERROR (filter, RESOURCE, NOT_FOUND, ("%s", "Failed to download video frame"), (NULL)); ret = FALSE; - goto error; } } -error: + gst_video_frame_unmap (&gl_frame); +upload_error: gst_video_frame_unmap (&out_frame); inbuf_error: gst_gl_upload_release_buffer (filter->upload); @@ -1267,8 +1285,28 @@ gst_gl_filter_transform (GstBaseTransform * bt, GstBuffer * inbuf, return GST_FLOW_NOT_NEGOTIATED; if (!filter->upload) { + GstCaps *in_caps = + gst_pad_get_current_caps (GST_BASE_TRANSFORM_SINK_PAD (bt)); + GstCapsFeatures *out_features; + GstVideoInfo out_info; + + gst_video_info_set_format (&out_info, + GST_VIDEO_FORMAT_RGBA, + GST_VIDEO_INFO_WIDTH (&filter->in_info), + GST_VIDEO_INFO_HEIGHT (&filter->in_info)); + out_features = + gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY); + + if (filter->in_gl_caps) + gst_caps_unref (filter->in_gl_caps); + filter->in_gl_caps = gst_video_info_to_caps (&out_info); + gst_caps_set_features (filter->in_gl_caps, 0, out_features); + filter->upload = gst_gl_upload_new (filter->context); - gst_gl_upload_set_format (filter->upload, &filter->in_info); + if (!gst_gl_upload_set_caps (filter->upload, in_caps, filter->in_gl_caps)) + return GST_FLOW_ERROR; + + gst_caps_unref (in_caps); } g_assert (filter_class->filter || filter_class->filter_texture); diff --git a/gst-libs/gst/gl/gstglfilter.h b/gst-libs/gst/gl/gstglfilter.h index 2a8d0dfcce..0df56f6638 100644 --- a/gst-libs/gst/gl/gstglfilter.h +++ b/gst-libs/gst/gl/gstglfilter.h @@ -88,6 +88,8 @@ struct _GstGLFilter GstGLContext *context; GstGLContext *other_context; + GstCaps *in_gl_caps; + #if GST_GL_HAVE_GLES2 GLint draw_attr_position_loc; GLint draw_attr_texture_loc; diff --git a/gst-libs/gst/gl/gstglupload.c b/gst-libs/gst/gl/gstglupload.c index 7cbdbdbcf0..c95375f39f 100644 --- a/gst-libs/gst/gl/gstglupload.c +++ b/gst-libs/gst/gl/gstglupload.c @@ -47,30 +47,522 @@ #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 _upload_memory (GstGLUpload * upload); -static gboolean _init_upload (GstGLUpload * upload); -static gboolean _gst_gl_upload_perform_with_data_unlocked (GstGLUpload * upload, - GLuint * texture_id, gpointer data[GST_VIDEO_MAX_PLANES], - GstBuffer ** outbuf); -static void _do_upload_with_meta (GstGLContext * context, GstGLUpload * upload); -static void gst_gl_upload_reset (GstGLUpload * upload); +GST_DEBUG_CATEGORY_STATIC (gst_gl_upload_debug); +#define GST_CAT_DEFAULT gst_gl_upload_debug + +typedef struct _UploadMethod UploadMethod; struct _GstGLUploadPrivate { - gboolean result; - guint tex_id; + GstVideoInfo in_info; + GstVideoInfo out_info; + GstCaps *in_caps; + GstCaps *out_caps; - gboolean mapped; - GstVideoFrame frame; - - GstVideoGLTextureUploadMeta *meta; + GstGLColorConvert *convert; GstBuffer *outbuf; - gboolean released; + + UploadMethod *method; + gpointer method_impl; + int method_i; }; -GST_DEBUG_CATEGORY_STATIC (gst_gl_upload_debug); -#define GST_CAT_DEFAULT gst_gl_upload_debug +typedef enum +{ + GST_GL_UPLOAD_DONE = 1, + GST_GL_UPLOAD_CONVERT_GL_MEMORY = 2, + + GST_GL_UPLOAD_ERROR = -1, + GST_GL_UPLOAD_UNSUPPORTED = -2, + GST_GL_UPLOAD_UNSHARED_GL_CONTEXT = 3, +} GstGLUploadReturn; + +typedef enum +{ + METHOD_FLAG_CAN_SHARE_CONTEXT = 1, +} GstGLUploadMethodFlags; + +struct _UploadMethod +{ + const gchar *name; + GstGLUploadMethodFlags flags; + + gpointer (*new) (GstGLUpload * upload); + gboolean (*accept) (gpointer impl, GstBuffer * buffer, GstCaps * in_caps, + GstCaps * out_caps); + GstGLUploadReturn (*perform) (gpointer impl, GstBuffer * buffer, + GstBuffer ** outbuf); + void (*release) (gpointer impl, GstBuffer * buffer); + void (*free) (gpointer impl); +} _UploadMethod; + +struct GLMemoryUpload +{ + GstGLUpload *upload; +}; + +static gpointer +_gl_memory_upload_new (GstGLUpload * upload) +{ + struct GLMemoryUpload *mem = g_new0 (struct GLMemoryUpload, 1); + + mem->upload = upload; + + return mem; +} + +static gboolean +_gl_memory_upload_accept (gpointer impl, GstBuffer * buffer, GstCaps * in_caps, + GstCaps * out_caps) +{ + struct GLMemoryUpload *upload = impl; + GstCapsFeatures *features, *gl_features; + gboolean ret = TRUE; + int i; + + gl_features = + gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY); + + features = gst_caps_get_features (in_caps, 0); + if (!gst_caps_features_is_equal (features, gl_features)) + ret = FALSE; + + features = gst_caps_get_features (out_caps, 0); + if (!gst_caps_features_is_equal (features, gl_features)) + ret = FALSE; + + gst_caps_features_free (gl_features); + + if (!ret) + return FALSE; + + if (gst_buffer_n_memory (buffer) != + GST_VIDEO_INFO_N_PLANES (&upload->upload->priv->in_info)) + return FALSE; + + for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&upload->upload->priv->in_info); i++) { + GstMemory *mem = gst_buffer_peek_memory (buffer, i); + + if (!gst_is_gl_memory (mem)) + return FALSE; + } + + return TRUE; +} + +static GstGLUploadReturn +_gl_memory_upload_perform (gpointer impl, GstBuffer * buffer, + GstBuffer ** outbuf) +{ + struct GLMemoryUpload *upload = impl; + GstGLMemory *gl_mem; + int i; + + for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&upload->upload->priv->in_info); i++) { + GstMemory *mem = gst_buffer_peek_memory (buffer, i); + + gl_mem = (GstGLMemory *) mem; + if (!gst_gl_context_can_share (upload->upload->context, gl_mem->context)) + return GST_GL_UPLOAD_UNSHARED_GL_CONTEXT; + } + + *outbuf = gst_buffer_ref (buffer); + + return GST_GL_UPLOAD_CONVERT_GL_MEMORY; +} + +static void +_gl_memory_upload_release (gpointer impl, GstBuffer * buffer) +{ +} + +static void +_gl_memory_upload_free (gpointer impl) +{ + g_free (impl); +} + +static UploadMethod _gl_memory_upload = { + "GLMemory", + METHOD_FLAG_CAN_SHARE_CONTEXT, + &_gl_memory_upload_new, + &_gl_memory_upload_accept, + &_gl_memory_upload_perform, + &_gl_memory_upload_release, + &_gl_memory_upload_free +}; + +#if GST_GL_HAVE_PLATFORM_EGL +struct EGLImageUpload +{ + GstGLUpload *upload; +}; + +static gpointer +_egl_image_upload_new (GstGLUpload * upload) +{ + struct EGLImageUpload *image = g_new0 (struct EGLImageUpload, 1); + + image->upload = upload; + + return image; +} + +static gboolean +_egl_image_upload_accept (gpointer impl, GstBuffer * buffer, GstCaps * in_caps, + GstCaps * out_caps) +{ + struct EGLImageUpload *image = impl; + GstCapsFeatures *features, *gl_features; + gboolean ret = TRUE; + int i; + + gl_features = + gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_EGL_IMAGE); + features = gst_caps_get_features (in_caps, 0); + if (!gst_caps_features_is_equal (features, gl_features)) + ret = FALSE; + + gst_caps_features_free (gl_features); + + gl_features = + gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY); + features = gst_caps_get_features (out_caps, 0); + if (!gst_caps_features_is_equal (features, gl_features)) + ret = FALSE; + + gst_caps_features_free (gl_features); + + if (!ret) + return FALSE; + + if (gst_buffer_n_memory (buffer) != + GST_VIDEO_INFO_N_PLANES (&image->upload->priv->in_info)) + return FALSE; + + for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&image->upload->priv->in_info); i++) { + GstMemory *mem = gst_buffer_peek_memory (buffer, i); + + if (!gst_is_egl_image_memory (mem)) + return FALSE; + } + + return TRUE; +} + +static GstGLUploadReturn +_egl_image_upload_perform (gpointer impl, GstBuffer * buffer, + GstBuffer ** outbuf) +{ + struct EGLImageUpload *image = impl; + guint i; + + /* FIXME: buffer pool */ + *outbuf = gst_buffer_new (); + gst_gl_memory_setup_buffer (image->upload->context, + &image->upload->priv->out_info, *outbuf); + + for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&image->upload->priv->in_info); i++) { + GstMemory *mem = gst_buffer_peek_memory (buffer, i); + GstGLMemory *out_gl_mem = + (GstGLMemory *) gst_buffer_peek_memory (*outbuf, i); + const GstGLFuncs *gl = NULL; + + gl = GST_GL_CONTEXT (((GstEGLImageMemory *) mem)->context)->gl_vtable; + + gl->ActiveTexture (GL_TEXTURE0 + i); + gl->BindTexture (GL_TEXTURE_2D, out_gl_mem->tex_id); + gl->EGLImageTargetTexture2D (GL_TEXTURE_2D, + gst_egl_image_memory_get_image (mem)); + } + + if (GST_IS_GL_BUFFER_POOL (buffer->pool)) + gst_gl_buffer_pool_replace_last_buffer (GST_GL_BUFFER_POOL (buffer->pool), + buffer); + + return GST_GL_UPLOAD_CONVERT_GL_MEMORY; +} + +static void +_egl_image_upload_release (gpointer impl, GstBuffer * buffer) +{ +} + +static void +_egl_image_upload_free (gpointer impl) +{ +} + +static UploadMethod _egl_image_upload = { + "EGLImage", + 0, + &_egl_image_upload_new, + &_egl_image_upload_accept, + &_egl_image_upload_perform, + &_egl_image_upload_release, + &_egl_image_upload_free +}; +#endif + +struct GLUploadMeta +{ + GstGLUpload *upload; + + gboolean result; + GstVideoGLTextureUploadMeta *meta; + guint texture_ids[GST_VIDEO_MAX_PLANES]; +}; + +static gpointer +_upload_meta_upload_new (GstGLUpload * upload) +{ + struct GLUploadMeta *meta = g_new0 (struct GLUploadMeta, 1); + + meta->upload = upload; + + return meta; +} + +static gboolean +_upload_meta_upload_accept (gpointer impl, GstBuffer * buffer, + GstCaps * in_caps, GstCaps * out_caps) +{ + struct GLUploadMeta *upload = impl; + GstCapsFeatures *features, *gl_features; + GstVideoGLTextureUploadMeta *meta; + gboolean ret = TRUE; + + features = gst_caps_get_features (in_caps, 0); + gl_features = + gst_caps_features_from_string + (GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META); + + if (!gst_caps_features_is_equal (features, gl_features)) + ret = FALSE; + + gst_caps_features_free (gl_features); + + if ((meta = gst_buffer_get_video_gl_texture_upload_meta (buffer)) == NULL) + return FALSE; + + gl_features = + gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY); + features = gst_caps_get_features (out_caps, 0); + if (!gst_caps_features_is_equal (features, gl_features)) + ret = FALSE; + + gst_caps_features_free (gl_features); + + if (!ret) + return ret; + + if (meta->texture_type[0] != GST_VIDEO_GL_TEXTURE_TYPE_RGBA) { + GST_FIXME_OBJECT (upload, "only single rgba texture supported"); + return FALSE; + } + + if (meta->texture_orientation != + GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_NORMAL) { + GST_FIXME_OBJECT (upload, "only x-normal, y-normal textures supported"); + return FALSE; + } + + return TRUE; +} + +/* + * Uploads using gst_video_gl_texture_upload_meta_upload(). + * i.e. consumer of GstVideoGLTextureUploadMeta + */ +static void +_do_upload_with_meta (GstGLContext * context, struct GLUploadMeta *upload) +{ + if (!gst_video_gl_texture_upload_meta_upload (upload->meta, + upload->texture_ids)) { + upload->result = FALSE; + return; + } + + upload->result = TRUE; +} + +static GstGLUploadReturn +_upload_meta_upload_perform (gpointer impl, GstBuffer * buffer, + GstBuffer ** outbuf) +{ + struct GLUploadMeta *upload = impl; + int i; + + GST_LOG_OBJECT (upload, "Attempting upload with GstVideoGLTextureUploadMeta"); + + upload->meta = gst_buffer_get_video_gl_texture_upload_meta (buffer); + + /* FIXME: buffer pool */ + *outbuf = gst_buffer_new (); + gst_gl_memory_setup_buffer (upload->upload->context, + &upload->upload->priv->in_info, *outbuf); + + for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) { + guint tex_id = 0; + + if (i < GST_VIDEO_INFO_N_PLANES (&upload->upload->priv->in_info)) { + GstMemory *mem = gst_buffer_peek_memory (*outbuf, i); + tex_id = ((GstGLMemory *) mem)->tex_id; + } + + 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_gl_context_thread_add (upload->upload->context, + (GstGLContextThreadFunc) _do_upload_with_meta, upload); + + if (!upload->result) + return GST_GL_UPLOAD_ERROR; + + return GST_GL_UPLOAD_CONVERT_GL_MEMORY; +} + +static void +_upload_meta_upload_release (gpointer impl, GstBuffer * buffer) +{ +} + +static void +_upload_meta_upload_free (gpointer impl) +{ + struct GLUploadMeta *upload = impl; + gint i; + + for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) { + if (upload->texture_ids[i]); + gst_gl_context_del_texture (upload->upload->context, + &upload->texture_ids[i]); + } +} + +static UploadMethod _upload_meta_upload = { + "UploadMeta", + METHOD_FLAG_CAN_SHARE_CONTEXT, + &_upload_meta_upload_new, + &_upload_meta_upload_accept, + &_upload_meta_upload_perform, + &_upload_meta_upload_release, + &_upload_meta_upload_free +}; + +struct RawUpload +{ + GstGLUpload *upload; + GstGLMemory *in_tex[GST_VIDEO_MAX_PLANES]; + GstVideoFrame in_frame; +}; + +static gpointer +_raw_data_upload_new (GstGLUpload * upload) +{ + struct RawUpload *raw = g_new0 (struct RawUpload, 1); + + raw->upload = upload; + + return raw; +} + +static gboolean +_raw_data_upload_accept (gpointer impl, GstBuffer * buffer, GstCaps * in_caps, + GstCaps * out_caps) +{ + struct RawUpload *raw = impl; + GstCapsFeatures *features, *gl_features; + gboolean ret = TRUE; + + gl_features = + gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_GL_MEMORY); + features = gst_caps_get_features (out_caps, 0); + if (!gst_caps_features_is_equal (features, gl_features)) + ret = FALSE; + + gst_caps_features_free (gl_features); + + if (!ret) + return ret; + + if (!gst_video_frame_map (&raw->in_frame, &raw->upload->priv->in_info, buffer, + GST_MAP_READ)) + return FALSE; + + raw->upload->priv->in_info = raw->in_frame.info; + + return TRUE; +} + +static GstGLUploadReturn +_raw_data_upload_perform (gpointer impl, GstBuffer * buffer, + GstBuffer ** outbuf) +{ + struct RawUpload *raw = impl; + int i; + + if (!raw->in_tex[0]) + gst_gl_memory_setup_wrapped (raw->upload->context, + &raw->upload->priv->in_info, raw->in_frame.data, raw->in_tex); + + for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) { + if (raw->in_tex[i]) { + raw->in_tex[i]->data = raw->in_frame.data[i]; + GST_GL_MEMORY_FLAG_SET (raw->in_tex[i], GST_GL_MEMORY_FLAG_NEED_UPLOAD); + } + } + + *outbuf = gst_buffer_new (); + for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&raw->upload->priv->in_info); i++) { + gst_buffer_append_memory (*outbuf, + gst_memory_ref ((GstMemory *) raw->in_tex[i])); + } + + return GST_GL_UPLOAD_CONVERT_GL_MEMORY; +} + +static void +_raw_data_upload_release (gpointer impl, GstBuffer * buffer) +{ + struct RawUpload *raw = impl; + + gst_video_frame_unmap (&raw->in_frame); +} + +static void +_raw_data_upload_free (gpointer impl) +{ + struct RawUpload *raw = impl; + int i; + + for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) { + if (raw->in_tex[i]) + gst_memory_unref ((GstMemory *) raw->in_tex[i]); + } +} + +static UploadMethod _raw_data_upload = { + "Raw Data", + 0, + &_raw_data_upload_new, + &_raw_data_upload_accept, + &_raw_data_upload_perform, + &_raw_data_upload_release, + &_raw_data_upload_free +}; + +static UploadMethod *upload_methods[] = { &_gl_memory_upload, +#if GST_GL_HAVE_PLATFORM_EGL + &_egl_image_upload, +#endif + &_upload_meta_upload, &_raw_data_upload +}; #define DEBUG_INIT \ GST_DEBUG_CATEGORY_INIT (gst_gl_upload_debug, "glupload", 0, "upload"); @@ -78,6 +570,7 @@ GST_DEBUG_CATEGORY_STATIC (gst_gl_upload_debug); G_DEFINE_TYPE_WITH_CODE (GstGLUpload, gst_gl_upload, GST_TYPE_OBJECT, DEBUG_INIT); static void gst_gl_upload_finalize (GObject * object); +static void gst_gl_upload_release_buffer_unlocked (GstGLUpload * upload); #define GST_GL_UPLOAD_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \ GST_TYPE_GL_UPLOAD, GstGLUploadPrivate)) @@ -94,11 +587,6 @@ static void gst_gl_upload_init (GstGLUpload * upload) { upload->priv = GST_GL_UPLOAD_GET_PRIVATE (upload); - - upload->context = NULL; - upload->priv->tex_id = 0; - - gst_video_info_set_format (&upload->in_info, GST_VIDEO_FORMAT_ENCODED, 0, 0); } /** @@ -110,12 +598,9 @@ gst_gl_upload_init (GstGLUpload * upload) GstGLUpload * gst_gl_upload_new (GstGLContext * context) { - GstGLUpload *upload; - - upload = g_object_new (GST_TYPE_GL_UPLOAD, NULL); + GstGLUpload *upload = g_object_new (GST_TYPE_GL_UPLOAD, NULL); upload->context = gst_object_ref (context); - upload->convert = gst_gl_color_convert_new (context); return upload; } @@ -127,104 +612,157 @@ gst_gl_upload_finalize (GObject * object) upload = GST_GL_UPLOAD (object); - gst_gl_upload_reset (upload); + gst_gl_upload_release_buffer_unlocked (upload); + + if (upload->priv->method_impl) + upload->priv->method->free (upload->priv->method_impl); + upload->priv->method_i = 0; if (upload->context) { gst_object_unref (upload->context); upload->context = NULL; } + if (upload->priv->convert) { + gst_object_unref (upload->priv->convert); + upload->priv->convert = NULL; + } + + if (upload->priv->in_caps) { + gst_caps_unref (upload->priv->in_caps); + upload->priv->in_caps = NULL; + } + + if (upload->priv->out_caps) { + gst_caps_unref (upload->priv->out_caps); + upload->priv->out_caps = NULL; + } + G_OBJECT_CLASS (gst_gl_upload_parent_class)->finalize (object); } -static void -gst_gl_upload_reset (GstGLUpload * upload) +static gboolean +_gst_gl_upload_set_caps_unlocked (GstGLUpload * upload, GstCaps * in_caps, + GstCaps * out_caps) { - guint i; + g_return_val_if_fail (upload != NULL, FALSE); + g_return_val_if_fail (gst_caps_is_fixed (in_caps), FALSE); - if (upload->priv->tex_id) { - gst_gl_context_del_texture (upload->context, &upload->priv->tex_id); - upload->priv->tex_id = 0; - } + if (upload->priv->in_caps && upload->priv->out_caps + && gst_caps_is_equal (upload->priv->in_caps, in_caps) + && gst_caps_is_equal (upload->priv->out_caps, out_caps)) + return TRUE; - if (upload->convert) { - gst_object_unref (upload->convert); - upload->convert = NULL; - } + gst_caps_replace (&upload->priv->in_caps, in_caps); + gst_caps_replace (&upload->priv->out_caps, out_caps); - if (upload->out_tex) { - gst_memory_unref ((GstMemory *) upload->out_tex); - upload->out_tex = NULL; - } + gst_video_info_from_caps (&upload->priv->in_info, in_caps); + gst_video_info_from_caps (&upload->priv->out_info, out_caps); - for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) { - if (upload->in_tex[i]) { - gst_memory_unref ((GstMemory *) upload->in_tex[i]); - upload->in_tex[i] = NULL; - } - } + if (upload->priv->method_impl) + upload->priv->method->free (upload->priv->method_impl); + upload->priv->method_impl = NULL; + upload->priv->method_i = 0; - gst_gl_upload_release_buffer (upload); -} - -static void -_gst_gl_upload_set_format_unlocked (GstGLUpload * upload, - GstVideoInfo * in_info) -{ - g_return_if_fail (upload != NULL); - g_return_if_fail (GST_VIDEO_INFO_FORMAT (in_info) != - GST_VIDEO_FORMAT_UNKNOWN); - g_return_if_fail (GST_VIDEO_INFO_FORMAT (in_info) != - GST_VIDEO_FORMAT_ENCODED); - - if (gst_video_info_is_equal (&upload->in_info, in_info)) - return; - - gst_gl_upload_reset (upload); - upload->convert = gst_gl_color_convert_new (upload->context); - upload->in_info = *in_info; - upload->initted = FALSE; + return TRUE; } /** - * gst_gl_upload_set_format: + * gst_gl_upload_set_caps: * @upload: a #GstGLUpload - * @in_info: input #GstVideoInfo + * @in_caps: input #GstCaps + * @out_caps: output #GstCaps * * Initializes @upload with the information required for upload. - */ -void -gst_gl_upload_set_format (GstGLUpload * upload, GstVideoInfo * in_info) -{ - GST_OBJECT_LOCK (upload); - _gst_gl_upload_set_format_unlocked (upload, in_info); - GST_OBJECT_UNLOCK (upload); -} - -/** - * gst_gl_upload_get_format: - * @upload: a #GstGLUpload * - * Returns: (transfer none): The #GstVideoInfo set by gst_gl_upload_set_format() + * Returns: whether @in_caps and @out_caps could be set on @upload */ -GstVideoInfo * -gst_gl_upload_get_format (GstGLUpload * upload) +gboolean +gst_gl_upload_set_caps (GstGLUpload * upload, GstCaps * in_caps, + GstCaps * out_caps) { - GstVideoInfo *ret; + gboolean ret; GST_OBJECT_LOCK (upload); - ret = &upload->in_info; + ret = _gst_gl_upload_set_caps_unlocked (upload, in_caps, out_caps); GST_OBJECT_UNLOCK (upload); return ret; } +/** + * gst_gl_upload_get_caps: + * @upload: a #GstGLUpload + * @in_caps: (transfer full): (allow-none): (out): the input #GstCaps + * @ou_caps: (transfer full): (allow-none): (out): the output #GstCaps + * + * Returns: (transfer none): The #GstCaps set by gst_gl_upload_set_caps() + */ +void +gst_gl_upload_get_caps (GstGLUpload * upload, GstCaps ** in_caps, + GstCaps ** out_caps) +{ + GST_OBJECT_LOCK (upload); + if (in_caps) + *in_caps = + upload->priv->in_caps ? gst_caps_ref (upload->priv->in_caps) : NULL; + if (out_caps) + *out_caps = + upload->priv->out_caps ? gst_caps_ref (upload->priv->out_caps) : NULL; + GST_OBJECT_UNLOCK (upload); +} + +static void +gst_gl_upload_release_buffer_unlocked (GstGLUpload * upload) +{ + if (upload->priv->outbuf && upload->priv->method_impl) { + upload->priv->method->release (upload->priv->method_impl, + upload->priv->outbuf); + gst_buffer_replace (&upload->priv->outbuf, NULL); + } +} + +/** + * gst_gl_upload_release_buffer: + * @upload: a #GstGLUpload + * + * Releases any buffers currently referenced by @upload + */ +void +gst_gl_upload_release_buffer (GstGLUpload * upload) +{ + g_return_if_fail (upload != NULL); + + GST_OBJECT_LOCK (upload); + gst_gl_upload_release_buffer_unlocked (upload); + GST_OBJECT_UNLOCK (upload); +} + +static gboolean +_upload_find_method (GstGLUpload * upload) +{ + if (upload->priv->method_i >= G_N_ELEMENTS (upload_methods)) + return FALSE; + + if (upload->priv->method_impl) + upload->priv->method->free (upload->priv->method_impl); + + upload->priv->method = upload_methods[upload->priv->method_i]; + upload->priv->method_impl = upload->priv->method->new (upload); + + GST_DEBUG_OBJECT (upload, "attempting upload with uploader %s", + upload->priv->method->name); + + upload->priv->method_i++; + + return TRUE; +} + /** * gst_gl_upload_perform_with_buffer: * @upload: a #GstGLUpload * @buffer: a #GstBuffer - * @tex_id: resulting texture - * @outbuf: (allow-none): resulting buffer + * @outbuf_ptr: (allow-none): resulting buffer * * Uploads @buffer to the texture given by @tex_id. @tex_id is valid * until gst_gl_upload_release_buffer() is called. @@ -233,347 +771,72 @@ gst_gl_upload_get_format (GstGLUpload * upload) */ gboolean gst_gl_upload_perform_with_buffer (GstGLUpload * upload, GstBuffer * buffer, - guint * tex_id, GstBuffer ** outbuf) + GstBuffer ** outbuf_ptr) { - GstMemory *mem; - GstVideoGLTextureUploadMeta *gl_tex_upload_meta; - guint texture_ids[] = { 0, 0, 0, 0 }; - gint i; - gboolean ret; + GstGLUploadReturn ret; - g_return_val_if_fail (upload != NULL, FALSE); - g_return_val_if_fail (buffer != NULL, FALSE); - g_return_val_if_fail (tex_id != NULL, FALSE); - g_return_val_if_fail (gst_buffer_n_memory (buffer) > 0, FALSE); - - gst_gl_upload_release_buffer (upload); - - /* GstGLMemory */ - mem = gst_buffer_peek_memory (buffer, 0); - - if (gst_is_gl_memory (mem)) { - GstGLMemory *gl_mem = (GstGLMemory *) gst_buffer_peek_memory (buffer, 0); - - if (!gst_gl_context_can_share (upload->context, gl_mem->context)) - goto raw_data_upload; - - if (GST_VIDEO_INFO_FORMAT (&upload->in_info) == GST_VIDEO_FORMAT_RGBA) { - GstMapInfo map_info; - - gst_memory_map (mem, &map_info, GST_MAP_READ | GST_MAP_GL); - gst_memory_unmap (mem, &map_info); - - *tex_id = ((GstGLMemory *) mem)->tex_id; - - if (outbuf) - *outbuf = gst_buffer_ref (buffer); - - return TRUE; - } - - GST_LOG_OBJECT (upload, "Attempting upload with GstGLMemory"); - for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&upload->in_info); i++) { - upload->in_tex[i] = (GstGLMemory *) gst_buffer_peek_memory (buffer, i); - } - - ret = _upload_memory (upload); - - *tex_id = upload->out_tex->tex_id; - for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&upload->in_info); i++) { - upload->in_tex[i] = NULL; - } - - if (ret && outbuf != NULL) - *outbuf = gst_buffer_ref (upload->priv->outbuf); - - return ret; - } -#if GST_GL_HAVE_PLATFORM_EGL - if (!upload->priv->tex_id && gst_is_egl_image_memory (mem)) - gst_gl_context_gen_texture (upload->context, &upload->priv->tex_id, - GST_VIDEO_FORMAT_RGBA, 0, 0); -#endif - - if (!upload->priv->tex_id) - gst_gl_context_gen_texture (upload->context, &upload->priv->tex_id, - GST_VIDEO_FORMAT_RGBA, GST_VIDEO_INFO_WIDTH (&upload->in_info), - GST_VIDEO_INFO_HEIGHT (&upload->in_info)); - - /* GstVideoGLTextureUploadMeta */ - gl_tex_upload_meta = gst_buffer_get_video_gl_texture_upload_meta (buffer); - if (gl_tex_upload_meta) { - GST_LOG_OBJECT (upload, "Attempting upload with " - "GstVideoGLTextureUploadMeta"); - texture_ids[0] = upload->priv->tex_id; - - if (!gst_gl_upload_perform_with_gl_texture_upload_meta (upload, - gl_tex_upload_meta, texture_ids, outbuf)) { - GST_DEBUG_OBJECT (upload, "Upload with GstVideoGLTextureUploadMeta " - "failed"); - } else { - upload->priv->mapped = FALSE; - *tex_id = upload->priv->tex_id; - return TRUE; - } - } - -raw_data_upload: - GST_LOG_OBJECT (upload, "Attempting upload with raw data"); - /* GstVideoMeta map */ - if (!gst_video_frame_map (&upload->priv->frame, &upload->in_info, buffer, - GST_MAP_READ)) { - GST_ERROR_OBJECT (upload, "Failed to map memory"); - return FALSE; - } - upload->priv->mapped = TRUE; - - /* update the video info from the one updated by frame_map using video meta */ - gst_gl_upload_set_format (upload, &upload->priv->frame.info); - - if (!gst_gl_upload_perform_with_data (upload, tex_id, - upload->priv->frame.data, outbuf)) { - return FALSE; - } - - return TRUE; -} - -void -gst_gl_upload_release_buffer (GstGLUpload * upload) -{ - g_return_if_fail (upload != NULL); - - if (upload->priv->mapped) - gst_video_frame_unmap (&upload->priv->frame); - upload->priv->mapped = FALSE; - - if (upload->priv->outbuf) { - gst_buffer_unref (upload->priv->outbuf); - upload->priv->outbuf = NULL; - } - - upload->priv->released = TRUE; -} - -/* - * Uploads using gst_video_gl_texture_upload_meta_upload(). - * i.e. consumer of GstVideoGLTextureUploadMeta - */ -static void -_do_upload_with_meta (GstGLContext * context, GstGLUpload * upload) -{ - guint texture_ids[] = { upload->priv->tex_id, 0, 0, 0 }; - - if (!gst_video_gl_texture_upload_meta_upload (upload->priv->meta, - texture_ids)) - goto error; - - upload->priv->result = TRUE; - return; - -error: - upload->priv->result = FALSE; -} - -/** - * gst_gl_upload_perform_with_gl_texture_upload_meta: - * @upload: a #GstGLUpload - * @meta: a #GstVideoGLTextureUploadMeta - * @texture_id: resulting GL textures to place the data into. - * @outbuf: (allow-none): resulting buffer - * - * Uploads @meta into @texture_id. - * - * Returns: whether the upload was successful - */ -gboolean -gst_gl_upload_perform_with_gl_texture_upload_meta (GstGLUpload * upload, - GstVideoGLTextureUploadMeta * meta, guint texture_id[4], - GstBuffer ** outbuf) -{ - gboolean ret; - - g_return_val_if_fail (upload != NULL, FALSE); - g_return_val_if_fail (meta != NULL, FALSE); - - if (meta->texture_orientation != - GST_VIDEO_GL_TEXTURE_ORIENTATION_X_NORMAL_Y_NORMAL) - GST_FIXME_OBJECT (upload, "only x-normal,y-normal textures supported, " - "the images will not appear the right way up"); - if (meta->texture_type[0] != GST_VIDEO_GL_TEXTURE_TYPE_RGBA) { - GST_FIXME_OBJECT (upload, "only single rgba texture supported"); - return FALSE; - } + g_return_val_if_fail (GST_IS_GL_UPLOAD (upload), FALSE); + g_return_val_if_fail (GST_IS_BUFFER (buffer), FALSE); GST_OBJECT_LOCK (upload); - upload->priv->meta = meta; - if (!upload->priv->tex_id) - gst_gl_context_gen_texture (upload->context, &upload->priv->tex_id, - GST_VIDEO_FORMAT_RGBA, GST_VIDEO_INFO_WIDTH (&upload->in_info), - GST_VIDEO_INFO_HEIGHT (&upload->in_info)); + gst_gl_upload_release_buffer_unlocked (upload); - GST_LOG ("Uploading with GLTextureUploadMeta with textures %i,%i,%i,%i", - texture_id[0], texture_id[1], texture_id[2], texture_id[3]); +#define NEXT_METHOD \ +do { \ + if (!_upload_find_method (upload)) { \ + GST_OBJECT_UNLOCK (upload); \ + return FALSE; \ + } \ + goto restart; \ +} while (0) - gst_gl_context_thread_add (upload->context, - (GstGLContextThreadFunc) _do_upload_with_meta, upload); + if (!upload->priv->method_impl) + _upload_find_method (upload); - ret = upload->priv->result; +restart: + if (!upload->priv->method->accept (upload->priv->method_impl, buffer, + upload->priv->in_caps, upload->priv->out_caps)) + NEXT_METHOD; - if (ret && outbuf != NULL) - *outbuf = gst_buffer_ref (upload->priv->outbuf); - - GST_OBJECT_UNLOCK (upload); - - return ret; -} - -/** - * gst_gl_upload_perform_with_data: - * @upload: a #GstGLUpload - * @texture_id: (out): the texture id to upload into - * @data: where the downloaded data should go - * @outbuf: (allow-none): resulting buffer - * - * Uploads @data into @texture_id. data size and format is specified by - * the #GstVideoInfos passed to gst_gl_upload_set_format() - * - * Returns: whether the upload was successful - */ -gboolean -gst_gl_upload_perform_with_data (GstGLUpload * upload, GLuint * texture_id, - gpointer data[GST_VIDEO_MAX_PLANES], GstBuffer ** outbuf) -{ - gboolean ret; - - g_return_val_if_fail (upload != NULL, FALSE); - - GST_OBJECT_LOCK (upload); ret = - _gst_gl_upload_perform_with_data_unlocked (upload, texture_id, data, - outbuf); + upload->priv->method->perform (upload->priv->method_impl, buffer, + &upload->priv->outbuf); + if (ret == GST_GL_UPLOAD_UNSHARED_GL_CONTEXT) { + upload->priv->method->free (upload->priv->method_impl); + upload->priv->method = &_raw_data_upload; + upload->priv->method_impl = upload->priv->method->new (upload); + goto restart; + } else if (ret == GST_GL_UPLOAD_CONVERT_GL_MEMORY) { + GstBuffer *outbuf; + + if (!upload->priv->convert) + upload->priv->convert = gst_gl_color_convert_new (upload->context); + gst_gl_color_convert_set_format (upload->priv->convert, + &upload->priv->in_info, &upload->priv->out_info); + + if (!(outbuf = gst_gl_color_convert_perform (upload->priv->convert, + upload->priv->outbuf))) { + gst_gl_upload_release_buffer_unlocked (upload); + NEXT_METHOD; + } + + gst_buffer_unref (upload->priv->outbuf); + upload->priv->outbuf = outbuf; + } else if (ret == GST_GL_UPLOAD_DONE) { + /* we are done */ + } else { + gst_gl_upload_release_buffer_unlocked (upload); + upload->priv->method->free (upload->priv->method_impl); + NEXT_METHOD; + } + + if (outbuf_ptr) + *outbuf_ptr = gst_buffer_ref (upload->priv->outbuf); + GST_OBJECT_UNLOCK (upload); - return ret; -} - -static gboolean -_gst_gl_upload_perform_with_data_unlocked (GstGLUpload * upload, - GLuint * texture_id, gpointer data[GST_VIDEO_MAX_PLANES], - GstBuffer ** outbuf) -{ - gboolean ret; - guint i; - - g_return_val_if_fail (upload != NULL, FALSE); - g_return_val_if_fail (texture_id != NULL, FALSE); - - if (!upload->in_tex[0]) - gst_gl_memory_setup_wrapped (upload->context, &upload->in_info, data, - upload->in_tex); - - for (i = 0; i < GST_VIDEO_MAX_PLANES; i++) { - if (upload->in_tex[i]) { - upload->in_tex[i]->data = data[i]; - GST_GL_MEMORY_FLAG_SET (upload->in_tex[i], - GST_GL_MEMORY_FLAG_NEED_UPLOAD); - } - } - - ret = _upload_memory (upload); - *texture_id = upload->out_tex->tex_id; - - if (ret && outbuf != NULL) - *outbuf = gst_buffer_ref (upload->priv->outbuf); - - return ret; -} - -/* Called in the gl thread */ -static gboolean -_init_upload (GstGLUpload * upload) -{ - GstGLFuncs *gl; - GstVideoFormat v_format; - GstVideoInfo out_info; - - gl = upload->context->gl_vtable; - - v_format = GST_VIDEO_INFO_FORMAT (&upload->in_info); - - GST_INFO ("Initializing texture upload for format:%s", - gst_video_format_to_string (v_format)); - - if (!gl->CreateProgramObject && !gl->CreateProgram) { - gst_gl_context_set_error (upload->context, - "Cannot upload YUV formats without OpenGL shaders"); - goto error; - } - - gst_video_info_set_format (&out_info, GST_VIDEO_FORMAT_RGBA, - GST_VIDEO_INFO_WIDTH (&upload->in_info), - GST_VIDEO_INFO_HEIGHT (&upload->in_info)); - - gst_gl_color_convert_set_format (upload->convert, &upload->in_info, - &out_info); - - upload->out_tex = gst_gl_memory_wrapped_texture (upload->context, 0, - &out_info, 0, NULL, NULL); - - upload->initted = TRUE; - - return TRUE; - -error: - return FALSE; -} - -static gboolean -_upload_memory (GstGLUpload * upload) -{ - guint in_width, in_height; - guint in_texture[GST_VIDEO_MAX_PLANES]; - GstBuffer *inbuf; - GstVideoFrame out_frame; - GstVideoInfo out_info; - gint i; - - in_width = GST_VIDEO_INFO_WIDTH (&upload->in_info); - in_height = GST_VIDEO_INFO_HEIGHT (&upload->in_info); - - if (!upload->initted) { - if (!_init_upload (upload)) { - return FALSE; - } - } - - inbuf = gst_buffer_new (); - for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&upload->in_info); i++) { - in_texture[i] = upload->in_tex[i]->tex_id; - gst_buffer_append_memory (inbuf, - gst_memory_ref ((GstMemory *) upload->in_tex[i])); - } - - GST_TRACE ("uploading with textures:%u,%u,%u dimensions:%ux%u", - in_texture[0], in_texture[1], in_texture[2], in_width, in_height); - - upload->priv->outbuf = gst_gl_color_convert_perform (upload->convert, inbuf); - gst_buffer_unref (inbuf); - - gst_video_info_set_format (&out_info, GST_VIDEO_FORMAT_RGBA, in_width, - in_height); - if (!gst_video_frame_map (&out_frame, &out_info, upload->priv->outbuf, - GST_MAP_READ | GST_MAP_GL)) { - gst_buffer_unref (upload->priv->outbuf); - upload->priv->outbuf = NULL; - return FALSE; - } - - upload->out_tex->tex_id = *(guint *) out_frame.data[0]; - - gst_video_frame_unmap (&out_frame); - upload->priv->released = FALSE; - - return TRUE; + return upload->priv->outbuf != NULL; + +#undef NEXT_METHOD } diff --git a/gst-libs/gst/gl/gstglupload.h b/gst-libs/gst/gl/gstglupload.h index 5c357c1b84..25860ba09c 100644 --- a/gst-libs/gst/gl/gstglupload.h +++ b/gst-libs/gst/gl/gstglupload.h @@ -22,7 +22,6 @@ #define __GST_GL_UPLOAD_H__ #include -#include #include @@ -47,17 +46,7 @@ struct _GstGLUpload GstObject parent; GstGLContext *context; - GstGLColorConvert *convert; - /* input data */ - GstVideoInfo in_info; - - gboolean initted; - - GstGLMemory *in_tex[GST_VIDEO_MAX_PLANES]; - GstGLMemory *out_tex; - - /* */ GstGLUploadPrivate *priv; gpointer _reserved[GST_PADDING]; @@ -75,15 +64,11 @@ struct _GstGLUploadClass GstGLUpload * gst_gl_upload_new (GstGLContext * context); -void gst_gl_upload_set_format (GstGLUpload * upload, GstVideoInfo * in_info); -GstVideoInfo * gst_gl_upload_get_format (GstGLUpload * upload); +gboolean gst_gl_upload_set_caps (GstGLUpload * upload, GstCaps * in_caps, GstCaps * out_caps); +void gst_gl_upload_get_caps (GstGLUpload * upload, GstCaps ** in_caps, GstCaps ** out_caps); -gboolean gst_gl_upload_perform_with_buffer (GstGLUpload * upload, GstBuffer * buffer, guint * tex_id, GstBuffer ** outbuf); +gboolean gst_gl_upload_perform_with_buffer (GstGLUpload * upload, GstBuffer * buffer, GstBuffer ** outbuf); void gst_gl_upload_release_buffer (GstGLUpload * upload); -gboolean gst_gl_upload_perform_with_data (GstGLUpload * upload, GLuint * texture_id, - gpointer data[GST_VIDEO_MAX_PLANES], GstBuffer ** outbuf); - -gboolean gst_gl_upload_perform_with_gl_texture_upload_meta (GstGLUpload *upload, GstVideoGLTextureUploadMeta *meta, guint texture_id[4], GstBuffer ** outbuf); G_END_DECLS diff --git a/tests/check/libs/gstglupload.c b/tests/check/libs/gstglupload.c index fa6210f57b..1490f494ab 100644 --- a/tests/check/libs/gstglupload.c +++ b/tests/check/libs/gstglupload.c @@ -226,18 +226,33 @@ draw_render (gpointer data) GST_START_TEST (test_upload_data) { - gpointer data[GST_VIDEO_MAX_PLANES] = { rgba_data, NULL, NULL, NULL }; - GstVideoInfo in_info; + GstCaps *in_caps, *out_caps; + GstBuffer *inbuf, *outbuf; + GstMapInfo map_info; gboolean res; gint i = 0; - gst_video_info_set_format (&in_info, GST_VIDEO_FORMAT_RGBA, WIDTH, HEIGHT); + in_caps = gst_caps_from_string ("video/x-raw,format=RGBA," + "width=10,height=10"); + out_caps = gst_caps_from_string ("video/x-raw(memory:GLMemory)," + "format=RGBA,width=10,height=10"); - gst_gl_upload_set_format (upload, &in_info); + gst_gl_upload_set_caps (upload, in_caps, out_caps); - res = gst_gl_upload_perform_with_data (upload, &tex_id, data, NULL); + inbuf = gst_buffer_new_wrapped_full (0, rgba_data, WIDTH * HEIGHT * 4, + 0, WIDTH * HEIGHT * 4, NULL, NULL); + + res = gst_gl_upload_perform_with_buffer (upload, inbuf, &outbuf); fail_if (res == FALSE, "Failed to upload buffer: %s\n", gst_gl_context_get_error ()); + fail_unless (GST_IS_BUFFER (outbuf)); + + res = gst_buffer_map (outbuf, &map_info, GST_MAP_READ | GST_MAP_GL); + fail_if (res == FALSE, "Failed to map gl memory"); + + tex_id = *(guint *) map_info.data; + + gst_buffer_unmap (outbuf, &map_info); gst_gl_window_draw (window, WIDTH, HEIGHT); @@ -248,30 +263,48 @@ GST_START_TEST (test_upload_data) context); i++; } + + gst_buffer_unref (inbuf); + gst_buffer_unref (outbuf); } GST_END_TEST; GST_START_TEST (test_upload_buffer) { - GstBuffer *buffer; + GstBuffer *buffer, *outbuf; GstGLMemory *gl_mem; + GstCaps *in_caps, *out_caps; GstVideoInfo in_info; + GstMapInfo map_info; gint i = 0; gboolean res; - gst_video_info_set_format (&in_info, GST_VIDEO_FORMAT_RGBA, WIDTH, HEIGHT); + in_caps = gst_caps_from_string ("video/x-raw,format=RGBA,width=10,height=10"); + gst_video_info_from_caps (&in_info, in_caps); /* create GL buffer */ buffer = gst_buffer_new (); gl_mem = gst_gl_memory_wrapped (context, &in_info, 0, rgba_data, NULL, NULL); + + res = + gst_memory_map ((GstMemory *) gl_mem, &map_info, + GST_MAP_READ | GST_MAP_GL); + fail_if (res == FALSE, "Failed to map gl memory\n"); + tex_id = *(guint *) map_info.data; + gst_memory_unmap ((GstMemory *) gl_mem, &map_info); + gst_buffer_append_memory (buffer, (GstMemory *) gl_mem); - gst_gl_upload_set_format (upload, &in_info); + out_caps = gst_caps_from_string ("video/x-raw(memory:GLMemory)," + "format=RGBA,width=10,height=10"); - res = gst_gl_upload_perform_with_buffer (upload, buffer, &tex_id, NULL); + gst_gl_upload_set_caps (upload, in_caps, out_caps); + + res = gst_gl_upload_perform_with_buffer (upload, buffer, &outbuf); fail_if (res == FALSE, "Failed to upload buffer: %s\n", gst_gl_context_get_error ()); + fail_unless (GST_IS_BUFFER (outbuf)); gst_gl_window_draw (window, WIDTH, HEIGHT); gst_gl_window_send_message (window, GST_GL_WINDOW_CB (init), context); @@ -284,6 +317,7 @@ GST_START_TEST (test_upload_buffer) gst_gl_upload_release_buffer (upload); gst_buffer_unref (buffer); + gst_buffer_unref (outbuf); } GST_END_TEST; @@ -312,7 +346,6 @@ GST_START_TEST (test_upload_meta_producer) upload_meta = gst_gl_upload_meta_new (context); gst_gl_upload_meta_set_format (upload_meta, &in_info); - gst_gl_upload_set_format (upload, &in_info); gst_buffer_add_video_meta_full (buffer, 0, GST_VIDEO_FORMAT_RGBA, WIDTH, HEIGHT, 1, in_info.offset, in_info.stride); gst_gl_upload_meta_add_to_buffer (upload_meta, buffer);