diff --git a/sys/vdpau/gstvdp/gstvdpdevice.c b/sys/vdpau/gstvdp/gstvdpdevice.c
index a41be6b..444d5fa 100644
--- a/sys/vdpau/gstvdp/gstvdpdevice.c
+++ b/sys/vdpau/gstvdp/gstvdpdevice.c
@@ -43,8 +43,12 @@ gst_vdp_device_init (GstVdpDevice * device)
   device->display_name = NULL;
   device->display = NULL;
   device->device = VDP_INVALID_HANDLE;
-
+  device->cache_size = 20; /* TODO: parameter */
   device->constructed = FALSE;
+  device->cache_enabled = TRUE;
+  device->cache_mutex = g_mutex_new();
+  device->vos_cache = g_queue_new ();
+  device->vvs_cache = g_queue_new ();
 }
 
 static void
diff --git a/sys/vdpau/gstvdp/gstvdpdevice.h b/sys/vdpau/gstvdp/gstvdpdevice.h
index df13675..270408e 100644
--- a/sys/vdpau/gstvdp/gstvdpdevice.h
+++ b/sys/vdpau/gstvdp/gstvdpdevice.h
@@ -54,6 +54,13 @@ struct _GstVdpDevice
   Display *display;
   VdpDevice device;
 
+  guint cache_size;
+  gboolean cache_enabled;
+  GMutex *cache_mutex;
+
+  GQueue* vos_cache; /* VdpOutputSurface Cache */
+  GQueue* vvs_cache; /* VdpVideoSurface Cache */
+
   VdpDeviceDestroy                                *vdp_device_destroy;
   VdpGetProcAddress                               *vdp_get_proc_address;
   VdpGetErrorString                               *vdp_get_error_string;
diff --git a/sys/vdpau/gstvdp/gstvdpoutputbuffer.c b/sys/vdpau/gstvdp/gstvdpoutputbuffer.c
index 04a3cb5..8f777f2 100644
--- a/sys/vdpau/gstvdp/gstvdpoutputbuffer.c
+++ b/sys/vdpau/gstvdp/gstvdpoutputbuffer.c
@@ -38,25 +38,34 @@ gst_vdp_output_buffer_new (GstVdpDevice * device, VdpRGBAFormat rgba_format,
   VdpStatus status;
   VdpOutputSurface surface;
 
-  status =
-      device->vdp_output_surface_create (device->device, rgba_format, width,
-      height, &surface);
-  if (status != VDP_STATUS_OK) {
-    GST_ERROR ("Couldn't create a VdpOutputSurface, error returned was: %s",
-        device->vdp_get_error_string (status));
-    return NULL;
-  }
-
-
-  buffer =
-      (GstVdpOutputBuffer *) gst_mini_object_new (GST_TYPE_VDP_OUTPUT_BUFFER);
-
-  buffer->device = g_object_ref (device);
-  buffer->rgba_format = rgba_format;
-  buffer->width = width;
-  buffer->height = height;
+  g_mutex_lock (device->cache_mutex);
+  if (g_queue_get_length (device->vos_cache) < device->cache_size) {
+    status =
+        device->vdp_output_surface_create (device->device, rgba_format, width,
+        height, &surface);
+        g_print ("%u %u %u %u\n", device->device, rgba_format, width,
+        height);
+    if (status != VDP_STATUS_OK) {
+      GST_ERROR ("Couldn't create a VdpOutputSurface, error returned was: %s",
+          device->vdp_get_error_string (status));
+      return NULL;
+    }
 
-  buffer->surface = surface;
+    buffer =
+        (GstVdpOutputBuffer *) gst_mini_object_new (GST_TYPE_VDP_OUTPUT_BUFFER);
+
+    buffer->device = g_object_ref (device);
+    buffer->rgba_format = rgba_format;
+    buffer->width = width;
+    buffer->height = height;
+    buffer->surface = surface;
+    if (width > 1 && height > 1)
+      g_queue_push_tail (device->vos_cache, buffer);
+  } else {
+    buffer = GST_VDP_OUTPUT_BUFFER (g_queue_pop_head (device->vos_cache));
+    g_queue_push_tail (device->vos_cache, buffer);
+  }
+  g_mutex_unlock (device->cache_mutex);
 
   return buffer;
 }
@@ -71,16 +80,21 @@ gst_vdp_output_buffer_finalize (GstVdpOutputBuffer * buffer)
 
   device = buffer->device;
 
-  status = device->vdp_output_surface_destroy (buffer->surface);
-  if (status != VDP_STATUS_OK)
-    GST_ERROR
-        ("Couldn't destroy the buffers VdpOutputSurface, error returned was: %s",
-        device->vdp_get_error_string (status));
-
-  g_object_unref (buffer->device);
+  g_mutex_lock (device->cache_mutex);
+  if (device->cache_enabled) {
+    buffer = (GstVdpOutputBuffer*) gst_mini_object_ref (GST_MINI_OBJECT(buffer));
+  } else {
+    status = device->vdp_output_surface_destroy (buffer->surface);
+    if (status != VDP_STATUS_OK)
+      GST_ERROR
+          ("Couldn't destroy the buffers VdpOutputSurface, error returned was: %s",
+          device->vdp_get_error_string (status));
+    g_object_unref (buffer->device);
 
-  GST_MINI_OBJECT_CLASS (gst_vdp_output_buffer_parent_class)->finalize
-      (GST_MINI_OBJECT (buffer));
+    GST_MINI_OBJECT_CLASS (gst_vdp_output_buffer_parent_class)->finalize
+        (GST_MINI_OBJECT (buffer));
+  }
+  g_mutex_unlock (device->cache_mutex);
 }
 
 static void
