Support VIDEO-RAW-YUV 'NV12/I420' for vaapisink sinkpad
authorqing.zhang <qing.zhang@intel.com>
Thu, 14 Mar 2013 09:28:20 +0000 (05:28 -0400)
committerWind Yuan <feng.yuan@intel.com>
Fri, 22 Mar 2013 05:41:25 +0000 (13:41 +0800)
    - Support NV12 direct upload to PVR;
    - Support I420 software convert to NV12 in vaapisink;
    - Use VideoBufferPool for enhancing performance.

Change-Id: I352172d4e18e1038e89dbbc86efc6399b13157b1

gst-libs/gst/vaapi/gstvaapiimagepool.c
gst/vaapi/Makefile.am
gst/vaapi/gstvaapidownload.c
gst/vaapi/gstvaapipluginbuffer.h
gst/vaapi/gstvaapisink.c
gst/vaapi/gstvaapisink.h
gst/vaapi/gstvaapiupload.c
gst/vaapi/gstvaapiuploader.c [new file with mode: 0644]
gst/vaapi/gstvaapiuploader.h [new file with mode: 0644]

index 159cb30..a21cc53 100644 (file)
@@ -70,6 +70,10 @@ gst_vaapi_image_pool_alloc_object(
 {
     GstVaapiImagePoolPrivate * const priv = GST_VAAPI_IMAGE_POOL(pool)->priv;
 
+    // Virtual I420 Image Object Will Be Converted to NV12.
+    // TODO FIXME
+    priv->format = GST_VAAPI_IMAGE_NV12;
+
     return gst_vaapi_image_new(display,
                                priv->format,
                                priv->width,
index ebe46bb..b473907 100644 (file)
@@ -38,6 +38,7 @@ libgstvaapi_la_SOURCES =      \
        gstvaapipostproc.c      \
        gstvaapisink.c          \
        gstvaapiupload.c        \
+       gstvaapiuploader.c      \
        $(NULL)
 
 noinst_HEADERS =               \
@@ -48,6 +49,7 @@ noinst_HEADERS =              \
        gstvaapipostproc.h      \
        gstvaapisink.h          \
        gstvaapiupload.h        \
+       gstvaapiuploader.h      \
        $(NULL)
 
 if USE_ENCODERS
index 56ae902..92fe61d 100644 (file)
@@ -54,7 +54,7 @@ static const GstElementDetails gst_vaapidownload_details =
 
 /* Default templates */
 static const char gst_vaapidownload_yuv_caps_str[] =
-    "video/x-raw-yuv, "
+    GST_VAAPI_RAWYUV_CAPS_NAME", "
     "width  = (int) [ 1, MAX ], "
     "height = (int) [ 1, MAX ]; ";
 
@@ -484,7 +484,7 @@ gst_vaapidownload_transform_caps(
         }
     }
     else {
-        if (!gst_structure_has_name(structure, "video/x-raw-yuv"))
+        if (!gst_structure_has_name(structure,GST_VAAPI_RAWYUV_CAPS_NAME))
             return NULL;
         out_caps = gst_caps_from_string(gst_vaapidownload_vaapi_caps_str);
 
index d6798d5..cdf3fed 100644 (file)
@@ -22,6 +22,8 @@
 #ifndef GST_VAAPI_PLUGIN_BUFFER_H
 #define GST_VAAPI_PLUGIN_BUFFER_H
 
+#define GST_VAAPI_RAWYUV_CAPS_NAME "video/x-raw-yuv"
+
 G_GNUC_INTERNAL
 GstBuffer *
 gst_vaapi_video_buffer_new(GstVaapiDisplay *display);
index e250c94..d3711d3 100644 (file)
@@ -75,6 +75,10 @@ static const GstElementDetails gst_vaapisink_details =
         "Gwenole Beauchesne <gwenole.beauchesne@intel.com>");
 
 static const char gst_vaapisink_sink_caps_str[] =
+    GST_VAAPI_RAWYUV_CAPS_NAME", "
+    "format= (fourcc) { NV12, I420},"
+    "width  = (int) [ 1, MAX ], "
+    "height = (int) [ 1, MAX ]; "
     GST_VAAPI_SURFACE_CAPS "; "
     GST_VAAPI_BUFFER_SHARING_CAPS;
 
@@ -280,6 +284,7 @@ gst_vaapisink_destroy(GstVaapiSink *sink)
     gst_buffer_replace(&sink->video_buffer, NULL);
     g_clear_object(&sink->texture);
     g_clear_object(&sink->display);
+    g_clear_object(&sink->uploader);
 
     gst_caps_replace(&sink->caps, NULL);
 
@@ -381,6 +386,20 @@ gst_vaapisink_ensure_display(GstVaapiSink *sink)
 }
 
 static gboolean
+gst_vaapisink_ensure_uploader(GstVaapiSink *sink)
+{
+    if (!gst_vaapisink_ensure_display(sink))
+        return FALSE;
+
+    if (!sink->uploader) {
+        sink->uploader = gst_vaapi_uploader_new(sink->display);
+        if (!sink->uploader)
+            return FALSE;
+    }
+    return TRUE;
+}
+
+static gboolean
 gst_vaapisink_ensure_display_rect(GstVaapiSink *sink, guint width, guint height)
 {
     GstVaapiRectangle * const display_rect = &sink->display_rect;
@@ -657,9 +676,8 @@ end:
 static gboolean
 gst_vaapisink_start(GstBaseSink *base_sink)
 {
-    GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
-
-    return gst_vaapisink_ensure_display(sink);
+    GstVaapiSink *const sink = GST_VAAPISINK(base_sink);
+    return gst_vaapisink_ensure_uploader(sink);
 }
 
 static gboolean
@@ -681,6 +699,7 @@ gst_vaapisink_stop(GstBaseSink *base_sink)
     sink->window = NULL;
 
     g_clear_object(&sink->display);
+    g_clear_object(&sink->uploader);
 
     if (sink->video_pool) {
         g_object_unref(sink->video_pool);
@@ -691,6 +710,30 @@ gst_vaapisink_stop(GstBaseSink *base_sink)
     return TRUE;
 }
 
+static GstCaps *
+gst_vaapisink_get_caps(GstBaseSink *base_sink)
+{
+    GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
+    GstCaps *out_caps, *yuv_caps, *share_caps;
+
+    out_caps = gst_caps_from_string(GST_VAAPI_SURFACE_CAPS);
+    if (!out_caps)
+        return NULL;
+
+    share_caps = gst_caps_from_string(GST_VAAPI_BUFFER_SHARING_CAPS_NAME);
+    if (!share_caps){
+        gst_caps_unref(out_caps);
+        return NULL;
+    }
+    if (gst_vaapisink_ensure_uploader(sink)) {
+        yuv_caps = gst_vaapi_uploader_get_caps(sink->uploader);
+        if (yuv_caps)
+            gst_caps_append(out_caps, gst_caps_copy(yuv_caps));
+    }
+    gst_caps_append(out_caps, share_caps);
+    return out_caps;
+}
+
 static gboolean
 gst_vaapisink_set_caps(GstBaseSink *base_sink, GstCaps *caps)
 {
@@ -717,6 +760,9 @@ gst_vaapisink_set_caps(GstBaseSink *base_sink, GstCaps *caps)
     sink->video_width  = video_width;
     sink->video_height = video_height;
 
+    if (gst_structure_has_name(structure, GST_VAAPI_RAWYUV_CAPS_NAME))
+      sink->use_video_raw = TRUE;
+
     gst_video_parse_caps_pixel_aspect_ratio(caps, &video_par_n, &video_par_d);
     sink->video_par_n  = video_par_n;
     sink->video_par_d  = video_par_d;
@@ -935,12 +981,30 @@ static GstFlowReturn
 gst_vaapisink_show_frame(GstBaseSink *base_sink, GstBuffer *buffer)
 {
     GstVaapiSink * const sink = GST_VAAPISINK(base_sink);
-    GstVaapiVideoBuffer * vbuffer;
+    GstVaapiVideoBuffer *vbuffer;
     GstVaapiSurface *surface;
     guint flags;
     gboolean success;
     GstVideoOverlayComposition * const composition =
         gst_video_buffer_get_overlay_composition(buffer);
+    if (!sink->use_video_raw){
+        buffer = gst_buffer_ref(buffer);
+    }else{
+        GstBuffer *const src_buffer = buffer;
+        if (GST_VAAPI_IS_VIDEO_BUFFER(buffer)){
+            buffer = gst_buffer_ref(src_buffer);
+        }else if (GST_VAAPI_IS_VIDEO_BUFFER(buffer->parent)){
+            buffer = gst_buffer_ref(src_buffer->parent);
+        }else {
+            buffer = gst_vaapi_uploader_get_buffer(sink->uploader);
+            if (!buffer)
+                return GST_FLOW_UNEXPECTED;
+        }
+
+        if (!gst_vaapi_uploader_process(sink->uploader, src_buffer, buffer)){
+            goto error;
+        }
+    }
 
     if (sink->buffer_sharing)
         vbuffer = GST_VAAPI_VIDEO_BUFFER(buffer->data);
@@ -948,18 +1012,18 @@ gst_vaapisink_show_frame(GstBaseSink *base_sink, GstBuffer *buffer)
         vbuffer = GST_VAAPI_VIDEO_BUFFER(buffer);
 
     if (sink->display != gst_vaapi_video_buffer_get_display (vbuffer)) {
-      g_clear_object(&sink->display);
-      sink->display = g_object_ref (gst_vaapi_video_buffer_get_display (vbuffer));
+        g_clear_object(&sink->display);
+        sink->display = g_object_ref (gst_vaapi_video_buffer_get_display (vbuffer));
     }
 
     if (!sink->window)
-        return GST_FLOW_UNEXPECTED;
+        goto error;
 
     gst_vaapisink_ensure_rotation(sink, TRUE);
 
     surface = gst_vaapi_video_buffer_get_surface(vbuffer);
     if (!surface)
-        return GST_FLOW_UNEXPECTED;
+        goto error;
 
     GST_DEBUG("render surface %" GST_VAAPI_ID_FORMAT,
               GST_VAAPI_ID_ARGS(gst_vaapi_surface_get_id(surface)));
@@ -997,14 +1061,26 @@ gst_vaapisink_show_frame(GstBaseSink *base_sink, GstBuffer *buffer)
         break;
     }
     if (!success)
-        return GST_FLOW_UNEXPECTED;
+        goto error;
+
+    if(sink->use_video_raw){
+        gst_vaapi_uploader_free_buffer(sink->uploader,buffer);
+    }
 
     /* Retain VA surface until the next one is displayed */
     if (sink->use_overlay)
         gst_buffer_replace(&sink->video_buffer, buffer);
 
     FPS_CALCULATION(vaapisink);
+    gst_buffer_unref(buffer);
     return GST_FLOW_OK;
+
+error:
+    if(sink->use_video_raw){
+        gst_vaapi_uploader_free_buffer(sink->uploader,buffer);
+    }
+    gst_buffer_unref(buffer);
+    return GST_FLOW_UNEXPECTED;
 }
 
 static gboolean
@@ -1018,7 +1094,7 @@ gst_vaapisink_query(GstBaseSink *base_sink, GstQuery *query)
 static gboolean
 gst_vaapisink_ensure_video_pool(
     GstVaapiSink *sink,
-    GstCaps *caps)
+    GstCaps      *caps)
 {
     GstStructure *structure;
     gint width = 0, height = 0;
@@ -1044,14 +1120,13 @@ gst_vaapisink_ensure_video_pool(
 }
 
 static GstFlowReturn
-gst_vaapisink_pad_buffer_alloc(
-    GstPad * pad,
-    guint64 offset,
-    guint size,
-    GstCaps caps,
-    GstBuffer ** buf)
+gst_vaapisink_share_buffer(
+    GstVaapiSink *sink,
+    guint64       offset,
+    guint         size,
+    GstCaps      *caps,
+    GstBuffer   **buf)
 {
-    GstVaapiSink * const sink = GST_VAAPISINK(GST_OBJECT_PARENT(pad));
     GstStructure *structure = NULL;
     GstBuffer *video_buffer = NULL;
 
@@ -1084,6 +1159,63 @@ error:
     return GST_FLOW_ERROR;
 }
 
+static GstFlowReturn
+gst_vaapisink_raw_buffer(
+    GstVaapiSink *sink,
+    guint64       offset,
+    guint         size,
+    GstCaps      *caps,
+    GstBuffer   **pbuf)
+{
+    GstStructure *structure;
+    guint32 in_format = 0;
+    GstBuffer *buf;
+
+    *pbuf = NULL;
+    structure = gst_caps_get_structure(caps, 0);
+    if (!gst_vaapi_uploader_ensure_display(sink->uploader, sink->display))
+        return GST_FLOW_NOT_SUPPORTED;
+    if (!gst_vaapi_uploader_ensure_caps(sink->uploader, caps, NULL))
+        return GST_FLOW_NOT_SUPPORTED;
+
+    if (!gst_structure_get_fourcc(structure, "format", &in_format)){
+        return GST_FLOW_OK;
+    }
+
+    if(in_format != GST_MAKE_FOURCC('I','4','2','0')){
+        buf = gst_vaapi_uploader_get_buffer(sink->uploader);
+        if (!buf) {
+            GST_WARNING("failed to allocate resources for raw YUV buffer");
+            return GST_FLOW_NOT_SUPPORTED;
+        }
+        *pbuf = buf;
+    }
+    return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+gst_vaapisink_buffer_alloc(
+    GstBaseSink *base_sink,
+    guint64      offset,
+    guint        size,
+    GstCaps     *caps,
+    GstBuffer   **pbuf)
+{
+    GstVaapiSink *sink = GST_VAAPISINK(base_sink);
+    GstStructure *structure = NULL;
+
+    structure = gst_caps_get_structure(caps, 0);
+    if(!structure){
+        return GST_FLOW_ERROR;
+    }
+
+    if (!gst_structure_has_name(structure, GST_VAAPI_RAWYUV_CAPS_NAME)){
+        return gst_vaapisink_share_buffer(sink,offset,size,caps,pbuf);
+    }else{
+        return gst_vaapisink_raw_buffer(sink,offset,size,caps,pbuf);
+    }
+}
+
 static void
 gst_vaapisink_finalize(GObject *object)
 {
@@ -1179,10 +1311,12 @@ gst_vaapisink_class_init(GstVaapiSinkClass *klass)
 
     basesink_class->start        = gst_vaapisink_start;
     basesink_class->stop         = gst_vaapisink_stop;
+    basesink_class->get_caps     = gst_vaapisink_get_caps;
     basesink_class->set_caps     = gst_vaapisink_set_caps;
     basesink_class->preroll      = gst_vaapisink_show_frame;
     basesink_class->render       = gst_vaapisink_show_frame;
     basesink_class->query        = gst_vaapisink_query;
+    basesink_class->buffer_alloc = gst_vaapisink_buffer_alloc;
 
     gst_element_class_set_details_simple(
         element_class,
@@ -1273,9 +1407,6 @@ gst_vaapisink_init(GstVaapiSink *sink)
     int i = 0;
     GstPad *sinkpad = GST_BASE_SINK_PAD(sink);
 
-    gst_pad_set_bufferalloc_function(sinkpad,
-      gst_vaapisink_pad_buffer_alloc);
-
     sink->caps           = NULL;
     sink->display        = NULL;
     sink->window         = NULL;
index acf6dd6..f0d008f 100644 (file)
@@ -30,6 +30,7 @@
 #include <gst/vaapi/gstvaapitexture.h>
 #endif
 #include "gstvaapipluginutil.h"
+#include "gstvaapiuploader.h"
 
 G_BEGIN_DECLS
 
@@ -69,6 +70,7 @@ struct _GstVaapiSink {
     /*< private >*/
     GstVideoSink parent_instance;
 
+    GstVaapiUploader   *uploader;
     GstCaps            *caps;
     GstVaapiDisplay    *display;
     GstVaapiDisplayType display_type;
@@ -92,6 +94,7 @@ struct _GstVaapiSink {
     guint               use_reflection  : 1;
     guint               use_overlay     : 1;
     guint               use_rotation    : 1;
+    guint               use_video_raw   : 1;
     guint               buffer_sharing  : 1;
     guint               is_pixmap       : 1;
     GstVaapiWindow     *pixmap_pool[MAX_PIXMAP_COUNT];
index ce31088..d9432ee 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  gstvaapiupload.c - VA-API video uploader
+ *  gstvaapiupload.c - VA-API video upload element
  *
  *  Copyright (C) 2010-2011 Splitted-Desktop Systems
  *  Copyright (C) 2011-2012 Intel Corporation
@@ -56,7 +56,7 @@ static const GstElementDetails gst_vaapiupload_details =
 
 /* Default templates */
 static const char gst_vaapiupload_yuv_caps_str[] =
-    "video/x-raw-yuv, "
+    GST_VAAPI_RAWYUV_CAPS_NAME", "
     "width  = (int) [ 1, MAX ], "
     "height = (int) [ 1, MAX ]; ";
 
@@ -486,7 +486,7 @@ gst_vaapiupload_transform_caps(
     structure = gst_caps_get_structure(caps, 0);
 
     if (direction == GST_PAD_SINK) {
-        if (!gst_structure_has_name(structure, "video/x-raw-yuv"))
+        if (!gst_structure_has_name(structure,GST_VAAPI_RAWYUV_CAPS_NAME))
             return NULL;
         out_caps = gst_caps_from_string(gst_vaapiupload_vaapi_caps_str);
 
diff --git a/gst/vaapi/gstvaapiuploader.c b/gst/vaapi/gstvaapiuploader.c
new file mode 100644 (file)
index 0000000..d35941b
--- /dev/null
@@ -0,0 +1,539 @@
+/*
+ *  gstvaapiuploader.c - VA-API video uploader helper
+ *
+ *  Copyright (C) 2010-2011 Splitted-Desktop Systems
+ *  Copyright (C) 2011-2012 Intel Corporation
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1
+ *  of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free
+ *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+ */
+
+#include "gst/vaapi/sysdeps.h"
+#include <string.h>
+#include <gst/video/video.h>
+#include <gst/vaapi/gstvaapisurface.h>
+#include <gst/vaapi/gstvaapiimagepool.h>
+#include <gst/vaapi/gstvaapisurfacepool.h>
+#include <gst/vaapi/gstvaapivideobuffer.h>
+
+#include "gstvaapiuploader.h"
+#include "gstvaapipluginbuffer.h"
+
+#define GST_HELPER_NAME "vaapiuploader"
+#define GST_HELPER_DESC "VA-API video internal uploader"
+
+GST_DEBUG_CATEGORY_STATIC(gst_debug_vaapi_uploader);
+#define GST_CAT_DEFAULT gst_debug_vaapi_uploader
+
+G_DEFINE_TYPE(GstVaapiUploader, gst_vaapi_uploader, G_TYPE_OBJECT)
+
+#define GST_VAAPI_UPLOADER_CAST(obj) \
+    ((GstVaapiUploader *)(obj))
+
+#define GST_VAAPI_UPLOADER_GET_PRIVATE(obj)                     \
+  (G_TYPE_INSTANCE_GET_PRIVATE((obj),                                  \
+                              GST_VAAPI_TYPE_UPLOADER,                 \
+                              GstVaapiUploaderPrivate))
+
+struct _GstVaapiUploaderPrivate {
+  GstVaapiDisplay    *display;
+  GstCaps            *allowed_caps;
+  GstVaapiVideoPool  *images;
+  GstCaps            *image_caps;
+  guint               image_width;
+  guint               image_height;
+  GstVaapiVideoPool  *surfaces;
+  guint               surface_width;
+  guint               surface_height;
+  guint               direct_rendering;
+};
+
+enum {
+  PROP_0,
+  PROP_DISPLAY,
+};
+
+static void
+gst_vaapi_uploader_destroy(GstVaapiUploader *uploader)
+{
+  GstVaapiUploaderPrivate * const priv = uploader->priv;
+
+  gst_caps_replace(&priv->image_caps, NULL);
+  gst_caps_replace(&priv->allowed_caps, NULL);
+  g_clear_object(&priv->images);
+  g_clear_object(&priv->surfaces);
+  g_clear_object(&priv->display);
+}
+
+static gboolean
+ensure_display(GstVaapiUploader *uploader, GstVaapiDisplay *display)
+{
+  GstVaapiUploaderPrivate * const priv = uploader->priv;
+  if (priv->display == display)
+    return TRUE;
+
+  g_clear_object(&priv->display);
+  if (display)
+    priv->display = g_object_ref(display);
+  return TRUE;
+}
+
+static gboolean
+ensure_image(GstVaapiImage *image)
+{
+  guint i, num_planes, width, height;
+
+  /* Make the image fully dirty */
+  if (!gst_vaapi_image_map(image))
+    return FALSE;
+
+  gst_vaapi_image_get_size(image, &width, &height);
+  num_planes = gst_vaapi_image_get_plane_count(image);
+  for (i = 0; i < num_planes; i++) {
+    guchar * const plane = gst_vaapi_image_get_plane(image, i);
+    if (plane)
+      memset(plane, 0, height * gst_vaapi_image_get_pitch(image, i));
+  }
+
+  if (!gst_vaapi_image_unmap(image))
+    gst_vaapi_image_unmap(image);
+  return TRUE;
+}
+
+static GstCaps *
+virtual_src_caps_new (const char *mimetype,const char *fieldname, ...)
+{
+  GstStructure *structure = NULL;
+  GstCaps *caps = NULL;
+  va_list var_args;
+
+  caps = gst_caps_new_simple (mimetype,
+                             "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
+                             "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
+                             "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
+
+  structure = gst_caps_get_structure (caps, 0);
+
+  if (structure) {
+    va_start (var_args, fieldname);
+    gst_structure_set_valist (structure, fieldname, var_args);
+    va_end (var_args);
+  }
+  return caps;
+}
+
+
+static gboolean
+ensure_allowed_caps(GstVaapiUploader *uploader)
+{
+  GstVaapiUploaderPrivate * const priv = uploader->priv;
+  GstVaapiSurface *surface = NULL;
+  GstCaps *out_caps, *image_caps = NULL;
+  guint i, n_structures;
+  gboolean success = FALSE;
+  GstCaps *virtual_caps = NULL;
+
+  enum { WIDTH = 64, HEIGHT = 64 };
+  if (priv->allowed_caps)
+    return TRUE;
+
+  out_caps = gst_caps_new_empty();
+  if (!out_caps)
+    return FALSE;
+
+  image_caps = gst_vaapi_display_get_image_caps(priv->display);
+  if (!image_caps)
+    goto end;
+
+  surface = gst_vaapi_surface_new(priv->display,
+                                 GST_VAAPI_CHROMA_TYPE_YUV420, WIDTH, HEIGHT);
+  if (!surface)
+    goto end;
+
+  virtual_caps = virtual_src_caps_new(GST_VAAPI_RAWYUV_CAPS_NAME,"format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('I', '4', '2', '0'), NULL);
+
+  n_structures = gst_caps_get_size(image_caps);
+
+  for (i = 0; i < n_structures; i++) {
+    GstStructure * const structure = gst_caps_get_structure(image_caps, i);
+    GstVaapiImage *image;
+    GstVaapiImageFormat format;
+    guint32 fourcc;
+
+    if (!gst_structure_get_fourcc(structure, "format", &fourcc))
+      continue;
+    format = gst_vaapi_image_format_from_fourcc(fourcc);
+    if (!format)
+      continue;
+    image = gst_vaapi_image_new(priv->display, format, WIDTH, HEIGHT);
+    if (!image)
+      continue;
+
+    if (ensure_image(image) && gst_vaapi_surface_put_image(surface, image))
+      gst_caps_append_structure(out_caps, gst_structure_copy(structure));
+    gst_object_unref(image);
+  }
+
+  // Add Virtaul Src Caps
+  if(virtual_caps){
+    gst_caps_append(out_caps,virtual_caps);
+  }
+  gst_caps_replace(&priv->allowed_caps, out_caps);
+  success = TRUE;
+
+end:
+  gst_caps_unref(out_caps);
+
+  if (image_caps)
+    gst_caps_unref(image_caps);
+  if (surface)
+    gst_object_unref(surface);
+  return success;
+}
+
+static gboolean
+ensure_image_pool(GstVaapiUploader *uploader, GstCaps *caps)
+{
+  GstVaapiUploaderPrivate * const priv = uploader->priv;
+  GstStructure * const structure = gst_caps_get_structure(caps, 0);
+  gint width, height;
+
+  gst_structure_get_int(structure, "width",  &width);
+  gst_structure_get_int(structure, "height", &height);
+
+  if (width != priv->image_width || height != priv->image_height) {
+    priv->image_width  = width;
+    priv->image_height = height;
+    g_clear_object(&priv->images);
+    priv->images = gst_vaapi_image_pool_new(priv->display, caps);
+    if (!priv->images)
+      return FALSE;
+    gst_caps_replace(&priv->image_caps, caps);
+  }
+  return TRUE;
+}
+
+static gboolean
+ensure_surface_pool(GstVaapiUploader *uploader, GstCaps *caps)
+{
+  GstVaapiUploaderPrivate * const priv = uploader->priv;
+  GstStructure * const structure = gst_caps_get_structure(caps, 0);
+  gint width, height;
+  gst_structure_get_int(structure, "width",  &width);
+  gst_structure_get_int(structure, "height", &height);
+
+  if (width != priv->surface_width || height != priv->surface_height) {
+    priv->surface_width  = width;
+    priv->surface_height = height;
+    g_clear_object(&priv->surfaces);
+    priv->surfaces = gst_vaapi_surface_pool_new(priv->display, caps);
+    if (!priv->surfaces)
+      return FALSE;
+  }
+  return TRUE;
+}
+
+static void
+gst_vaapi_uploader_finalize(GObject *object)
+{
+  gst_vaapi_uploader_destroy(GST_VAAPI_UPLOADER_CAST(object));
+  G_OBJECT_CLASS(gst_vaapi_uploader_parent_class)->finalize(object);
+}
+
+static void
+gst_vaapi_uploader_set_property(GObject *object, guint prop_id,
+                               const GValue *value, GParamSpec *pspec)
+{
+  GstVaapiUploader * const uploader = GST_VAAPI_UPLOADER_CAST(object);
+  switch (prop_id) {
+  case PROP_DISPLAY:
+    ensure_display(uploader, g_value_get_object(value));
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+    break;
+  }
+}
+
+static void
+gst_vaapi_uploader_get_property(GObject *object, guint prop_id,
+                               GValue *value, GParamSpec *pspec)
+{
+  GstVaapiUploader * const uploader = GST_VAAPI_UPLOADER_CAST(object);
+  switch (prop_id) {
+  case PROP_DISPLAY:
+    g_value_set_object(value, uploader->priv->display);
+    break;
+  default:
+    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+    break;
+  }
+}
+
+static void
+gst_vaapi_uploader_class_init(GstVaapiUploaderClass *klass)
+{
+  GObjectClass * const object_class = G_OBJECT_CLASS(klass);
+  GST_DEBUG_CATEGORY_INIT(gst_debug_vaapi_uploader,
+                         GST_HELPER_NAME, 0, GST_HELPER_DESC);
+  g_type_class_add_private(klass, sizeof(GstVaapiUploaderPrivate));
+  object_class->finalize     = gst_vaapi_uploader_finalize;
+  object_class->set_property = gst_vaapi_uploader_set_property;
+  object_class->get_property = gst_vaapi_uploader_get_property;
+
+  g_object_class_install_property(
+         object_class,
+         PROP_DISPLAY,
+         g_param_spec_object(
+             "display",
+             "Display",
+             "The GstVaapiDisplay this object is bound to",
+             GST_VAAPI_TYPE_DISPLAY,
+             G_PARAM_READWRITE));
+}
+
+static void
+gst_vaapi_uploader_init(GstVaapiUploader *uploader)
+{
+  GstVaapiUploaderPrivate *priv;
+  priv                = GST_VAAPI_UPLOADER_GET_PRIVATE(uploader);
+  uploader->priv      = priv;
+}
+
+GstVaapiUploader *
+gst_vaapi_uploader_new(GstVaapiDisplay *display)
+{
+  return g_object_new(GST_VAAPI_TYPE_UPLOADER, "display", display, NULL);
+}
+
+gboolean
+gst_vaapi_uploader_ensure_display(
+                                 GstVaapiUploader *uploader,
+                                 GstVaapiDisplay  *display
+                                 )
+{
+  g_return_val_if_fail(GST_VAAPI_IS_UPLOADER(uploader), FALSE);
+  g_return_val_if_fail(GST_VAAPI_IS_DISPLAY(display), FALSE);
+  return ensure_display(uploader,display);
+}
+
+gboolean
+gst_vaapi_uploader_ensure_caps(
+                              GstVaapiUploader *uploader,
+                              GstCaps          *src_caps,
+                              GstCaps          *out_caps
+                              )
+{
+  GstVaapiUploaderPrivate *priv;
+  GstVaapiImage *image;
+  GstVaapiImageFormat vaformat;
+  GstVideoFormat vformat;
+  GstStructure *structure;
+  gint width, height;
+
+  g_return_val_if_fail(GST_VAAPI_IS_UPLOADER(uploader), FALSE);
+  g_return_val_if_fail(src_caps != NULL, FALSE);
+
+  if (!ensure_image_pool(uploader, src_caps))
+    return FALSE;
+  if (!ensure_surface_pool(uploader, out_caps ? out_caps : src_caps))
+    return FALSE;
+
+  priv = uploader->priv;
+  priv->direct_rendering = 0;
+  structure = gst_caps_get_structure(src_caps, 0);
+  if (!structure)
+    return FALSE;
+  gst_structure_get_int(structure, "width",  &width);
+  gst_structure_get_int(structure, "height", &height);
+
+  /* Translate from Gst video format to VA image format */
+  if (!gst_video_format_parse_caps(src_caps, &vformat, NULL, NULL))
+    return FALSE;
+  if (!gst_video_format_is_yuv(vformat))
+    return FALSE;
+  vaformat = gst_vaapi_image_format_from_video(vformat);
+  if (!vaformat)
+    return FALSE;
+
+  gst_vaapi_video_pool_set_capacity(priv->images, 4);
+  gst_vaapi_video_pool_reserve(priv->images, 3);
+
+  /* Check if we can alias source and output buffers (same data_size) */
+  image = gst_vaapi_video_pool_get_object(priv->images);
+  if (image) {
+    if (gst_vaapi_image_get_format(image) == vaformat &&
+       gst_vaapi_image_is_linear(image) &&
+       (gst_vaapi_image_get_data_size(image) ==
+        gst_video_format_get_size(vformat, width, height)))
+      priv->direct_rendering = 1;
+    gst_vaapi_video_pool_put_object(priv->images, image);
+  }
+  return TRUE;
+}
+
+gboolean
+gst_vaapi_uploader_process(
+                          GstVaapiUploader *uploader,
+                          GstBuffer        *src_buffer,
+                          GstBuffer        *out_buffer
+                          )
+{
+  GstVaapiVideoBuffer *out_vbuffer;
+  GstVaapiSurface *surface;
+  GstVaapiImage *image;
+  g_return_val_if_fail(GST_VAAPI_IS_UPLOADER(uploader), FALSE);
+
+  if (GST_VAAPI_IS_VIDEO_BUFFER(out_buffer))
+    out_vbuffer = GST_VAAPI_VIDEO_BUFFER(out_buffer);
+  else if (GST_VAAPI_IS_VIDEO_BUFFER(out_buffer->parent))
+    out_vbuffer = GST_VAAPI_VIDEO_BUFFER(out_buffer->parent);
+  else {
+    GST_WARNING("expected an output video buffer");
+    return FALSE;
+  }
+
+  surface = gst_vaapi_video_buffer_get_surface(out_vbuffer);
+  g_return_val_if_fail(surface != NULL, FALSE);
+
+  if (GST_VAAPI_IS_VIDEO_BUFFER(src_buffer)) {
+    /* GstVaapiVideoBuffer with mapped VA image */
+    image = gst_vaapi_video_buffer_get_image(
+            GST_VAAPI_VIDEO_BUFFER(src_buffer));
+
+    if (!image || !gst_vaapi_image_unmap(image))
+      return FALSE;
+  }
+  else if (GST_VAAPI_IS_VIDEO_BUFFER(src_buffer->parent)) {
+    /* Sub-buffer from GstVaapiVideoBuffer with mapped VA image */
+    image = gst_vaapi_video_buffer_get_image(
+            GST_VAAPI_VIDEO_BUFFER(src_buffer->parent));
+
+    if (!image || !gst_vaapi_image_unmap(image))
+    return FALSE;
+  }
+  else {
+    /* Regular GstBuffer that needs to be uploaded and transmit to a VA image */
+    image = gst_vaapi_video_buffer_get_image(out_vbuffer);
+
+    if (!image) {
+      image = gst_vaapi_video_pool_get_object(uploader->priv->images);
+      if (!image)
+       return FALSE;
+      gst_vaapi_video_buffer_set_image(out_vbuffer, image);
+    }
+
+    // convert coming image data type to real acceptable type
+    if (!gst_vaapi_convert_buffer_to_image(image, src_buffer))
+      return FALSE;
+  }
+  g_return_val_if_fail(image != NULL, FALSE);
+
+  if (!gst_vaapi_surface_put_image(surface, image)) {
+    GST_WARNING("failed to upload YUV buffer to VA surface");
+    return FALSE;
+  }
+
+  /* Map again for next uploads */
+  if (!gst_vaapi_image_map(image))
+    return FALSE;
+  return TRUE;
+}
+
+GstCaps *
+gst_vaapi_uploader_get_caps(GstVaapiUploader *uploader)
+{
+  g_return_val_if_fail(GST_VAAPI_IS_UPLOADER(uploader), NULL);
+  if (!ensure_allowed_caps(uploader))
+    return NULL;
+  return uploader->priv->allowed_caps;
+}
+
+GstBuffer *
+gst_vaapi_uploader_get_buffer(GstVaapiUploader *uploader)
+{
+  GstVaapiUploaderPrivate *priv;
+  GstVaapiSurface *surface;
+  GstVaapiImage *image;
+  GstVaapiVideoBuffer *vbuffer;
+  GstBuffer *buffer = NULL;
+
+  g_return_val_if_fail(GST_VAAPI_IS_UPLOADER(uploader), NULL);
+  priv = uploader->priv;
+
+  buffer = gst_vaapi_video_buffer_new_from_pool(priv->images);
+  if (!buffer) {
+    GST_WARNING("failed to allocate video buffer");
+    goto error;
+  }
+
+  vbuffer = GST_VAAPI_VIDEO_BUFFER(buffer);
+  surface = gst_vaapi_video_pool_get_object(priv->surfaces);
+  if (!surface) {
+    GST_WARNING("failed to allocate VA surface");
+    goto error;
+  }
+
+  gst_vaapi_video_buffer_set_surface(vbuffer, surface);
+  image = gst_vaapi_video_buffer_get_image(vbuffer);
+  if (!gst_vaapi_image_map(image)) {
+    GST_WARNING("failed to map VA image");
+    goto error;
+  }
+
+  GST_BUFFER_DATA(buffer) = gst_vaapi_image_get_plane(image, 0);
+  GST_BUFFER_SIZE(buffer) = gst_vaapi_image_get_data_size(image);
+  gst_buffer_set_caps(buffer, priv->image_caps);
+  return buffer;
+
+error:
+  gst_buffer_unref(buffer);
+  return buffer;
+}
+
+gboolean
+gst_vaapi_uploader_free_buffer(GstVaapiUploader *uploader, GstBuffer *buffer)
+{
+  GstVaapiUploaderPrivate *priv;
+  GstVaapiSurface *surface;
+  GstVaapiImage *image;
+  gboolean success = FALSE;
+
+  g_return_val_if_fail(GST_VAAPI_IS_UPLOADER(uploader), success);
+  g_return_val_if_fail(GST_VAAPI_IS_VIDEO_BUFFER(buffer), success);
+
+  priv = uploader->priv;
+  image = gst_vaapi_video_buffer_get_image(GST_VAAPI_VIDEO_BUFFER(buffer));
+  if (image){
+    gst_vaapi_video_pool_put_object(priv->images,image);
+    success = TRUE;
+  }
+
+  surface = gst_vaapi_video_buffer_get_surface(GST_VAAPI_VIDEO_BUFFER(buffer));
+  if (surface){
+    gst_vaapi_video_pool_put_object(priv->surfaces,surface);
+  }else{
+    success = FALSE;
+  }
+
+  return success;
+}
+
+gboolean
+gst_vaapi_uploader_has_direct_rendering(GstVaapiUploader *uploader)
+{
+  g_return_val_if_fail(GST_VAAPI_IS_UPLOADER(uploader), FALSE);
+  return uploader->priv->direct_rendering;
+}
diff --git a/gst/vaapi/gstvaapiuploader.h b/gst/vaapi/gstvaapiuploader.h
new file mode 100644 (file)
index 0000000..93a2e0a
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ *  gstvaapiuploader.h - VA-API video uploader helper
+ *
+ *  Copyright (C) 2010-2011 Splitted-Desktop Systems
+ *  Copyright (C) 2011-2012 Intel Corporation
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public License
+ *  as published by the Free Software Foundation; either version 2.1
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this program; if not, write to the Free
+ *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ *  Boston, MA 02110-1301 USA
+*/
+
+#ifndef GST_VAAPIUPLOADER_H
+#define GST_VAAPIUPLOADER_H
+
+#include <gst/vaapi/gstvaapidisplay.h>
+
+G_BEGIN_DECLS
+
+#define GST_VAAPI_TYPE_UPLOADER                        \
+  (gst_vaapi_uploader_get_type())
+
+#define GST_VAAPI_UPLOADER(obj)                                 \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),                                   \
+                             GST_VAAPI_TYPE_UPLOADER,                  \
+                             GstVaapiUploader))
+
+#define GST_VAAPI_UPLOADER_CLASS(klass)                         \
+  (G_TYPE_CHECK_CLASS_CAST((klass),                                    \
+                          GST_VAAPI_TYPE_UPLOADER,                     \
+                          GstVaapiUploaderClass))
+
+#define GST_VAAPI_IS_UPLOADER(obj)                                     \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_VAAPI_TYPE_UPLOADER))
+
+#define GST_VAAPI_IS_UPLOADER_CLASS(klass)                     \
+  (G_TYPE_CHECK_CLASS_TYPE((klass), GST_VAAPI_TYPE_UPLOADER))
+
+#define GST_VAAPI_UPLOADER_GET_CLASS(obj)                       \
+  (G_TYPE_INSTANCE_GET_CLASS((obj),                                    \
+                            GST_VAAPI_TYPE_UPLOADER,                   \
+                            GstVaapiUploaderClass))
+
+typedef struct _GstVaapiUploader                GstVaapiUploader;
+typedef struct _GstVaapiUploaderPrivate         GstVaapiUploaderPrivate;
+typedef struct _GstVaapiUploaderClass           GstVaapiUploaderClass;
+
+struct _GstVaapiUploader {
+  /*< private >*/
+  GObject             parent_instance;
+  GstVaapiUploaderPrivate *priv;
+};
+
+struct _GstVaapiUploaderClass {
+  /*< private >*/
+  GObjectClass        parent_class;
+};
+
+G_GNUC_INTERNAL
+GType
+gst_vaapi_uploader_get_type(void) G_GNUC_CONST;
+
+G_GNUC_INTERNAL
+GstVaapiUploader *
+gst_vaapi_uploader_new(GstVaapiDisplay *display);
+
+G_GNUC_INTERNAL
+gboolean
+gst_vaapi_uploader_ensure_display(
+                                 GstVaapiUploader *uploader,
+                                 GstVaapiDisplay  *display
+                                 );
+
+G_GNUC_INTERNAL
+gboolean
+gst_vaapi_uploader_ensure_caps(
+                              GstVaapiUploader *uploader,
+                              GstCaps          *src_caps,
+                              GstCaps          *out_caps
+                              );
+
+G_GNUC_INTERNAL
+gboolean
+gst_vaapi_uploader_process(
+                          GstVaapiUploader *uploader,
+                          GstBuffer        *src_buffer,
+                          GstBuffer        *out_buffer
+                          );
+
+G_GNUC_INTERNAL
+GstCaps *
+gst_vaapi_uploader_get_caps(GstVaapiUploader *uploader);
+
+G_GNUC_INTERNAL
+GstBuffer *
+gst_vaapi_uploader_get_buffer(GstVaapiUploader *uploader);
+
+G_GNUC_INTERNAL
+gboolean
+gst_vaapi_uploader_free_buffer(GstVaapiUploader *uploader, GstBuffer *buffer);
+
+G_GNUC_INTERNAL
+gboolean
+gst_vaapi_uploader_has_direct_rendering(GstVaapiUploader *uploader);
+
+G_END_DECLS
+
+#endif /* GST_VAAPI_UPLOADER_H */