diff --git a/sys/vdpau/gstvdp/gstvdpvideobuffer.c b/sys/vdpau/gstvdp/gstvdpvideobuffer.c
index ff7fc5b..63db394 100644
--- a/sys/vdpau/gstvdp/gstvdpvideobuffer.c
+++ b/sys/vdpau/gstvdp/gstvdpvideobuffer.c
@@ -36,21 +36,30 @@ gst_vdp_video_buffer_new (GstVdpDevice * device, VdpChromaType chroma_type,
 {
   GstVdpVideoBuffer *buffer;
   VdpStatus status;
-  VdpVideoSurface surface;
-
-  status = device->vdp_video_surface_create (device->device, chroma_type, width,
-      height, &surface);
-  if (status != VDP_STATUS_OK) {
-    GST_ERROR ("Couldn't create a VdpVideoSurface, error returned was: %s",
-        device->vdp_get_error_string (status));
-    return NULL;
-  }
+  VdpVideoSurface *surface;
+
+  g_mutex_lock (device->cache_mutex);
+  if (g_queue_get_length (device->vvs_cache) < device->cache_size) {
+    surface = g_slice_new (VdpVideoSurface);
+    status = device->vdp_video_surface_create (device->device, chroma_type, width,
+        height, surface);
+    if (status != VDP_STATUS_OK) {
+      GST_ERROR ("Couldn't create a VdpVideoSurface, error returned was: %s",
+          device->vdp_get_error_string (status));
+      return NULL;
+    }
 
-  buffer =
-      (GstVdpVideoBuffer *) gst_mini_object_new (GST_TYPE_VDP_VIDEO_BUFFER);
+    buffer =
+        (GstVdpVideoBuffer *) gst_mini_object_new (GST_TYPE_VDP_VIDEO_BUFFER);
 
-  buffer->device = g_object_ref (device);
-  buffer->surface = surface;
+    buffer->device = g_object_ref (device);
+    buffer->surface = *surface;
+    g_queue_push_tail (device->vvs_cache, buffer);
+  } else {
+    buffer = GST_VDP_VIDEO_BUFFER (g_queue_pop_head (device->vvs_cache));
+    g_queue_push_tail (device->vvs_cache, buffer);
+  }
+  g_mutex_unlock (device->cache_mutex);
 
   return buffer;
 }
@@ -64,17 +73,22 @@ gst_vdp_video_buffer_finalize (GstVdpVideoBuffer * buffer)
   VdpStatus status;
 
   device = buffer->device;
-
-  status = device->vdp_video_surface_destroy (buffer->surface);
-  if (status != VDP_STATUS_OK)
-    GST_ERROR
-        ("Couldn't destroy the buffers VdpVideoSurface, error returned was: %s",
-        device->vdp_get_error_string (status));
-
-  g_object_unref (buffer->device);
-
-  GST_MINI_OBJECT_CLASS (gst_vdp_video_buffer_parent_class)->finalize
-      (GST_MINI_OBJECT (buffer));
+  g_mutex_lock (device->cache_mutex);
+  if (device->cache_enabled) {
+    buffer = (GstVdpVideoBuffer*) gst_mini_object_ref (GST_MINI_OBJECT(buffer));
+  } else {
+    status = device->vdp_video_surface_destroy (buffer->surface);
+    if (status != VDP_STATUS_OK)
+      GST_ERROR
+          ("Couldn't destroy the buffers VdpVideoSurface, error returned was: %s",
+          device->vdp_get_error_string (status));
+
+    g_object_unref (buffer->device);
+
+    GST_MINI_OBJECT_CLASS (gst_vdp_video_buffer_parent_class)->finalize
+        (GST_MINI_OBJECT (buffer));
+  }
+  g_mutex_unlock (device->cache_mutex);
 }
 
 static void
diff --git a/sys/vdpau/gstvdpvideopostprocess.c b/sys/vdpau/gstvdpvideopostprocess.c
index cd45b4d..b898142 100644
--- a/sys/vdpau/gstvdpvideopostprocess.c
+++ b/sys/vdpau/gstvdpvideopostprocess.c
@@ -597,6 +597,10 @@ gst_vdp_vpp_stop (GstVdpVideoPostProcess * vpp)
   if (vpp->mixer != VDP_INVALID_HANDLE)
     vpp->device->vdp_video_mixer_destroy (vpp->mixer);
 
+  g_mutex_lock (vpp->device->cache_mutex);
+  vpp->device->cache_enabled = FALSE;
+  g_mutex_unlock (vpp->device->cache_mutex);
+
   gst_vdp_vpp_flush (vpp);
 }
 
