sys/directdraw/gstdirectdrawsink.*: Handle display mode changes during playback.
authorSébastien Moutte <sebastien@moutte.net>
Sun, 11 Mar 2007 22:23:04 +0000 (22:23 +0000)
committerSébastien Moutte <sebastien@moutte.net>
Sun, 11 Mar 2007 22:23:04 +0000 (22:23 +0000)
Original commit message from CVS:
* sys/directdraw/gstdirectdrawsink.c:
* sys/directdraw/gstdirectdrawsink.h:
Handle display mode changes during playback.

ChangeLog
sys/directdraw/gstdirectdrawsink.c
sys/directdraw/gstdirectdrawsink.h

index 664bf6f..c098c47 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2007-03-11  Sebastien Moutte  <sebastien@moutte.net>
+
+       * sys/directdraw/gstdirectdrawsink.c:
+       * sys/directdraw/gstdirectdrawsink.h:
+       Handle display mode changes during playback.
+
 2007-03-10  David Schleef  <ds@schleef.org>
 
        * configure.ac:
index b7e3fc8..ab778c8 100644 (file)
@@ -30,7 +30,7 @@
  *
  * <refsect2>
  * <para>
- * DirectdrawSink renders video frames to any win32 window. This element can receive
+ * DirectdrawSink renders video RGB frames to any win32 window. This element can receive
  * a window ID from the application through the XOverlay interface and will then render
  * video frames in this window.
  * If no Window ID was provided by the application, the element will create its
@@ -88,21 +88,32 @@ static GstFlowReturn gst_directdraw_sink_show_frame (GstBaseSink * bsink,
 static gboolean gst_directdraw_sink_setup_ddraw (GstDirectDrawSink * ddrawsink);
 static gboolean gst_directdraw_sink_create_default_window (GstDirectDrawSink *
     ddrawsink);
-static gboolean gst_directdraw_sink_create_ddraw_surface (GstDirectDrawSink *
+static gboolean gst_directdraw_sink_check_primary_surface (GstDirectDrawSink *
+    ddrawsink);
+static gboolean gst_directdraw_sink_check_offscreen_surface (GstDirectDrawSink *
     ddrawsink);
 static GstCaps *gst_directdraw_sink_get_ddrawcaps (GstDirectDrawSink *
     ddrawsink);
+static GstCaps
+    *gst_directdraw_sink_create_caps_from_surfacedesc (LPDDSURFACEDESC2 desc);
 static void gst_directdraw_sink_cleanup (GstDirectDrawSink * ddrawsink);
 static void gst_directdraw_sink_bufferpool_clear (GstDirectDrawSink *
     ddrawsink);
 static void gst_directdraw_sink_ddraw_put (GstDirectDrawSink * ddrawsink,
     GstDDrawSurface * surface);
+static gboolean gst_ddrawvideosink_get_format_from_caps (GstDirectDrawSink *
+    ddrawsink, GstCaps * caps, DDPIXELFORMAT * pPixelFormat);
+static void gst_directdraw_sink_center_rect (GstDirectDrawSink * ddrawsink,
+    RECT src, RECT dst, RECT * result);
+char *DDErrorString (HRESULT hr);
 
 /* surfaces management functions */
 static void gst_directdraw_sink_surface_destroy (GstDirectDrawSink * ddrawsink,
     GstDDrawSurface * surface);
 static GstDDrawSurface *gst_directdraw_sink_surface_create (GstDirectDrawSink *
     ddrawsink, GstCaps * caps, size_t size);
+static gboolean gst_directdraw_sink_surface_check (GstDirectDrawSink *
+    ddrawsink, GstDDrawSurface * surface);
 
 static GstStaticPadTemplate directdrawsink_sink_factory =
 GST_STATIC_PAD_TEMPLATE ("sink",
@@ -165,351 +176,30 @@ gst_directdraw_sink_expose (GstXOverlay * overlay)
 }
 
 static void
-gst_directdraw_sink_xoverlay_interface_init (GstXOverlayClass * iface)
-{
-  iface->set_xwindow_id = gst_directdraw_sink_set_window_id;
-  iface->expose = gst_directdraw_sink_expose;
-}
-
-static void
-gst_directdraw_sink_init_interfaces (GType type)
-{
-  static const GInterfaceInfo iface_info = {
-    (GInterfaceInitFunc) gst_directdraw_sink_interface_init,
-    NULL,
-    NULL,
-  };
-
-  static const GInterfaceInfo xoverlay_info = {
-    (GInterfaceInitFunc) gst_directdraw_sink_xoverlay_interface_init,
-    NULL,
-    NULL,
-  };
-
-  g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE,
-      &iface_info);
-  g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &xoverlay_info);
-}
-
-/* Utility functions */
-
-/* this function fill a DDPIXELFORMAT using Gstreamer caps */
-static gboolean
-gst_ddrawvideosink_get_format_from_caps (GstDirectDrawSink * ddrawsink,
-    GstCaps * caps, DDPIXELFORMAT * pPixelFormat)
-{
-  GstStructure *structure = NULL;
-  gboolean ret = TRUE;
-
-  /* check params */
-  g_return_val_if_fail (pPixelFormat, FALSE);
-  g_return_val_if_fail (caps, FALSE);
-
-  /* init structure */
-  memset (pPixelFormat, 0, sizeof (DDPIXELFORMAT));
-  pPixelFormat->dwSize = sizeof (DDPIXELFORMAT);
-
-  if (!(structure = gst_caps_get_structure (caps, 0))) {
-    GST_CAT_ERROR_OBJECT (directdrawsink_debug, ddrawsink,
-        "can't get structure pointer from caps");
-    return FALSE;
-  }
-
-  if (gst_structure_has_name (structure, "video/x-raw-rgb")) {
-    gint depth, bitcount, bitmask, endianness;
-
-    pPixelFormat->dwFlags = DDPF_RGB;
-    ret &= gst_structure_get_int (structure, "bpp", &bitcount);
-    pPixelFormat->dwRGBBitCount = bitcount;
-    ret &= gst_structure_get_int (structure, "depth", &depth);
-    ret &= gst_structure_get_int (structure, "red_mask", &bitmask);
-    pPixelFormat->dwRBitMask = bitmask;
-    ret &= gst_structure_get_int (structure, "green_mask", &bitmask);
-    pPixelFormat->dwGBitMask = bitmask;
-    ret &= gst_structure_get_int (structure, "blue_mask", &bitmask);
-    pPixelFormat->dwBBitMask = bitmask;
-
-    gst_structure_get_int (structure, "endianness", &endianness);
-    if (endianness == G_BIG_ENDIAN) {
-      endianness = G_LITTLE_ENDIAN;
-      pPixelFormat->dwRBitMask = GUINT32_TO_BE (pPixelFormat->dwRBitMask);
-      pPixelFormat->dwGBitMask = GUINT32_TO_BE (pPixelFormat->dwGBitMask);
-      pPixelFormat->dwBBitMask = GUINT32_TO_BE (pPixelFormat->dwBBitMask);
-    }
-  } else if (gst_structure_has_name (structure, "video/x-raw-yuv")) {
-    gint fourcc;
-
-    pPixelFormat->dwFlags = DDPF_FOURCC;
-    ret &= gst_structure_get_fourcc (structure, "format", &fourcc);
-    pPixelFormat->dwFourCC = fourcc;
-  } else {
-    GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
-        "unknown caps name received %" GST_PTR_FORMAT, caps);
-    ret = FALSE;
-  }
-
-  return ret;
-}
-
-/* This function centers the RECT of source surface to
-a dest surface and set the result RECT into result */
-static void
-gst_directdraw_sink_center_rect (GstDirectDrawSink * ddrawsink, RECT src,
-    RECT dst, RECT * result)
-{
-  gdouble src_ratio, dst_ratio;
-  long src_width = src.right;
-  long src_height = src.bottom;
-  long dst_width = dst.right - dst.left;
-  long dst_heigth = dst.bottom - dst.top;
-  long result_width = 0, result_height = 0;
-
-  g_return_if_fail (result != NULL);
-
-  src_ratio = (gdouble) src_width / src_height;
-  dst_ratio = (gdouble) dst_width / dst_heigth;
-
-  if (src_ratio > dst_ratio) {
-    /* new height */
-    result_height = (long) (dst_width / src_ratio);
-
-    result->left = dst.left;
-    result->right = dst.right;
-    result->top = dst.top + (dst_heigth - result_height) / 2;
-    result->bottom = result->top + result_height;
-
-  } else if (src_ratio < dst_ratio) {
-    /* new width */
-    result_width = (long) (dst_heigth * src_ratio);
-
-    result->top = dst.top;
-    result->bottom = dst.bottom;
-    result->left = dst.left + (dst_width - result_width) / 2;
-    result->right = result->left + result_width;
-
-  } else {
-    /* same ratio */
-    memcpy (result, &dst, sizeof (RECT));
-  }
-
-  GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
-      "source is %ldx%ld dest is %ldx%ld, result is %ldx%ld with x,y %ldx%ld",
-      src_width, src_height, dst_width, dst_heigth,
-      result->right - result->left, result->bottom - result->top, result->left,
-      result->right);
-}
-
-/**
- * Get DirectDraw error message.
- * @hr: HRESULT code
- * Returns: Text representation of the error.
- */
-char *
-DDErrorString (HRESULT hr)
-{
-  switch (hr) {
-    case DDERR_ALREADYINITIALIZED:
-      return "DDERR_ALREADYINITIALIZED";
-    case DDERR_CANNOTATTACHSURFACE:
-      return "DDERR_CANNOTATTACHSURFACE";
-    case DDERR_CANNOTDETACHSURFACE:
-      return "DDERR_CANNOTDETACHSURFACE";
-    case DDERR_CURRENTLYNOTAVAIL:
-      return "DDERR_CURRENTLYNOTAVAIL";
-    case DDERR_EXCEPTION:
-      return "DDERR_EXCEPTION";
-    case DDERR_GENERIC:
-      return "DDERR_GENERIC";
-    case DDERR_HEIGHTALIGN:
-      return "DDERR_HEIGHTALIGN";
-    case DDERR_INCOMPATIBLEPRIMARY:
-      return "DDERR_INCOMPATIBLEPRIMARY";
-    case DDERR_INVALIDCAPS:
-      return "DDERR_INVALIDCAPS";
-    case DDERR_INVALIDCLIPLIST:
-      return "DDERR_INVALIDCLIPLIST";
-    case DDERR_INVALIDMODE:
-      return "DDERR_INVALIDMODE";
-    case DDERR_INVALIDOBJECT:
-      return "DDERR_INVALIDOBJECT";
-    case DDERR_INVALIDPARAMS:
-      return "DDERR_INVALIDPARAMS";
-    case DDERR_INVALIDPIXELFORMAT:
-      return "DDERR_INVALIDPIXELFORMAT";
-    case DDERR_INVALIDRECT:
-      return "DDERR_INVALIDRECT";
-    case DDERR_LOCKEDSURFACES:
-      return "DDERR_LOCKEDSURFACES";
-    case DDERR_NO3D:
-      return "DDERR_NO3D";
-    case DDERR_NOALPHAHW:
-      return "DDERR_NOALPHAHW";
-    case DDERR_NOCLIPLIST:
-      return "DDERR_NOCLIPLIST";
-    case DDERR_NOCOLORCONVHW:
-      return "DDERR_NOCOLORCONVHW";
-    case DDERR_NOCOOPERATIVELEVELSET:
-      return "DDERR_NOCOOPERATIVELEVELSET";
-    case DDERR_NOCOLORKEY:
-      return "DDERR_NOCOLORKEY";
-    case DDERR_NOCOLORKEYHW:
-      return "DDERR_NOCOLORKEYHW";
-    case DDERR_NODIRECTDRAWSUPPORT:
-      return "DDERR_NODIRECTDRAWSUPPORT";
-    case DDERR_NOEXCLUSIVEMODE:
-      return "DDERR_NOEXCLUSIVEMODE";
-    case DDERR_NOFLIPHW:
-      return "DDERR_NOFLIPHW";
-    case DDERR_NOGDI:
-      return "DDERR_NOGDI";
-    case DDERR_NOMIRRORHW:
-      return "DDERR_NOMIRRORHW";
-    case DDERR_NOTFOUND:
-      return "DDERR_NOTFOUND";
-    case DDERR_NOOVERLAYHW:
-      return "DDERR_NOOVERLAYHW";
-    case DDERR_NORASTEROPHW:
-      return "DDERR_NORASTEROPHW";
-    case DDERR_NOROTATIONHW:
-      return "DDERR_NOROTATIONHW";
-    case DDERR_NOSTRETCHHW:
-      return "DDERR_NOSTRETCHHW";
-    case DDERR_NOT4BITCOLOR:
-      return "DDERR_NOT4BITCOLOR";
-    case DDERR_NOT4BITCOLORINDEX:
-      return "DDERR_NOT4BITCOLORINDEX";
-    case DDERR_NOT8BITCOLOR:
-      return "DDERR_NOT8BITCOLOR";
-    case DDERR_NOTEXTUREHW:
-      return "DDERR_NOTEXTUREHW";
-    case DDERR_NOVSYNCHW:
-      return "DDERR_NOVSYNCHW";
-    case DDERR_NOZBUFFERHW:
-      return "DDERR_NOZBUFFERHW";
-    case DDERR_NOZOVERLAYHW:
-      return "DDERR_NOZOVERLAYHW";
-    case DDERR_OUTOFCAPS:
-      return "DDERR_OUTOFCAPS";
-    case DDERR_OUTOFMEMORY:
-      return "DDERR_OUTOFMEMORY";
-    case DDERR_OUTOFVIDEOMEMORY:
-      return "DDERR_OUTOFVIDEOMEMORY";
-    case DDERR_OVERLAYCANTCLIP:
-      return "DDERR_OVERLAYCANTCLIP";
-    case DDERR_OVERLAYCOLORKEYONLYONEACTIVE:
-      return "DDERR_OVERLAYCOLORKEYONLYONEACTIVE";
-    case DDERR_PALETTEBUSY:
-      return "DDERR_PALETTEBUSY";
-    case DDERR_COLORKEYNOTSET:
-      return "DDERR_COLORKEYNOTSET";
-    case DDERR_SURFACEALREADYATTACHED:
-      return "DDERR_SURFACEALREADYATTACHED";
-    case DDERR_SURFACEALREADYDEPENDENT:
-      return "DDERR_SURFACEALREADYDEPENDENT";
-    case DDERR_SURFACEBUSY:
-      return "DDERR_SURFACEBUSY";
-    case DDERR_CANTLOCKSURFACE:
-      return "DDERR_CANTLOCKSURFACE";
-    case DDERR_SURFACEISOBSCURED:
-      return "DDERR_SURFACEISOBSCURED";
-    case DDERR_SURFACELOST:
-      return "DDERR_SURFACELOST";
-    case DDERR_SURFACENOTATTACHED:
-      return "DDERR_SURFACENOTATTACHED";
-    case DDERR_TOOBIGHEIGHT:
-      return "DDERR_TOOBIGHEIGHT";
-    case DDERR_TOOBIGSIZE:
-      return "DDERR_TOOBIGSIZE";
-    case DDERR_TOOBIGWIDTH:
-      return "DDERR_TOOBIGWIDTH";
-    case DDERR_UNSUPPORTED:
-      return "DDERR_UNSUPPORTED";
-    case DDERR_UNSUPPORTEDFORMAT:
-      return "DDERR_UNSUPPORTEDFORMAT";
-    case DDERR_UNSUPPORTEDMASK:
-      return "DDERR_UNSUPPORTEDMASK";
-    case DDERR_VERTICALBLANKINPROGRESS:
-      return "DDERR_VERTICALBLANKINPROGRESS";
-    case DDERR_WASSTILLDRAWING:
-      return "DDERR_WASSTILLDRAWING";
-    case DDERR_XALIGN:
-      return "DDERR_XALIGN";
-    case DDERR_INVALIDDIRECTDRAWGUID:
-      return "DDERR_INVALIDDIRECTDRAWGUID";
-    case DDERR_DIRECTDRAWALREADYCREATED:
-      return "DDERR_DIRECTDRAWALREADYCREATED";
-    case DDERR_NODIRECTDRAWHW:
-      return "DDERR_NODIRECTDRAWHW";
-    case DDERR_PRIMARYSURFACEALREADYEXISTS:
-      return "DDERR_PRIMARYSURFACEALREADYEXISTS";
-    case DDERR_NOEMULATION:
-      return "DDERR_NOEMULATION";
-    case DDERR_REGIONTOOSMALL:
-      return "DDERR_REGIONTOOSMALL";
-    case DDERR_CLIPPERISUSINGHWND:
-      return "DDERR_CLIPPERISUSINGHWND";
-    case DDERR_NOCLIPPERATTACHED:
-      return "DDERR_NOCLIPPERATTACHED";
-    case DDERR_NOHWND:
-      return "DDERR_NOHWND";
-    case DDERR_HWNDSUBCLASSED:
-      return "DDERR_HWNDSUBCLASSED";
-    case DDERR_HWNDALREADYSET:
-      return "DDERR_HWNDALREADYSET";
-    case DDERR_NOPALETTEATTACHED:
-      return "DDERR_NOPALETTEATTACHED";
-    case DDERR_NOPALETTEHW:
-      return "DDERR_NOPALETTEHW";
-    case DDERR_BLTFASTCANTCLIP:
-      return "DDERR_BLTFASTCANTCLIP";
-    case DDERR_NOBLTHW:
-      return "DDERR_NOBLTHW";
-    case DDERR_NODDROPSHW:
-      return "DDERR_NODDROPSHW";
-    case DDERR_OVERLAYNOTVISIBLE:
-      return "DDERR_OVERLAYNOTVISIBLE";
-    case DDERR_NOOVERLAYDEST:
-      return "DDERR_NOOVERLAYDEST";
-    case DDERR_INVALIDPOSITION:
-      return "DDERR_INVALIDPOSITION";
-    case DDERR_NOTAOVERLAYSURFACE:
-      return "DDERR_NOTAOVERLAYSURFACE";
-    case DDERR_EXCLUSIVEMODEALREADYSET:
-      return "DDERR_EXCLUSIVEMODEALREADYSET";
-    case DDERR_NOTFLIPPABLE:
-      return "DDERR_NOTFLIPPABLE";
-    case DDERR_CANTDUPLICATE:
-      return "DDERR_CANTDUPLICATE";
-    case DDERR_NOTLOCKED:
-      return "DDERR_NOTLOCKED";
-    case DDERR_CANTCREATEDC:
-      return "DDERR_CANTCREATEDC";
-    case DDERR_NODC:
-      return "DDERR_NODC";
-    case DDERR_WRONGMODE:
-      return "DDERR_WRONGMODE";
-    case DDERR_IMPLICITLYCREATED:
-      return "DDERR_IMPLICITLYCREATED";
-    case DDERR_NOTPALETTIZED:
-      return "DDERR_NOTPALETTIZED";
-    case DDERR_UNSUPPORTEDMODE:
-      return "DDERR_UNSUPPORTEDMODE";
-    case DDERR_NOMIPMAPHW:
-      return "DDERR_NOMIPMAPHW";
-    case DDERR_INVALIDSURFACETYPE:
-      return "DDERR_INVALIDSURFACETYPE";
-    case DDERR_DCALREADYCREATED:
-      return "DDERR_DCALREADYCREATED";
-    case DDERR_CANTPAGELOCK:
-      return "DDERR_CANTPAGELOCK";
-    case DDERR_CANTPAGEUNLOCK:
-      return "DDERR_CANTPAGEUNLOCK";
-    case DDERR_NOTPAGELOCKED:
-      return "DDERR_NOTPAGELOCKED";
-    case DDERR_NOTINITIALIZED:
-      return "DDERR_NOTINITIALIZED";
-  }
-  return "Unknown Error";
+gst_directdraw_sink_xoverlay_interface_init (GstXOverlayClass * iface)
+{
+  iface->set_xwindow_id = gst_directdraw_sink_set_window_id;
+  iface->expose = gst_directdraw_sink_expose;
+}
+
+static void
+gst_directdraw_sink_init_interfaces (GType type)
+{
+  static const GInterfaceInfo iface_info = {
+    (GInterfaceInitFunc) gst_directdraw_sink_interface_init,
+    NULL,
+    NULL,
+  };
+
+  static const GInterfaceInfo xoverlay_info = {
+    (GInterfaceInitFunc) gst_directdraw_sink_xoverlay_interface_init,
+    NULL,
+    NULL,
+  };
+
+  g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE,
+      &iface_info);
+  g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &xoverlay_info);
 }
 
 /* Subclass of GstBuffer which manages buffer_pool surfaces lifetime    */
@@ -574,7 +264,8 @@ gst_ddrawsurface_finalize (GstDDrawSurface * surface)
   if ((surface->width != ddrawsink->video_width) ||
       (surface->height != ddrawsink->video_height) ||
       (memcmp (&surface->dd_pixel_format, &ddrawsink->dd_pixel_format,
-              sizeof (DDPIXELFORMAT)) != 0)
+              sizeof (DDPIXELFORMAT)) != 0 ||
+          !gst_directdraw_sink_surface_check (ddrawsink, surface))
       ) {
     GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
         "destroy image as its size changed %dx%d vs current %dx%d",
@@ -724,6 +415,8 @@ gst_directdraw_sink_init (GstDirectDrawSink * ddrawsink,
   ddrawsink->buffer_pool = NULL;
   ddrawsink->keep_aspect_ratio = FALSE;
   ddrawsink->pool_lock = g_mutex_new ();
+  ddrawsink->can_blit_between_colorspace = TRUE;
+  ddrawsink->must_recreate_offscreen = FALSE;
   memset (&ddrawsink->dd_pixel_format, 0, sizeof (DDPIXELFORMAT));
 
   /*video default values */
@@ -794,7 +487,7 @@ gst_directdraw_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
   }
 
   /* create an offscreen surface with the caps */
-  ret = gst_directdraw_sink_create_ddraw_surface (ddrawsink);
+  ret = gst_directdraw_sink_check_offscreen_surface (ddrawsink);
   if (!ret) {
     GST_ELEMENT_ERROR (ddrawsink, CORE, NEGOTIATION,
         ("Can't create a directdraw offscreen surface with the input caps"),
@@ -851,13 +544,13 @@ gst_directdraw_sink_buffer_alloc (GstBaseSink * bsink, guint64 offset,
 {
   GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (bsink);
   GstDDrawSurface *surface = NULL;
-  GstStructure *structure = NULL;
   GstFlowReturn ret = GST_FLOW_OK;
+  GstCaps *buffer_caps = caps;
+  gboolean buffercaps_unref = FALSE;
 
   GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
       "a buffer of %d bytes was requested", size);
 
-  structure = gst_caps_get_structure (caps, 0);
   g_mutex_lock (ddrawsink->pool_lock);
 
   /* Inspect our buffer pool */
@@ -872,7 +565,8 @@ gst_directdraw_sink_buffer_alloc (GstBaseSink * bsink, guint64 offset,
       if ((surface->width != ddrawsink->video_width) ||
           (surface->height != ddrawsink->video_height) ||
           (memcmp (&surface->dd_pixel_format, &ddrawsink->dd_pixel_format,
-                  sizeof (DDPIXELFORMAT)))
+                  sizeof (DDPIXELFORMAT)) ||
+              !gst_directdraw_sink_surface_check (ddrawsink, surface))
           ) {
         gst_directdraw_sink_surface_destroy (ddrawsink, surface);
         surface = NULL;
@@ -883,20 +577,111 @@ gst_directdraw_sink_buffer_alloc (GstBaseSink * bsink, guint64 offset,
     }
   }
 
+  if (!ddrawsink->can_blit_between_colorspace) {
+    /* Hardware doesn't support blit from one colorspace to another.
+     * Check if the colorspace of the current display mode has changed since
+     * the last negociation. If it's the case, we will have to renegociate
+     */
+    guint depth;
+    HRESULT hres;
+    DDSURFACEDESC2 surface_desc;
+    GstStructure *structure = NULL;
+
+    structure = gst_caps_get_structure (caps, 0);
+    if (!gst_structure_get_int (structure, "depth", &depth)) {
+      GST_CAT_DEBUG_OBJECT (directdrawsink_debug, ddrawsink,
+          "Can't get depth from buffer_alloc caps");
+      return GST_FLOW_ERROR;
+    }
+    surface_desc.dwSize = sizeof (DDSURFACEDESC);
+    hres = IDirectDraw7_GetDisplayMode (ddrawsink->ddraw_object, &surface_desc);
+    if (hres != DD_OK) {
+      GST_CAT_DEBUG_OBJECT (directdrawsink_debug, ddrawsink,
+          "Can't get current display mode (error=%d)", hres);
+      return GST_FLOW_ERROR;
+    }
+
+    if (depth != gst_directdraw_sink_get_depth (&surface_desc.ddpfPixelFormat)) {
+      GstCaps *copy_caps = NULL;
+      GstStructure *copy_structure = NULL;
+      GstCaps *display_caps = NULL;
+      GstStructure *display_structure = NULL;
+
+      /* make a copy of the original caps */
+      copy_caps = gst_caps_copy (caps);
+      copy_structure = gst_caps_get_structure (copy_caps, 0);
+
+      display_caps =
+          gst_directdraw_sink_create_caps_from_surfacedesc (&surface_desc);
+      if (display_caps) {
+        display_structure = gst_caps_get_structure (display_caps, 0);
+        if (display_structure) {
+          gint bpp, endianness, red_mask, green_mask, blue_mask;
+
+          /* get new display mode properties */
+          gst_structure_get_int (display_structure, "depth", &depth);
+          gst_structure_get_int (display_structure, "bpp", &bpp);
+          gst_structure_get_int (display_structure, "endianness", &endianness);
+          gst_structure_get_int (display_structure, "red_mask", &red_mask);
+          gst_structure_get_int (display_structure, "green_mask", &green_mask);
+          gst_structure_get_int (display_structure, "blue_mask", &blue_mask);
+
+          /* apply the new display mode changes to the previous caps */
+          gst_structure_set (copy_structure,
+              "bpp", G_TYPE_INT, bpp,
+              "depth", G_TYPE_INT, depth,
+              "endianness", G_TYPE_INT, endianness,
+              "red_mask", G_TYPE_INT, red_mask,
+              "green_mask", G_TYPE_INT, green_mask,
+              "blue_mask", G_TYPE_INT, blue_mask, NULL);
+
+          if (gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (ddrawsink),
+                  copy_caps)) {
+            buffer_caps = copy_caps;
+            buffercaps_unref = TRUE;
+            /* update buffer size needed to store video frames according to new caps */
+            size = ddrawsink->video_width * ddrawsink->video_height * (bpp / 8);
+
+            /* update our member pixel format */
+            gst_ddrawvideosink_get_format_from_caps (ddrawsink, buffer_caps,
+                &ddrawsink->dd_pixel_format);
+            ddrawsink->must_recreate_offscreen = TRUE;
+
+            GST_CAT_DEBUG_OBJECT (directdrawsink_debug, ddrawsink,
+                " desired caps %s \n\n new caps %s", gst_caps_to_string (caps),
+                gst_caps_to_string (buffer_caps));
+          } else {
+            GST_CAT_DEBUG_OBJECT (directdrawsink_debug, ddrawsink,
+                "peer refused caps re-negociation "
+                "and we can't render with the current caps.");
+            ret = GST_FLOW_ERROR;
+          }
+        }
+        gst_caps_unref (display_caps);
+      }
+
+      if (!buffercaps_unref)
+        gst_caps_unref (copy_caps);
+    }
+  }
+
   /* We haven't found anything, creating a new one */
   if (!surface) {
-    surface = gst_directdraw_sink_surface_create (ddrawsink, caps, size);
+    surface = gst_directdraw_sink_surface_create (ddrawsink, buffer_caps, size);
   }
 
   /* Now we should have a surface, set appropriate caps on it */
   if (surface) {
-    gst_buffer_set_caps (GST_BUFFER (surface), caps);
+    gst_buffer_set_caps (GST_BUFFER (surface), buffer_caps);
   }
 
   g_mutex_unlock (ddrawsink->pool_lock);
 
   *buf = GST_BUFFER (surface);
 
+  if (buffercaps_unref)
+    gst_caps_unref (buffer_caps);
+
   return ret;
 }
 
@@ -912,13 +697,9 @@ gst_directdraw_sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
     /* save a reference to the input buffer */
     if (ddrawsink->last_buffer != buf) {
       if (ddrawsink->last_buffer) {
-        GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink, "unreffing %p",
-            ddrawsink->last_buffer);
         gst_buffer_unref (ddrawsink->last_buffer);
       }
     }
-    GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink,
-        "reffing %p as our current buffer", buf);
     ddrawsink->last_buffer = gst_buffer_ref (buf);
   } else {
     /* use last buffer */
@@ -944,6 +725,17 @@ gst_directdraw_sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
         &destsurf_rect);
   }
 
+  if (ddrawsink->must_recreate_offscreen && ddrawsink->offscreen_surface) {
+    IDirectDrawSurface7_Release (ddrawsink->offscreen_surface);
+    ddrawsink->offscreen_surface = NULL;
+  }
+
+  /* check for surfaces lost */
+  if (!gst_directdraw_sink_check_primary_surface (ddrawsink) ||
+      !gst_directdraw_sink_check_offscreen_surface (ddrawsink)) {
+    return GST_FLOW_ERROR;
+  }
+
   if (!GST_IS_DDRAWSURFACE (buf) ||
       ((GST_IS_DDRAWSURFACE (buf)) && (GST_BUFFER (buf)->malloc_data))) {
     /* We are receiving a system memory buffer so we will copy 
@@ -956,12 +748,6 @@ gst_directdraw_sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
     ZeroMemory (&surf_desc, sizeof (surf_desc));
     surf_desc.dwSize = sizeof (surf_desc);
 
-    /* Check for lost surface */
-    if (IDirectDrawSurface7_IsLost (ddrawsink->offscreen_surface) ==
-        DDERR_SURFACELOST) {
-      IDirectDrawSurface7_Restore (ddrawsink->offscreen_surface);
-    }
-
     /* Lock the surface */
     hRes =
         IDirectDrawSurface7_Lock (ddrawsink->offscreen_surface, NULL,
@@ -1009,11 +795,6 @@ gst_directdraw_sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
     IDirectDrawSurface7_Unlock (surface->surface, NULL);
     surface->locked = FALSE;
 
-    /* Check for lost surfaces */
-    if (IDirectDrawSurface7_IsLost (surface->surface) == DDERR_SURFACELOST) {
-      IDirectDrawSurface7_Restore (surface->surface);
-    }
-
     /* blit to our primary surface */
     hRes = IDirectDrawSurface7_Blt (ddrawsink->primary_surface, &destsurf_rect,
         surface->surface, NULL, DDBLT_WAIT, NULL);
@@ -1022,8 +803,349 @@ gst_directdraw_sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
           "IDirectDrawSurface7_Blt (offscreen surface from buffer_alloc) "
           "returned %s", DDErrorString (hRes));
   }
-
-  return GST_FLOW_OK;
+
+  return GST_FLOW_OK;
+}
+
+static void
+gst_directdraw_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
+    GstClockTime * start, GstClockTime * end)
+{
+  GstDirectDrawSink *ddrawsink;
+
+  ddrawsink = GST_DIRECTDRAW_SINK (bsink);
+
+  if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
+    *start = GST_BUFFER_TIMESTAMP (buf);
+    if (GST_BUFFER_DURATION_IS_VALID (buf)) {
+      *end = *start + GST_BUFFER_DURATION (buf);
+    } else {
+      if (ddrawsink->fps_n > 0) {
+        *end = *start + (GST_SECOND * ddrawsink->fps_d) / ddrawsink->fps_n;
+      }
+    }
+  }
+}
+
+/* Utility functions */
+
+/* this function fill a DDPIXELFORMAT using Gstreamer caps */
+static gboolean
+gst_ddrawvideosink_get_format_from_caps (GstDirectDrawSink * ddrawsink,
+    GstCaps * caps, DDPIXELFORMAT * pPixelFormat)
+{
+  GstStructure *structure = NULL;
+  gboolean ret = TRUE;
+
+  /* check params */
+  g_return_val_if_fail (pPixelFormat, FALSE);
+  g_return_val_if_fail (caps, FALSE);
+
+  /* init structure */
+  memset (pPixelFormat, 0, sizeof (DDPIXELFORMAT));
+  pPixelFormat->dwSize = sizeof (DDPIXELFORMAT);
+
+  if (!(structure = gst_caps_get_structure (caps, 0))) {
+    GST_CAT_ERROR_OBJECT (directdrawsink_debug, ddrawsink,
+        "can't get structure pointer from caps");
+    return FALSE;
+  }
+
+  if (gst_structure_has_name (structure, "video/x-raw-rgb")) {
+    gint depth, bitcount, bitmask, endianness;
+
+    pPixelFormat->dwFlags = DDPF_RGB;
+    ret &= gst_structure_get_int (structure, "bpp", &bitcount);
+    pPixelFormat->dwRGBBitCount = bitcount;
+    ret &= gst_structure_get_int (structure, "depth", &depth);
+    ret &= gst_structure_get_int (structure, "red_mask", &bitmask);
+    pPixelFormat->dwRBitMask = bitmask;
+    ret &= gst_structure_get_int (structure, "green_mask", &bitmask);
+    pPixelFormat->dwGBitMask = bitmask;
+    ret &= gst_structure_get_int (structure, "blue_mask", &bitmask);
+    pPixelFormat->dwBBitMask = bitmask;
+
+    gst_structure_get_int (structure, "endianness", &endianness);
+    if (endianness == G_BIG_ENDIAN) {
+      endianness = G_LITTLE_ENDIAN;
+      pPixelFormat->dwRBitMask = GUINT32_TO_BE (pPixelFormat->dwRBitMask);
+      pPixelFormat->dwGBitMask = GUINT32_TO_BE (pPixelFormat->dwGBitMask);
+      pPixelFormat->dwBBitMask = GUINT32_TO_BE (pPixelFormat->dwBBitMask);
+    }
+  } else if (gst_structure_has_name (structure, "video/x-raw-yuv")) {
+    gint fourcc;
+
+    pPixelFormat->dwFlags = DDPF_FOURCC;
+    ret &= gst_structure_get_fourcc (structure, "format", &fourcc);
+    pPixelFormat->dwFourCC = fourcc;
+  } else {
+    GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
+        "unknown caps name received %" GST_PTR_FORMAT, caps);
+    ret = FALSE;
+  }
+
+  return ret;
+}
+
+/* This function centers the RECT of source surface to
+a dest surface and set the result RECT into result */
+static void
+gst_directdraw_sink_center_rect (GstDirectDrawSink * ddrawsink, RECT src,
+    RECT dst, RECT * result)
+{
+  gdouble src_ratio, dst_ratio;
+  long src_width = src.right;
+  long src_height = src.bottom;
+  long dst_width = dst.right - dst.left;
+  long dst_heigth = dst.bottom - dst.top;
+  long result_width = 0, result_height = 0;
+
+  g_return_if_fail (result != NULL);
+
+  src_ratio = (gdouble) src_width / src_height;
+  dst_ratio = (gdouble) dst_width / dst_heigth;
+
+  if (src_ratio > dst_ratio) {
+    /* new height */
+    result_height = (long) (dst_width / src_ratio);
+
+    result->left = dst.left;
+    result->right = dst.right;
+    result->top = dst.top + (dst_heigth - result_height) / 2;
+    result->bottom = result->top + result_height;
+
+  } else if (src_ratio < dst_ratio) {
+    /* new width */
+    result_width = (long) (dst_heigth * src_ratio);
+
+    result->top = dst.top;
+    result->bottom = dst.bottom;
+    result->left = dst.left + (dst_width - result_width) / 2;
+    result->right = result->left + result_width;
+
+  } else {
+    /* same ratio */
+    memcpy (result, &dst, sizeof (RECT));
+  }
+
+  GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
+      "source is %ldx%ld dest is %ldx%ld, result is %ldx%ld with x,y %ldx%ld",
+      src_width, src_height, dst_width, dst_heigth,
+      result->right - result->left, result->bottom - result->top, result->left,
+      result->right);
+}
+
+/**
+ * Get DirectDraw error message.
+ * @hr: HRESULT code
+ * Returns: Text representation of the error.
+ */
+char *
+DDErrorString (HRESULT hr)
+{
+  switch (hr) {
+    case DDERR_ALREADYINITIALIZED:
+      return "DDERR_ALREADYINITIALIZED";
+    case DDERR_CANNOTATTACHSURFACE:
+      return "DDERR_CANNOTATTACHSURFACE";
+    case DDERR_CANNOTDETACHSURFACE:
+      return "DDERR_CANNOTDETACHSURFACE";
+    case DDERR_CURRENTLYNOTAVAIL:
+      return "DDERR_CURRENTLYNOTAVAIL";
+    case DDERR_EXCEPTION:
+      return "DDERR_EXCEPTION";
+    case DDERR_GENERIC:
+      return "DDERR_GENERIC";
+    case DDERR_HEIGHTALIGN:
+      return "DDERR_HEIGHTALIGN";
+    case DDERR_INCOMPATIBLEPRIMARY:
+      return "DDERR_INCOMPATIBLEPRIMARY";
+    case DDERR_INVALIDCAPS:
+      return "DDERR_INVALIDCAPS";
+    case DDERR_INVALIDCLIPLIST:
+      return "DDERR_INVALIDCLIPLIST";
+    case DDERR_INVALIDMODE:
+      return "DDERR_INVALIDMODE";
+    case DDERR_INVALIDOBJECT:
+      return "DDERR_INVALIDOBJECT";
+    case DDERR_INVALIDPARAMS:
+      return "DDERR_INVALIDPARAMS";
+    case DDERR_INVALIDPIXELFORMAT:
+      return "DDERR_INVALIDPIXELFORMAT";
+    case DDERR_INVALIDRECT:
+      return "DDERR_INVALIDRECT";
+    case DDERR_LOCKEDSURFACES:
+      return "DDERR_LOCKEDSURFACES";
+    case DDERR_NO3D:
+      return "DDERR_NO3D";
+    case DDERR_NOALPHAHW:
+      return "DDERR_NOALPHAHW";
+    case DDERR_NOCLIPLIST:
+      return "DDERR_NOCLIPLIST";
+    case DDERR_NOCOLORCONVHW:
+      return "DDERR_NOCOLORCONVHW";
+    case DDERR_NOCOOPERATIVELEVELSET:
+      return "DDERR_NOCOOPERATIVELEVELSET";
+    case DDERR_NOCOLORKEY:
+      return "DDERR_NOCOLORKEY";
+    case DDERR_NOCOLORKEYHW:
+      return "DDERR_NOCOLORKEYHW";
+    case DDERR_NODIRECTDRAWSUPPORT:
+      return "DDERR_NODIRECTDRAWSUPPORT";
+    case DDERR_NOEXCLUSIVEMODE:
+      return "DDERR_NOEXCLUSIVEMODE";
+    case DDERR_NOFLIPHW:
+      return "DDERR_NOFLIPHW";
+    case DDERR_NOGDI:
+      return "DDERR_NOGDI";
+    case DDERR_NOMIRRORHW:
+      return "DDERR_NOMIRRORHW";
+    case DDERR_NOTFOUND:
+      return "DDERR_NOTFOUND";
+    case DDERR_NOOVERLAYHW:
+      return "DDERR_NOOVERLAYHW";
+    case DDERR_NORASTEROPHW:
+      return "DDERR_NORASTEROPHW";
+    case DDERR_NOROTATIONHW:
+      return "DDERR_NOROTATIONHW";
+    case DDERR_NOSTRETCHHW:
+      return "DDERR_NOSTRETCHHW";
+    case DDERR_NOT4BITCOLOR:
+      return "DDERR_NOT4BITCOLOR";
+    case DDERR_NOT4BITCOLORINDEX:
+      return "DDERR_NOT4BITCOLORINDEX";
+    case DDERR_NOT8BITCOLOR:
+      return "DDERR_NOT8BITCOLOR";
+    case DDERR_NOTEXTUREHW:
+      return "DDERR_NOTEXTUREHW";
+    case DDERR_NOVSYNCHW:
+      return "DDERR_NOVSYNCHW";
+    case DDERR_NOZBUFFERHW:
+      return "DDERR_NOZBUFFERHW";
+    case DDERR_NOZOVERLAYHW:
+      return "DDERR_NOZOVERLAYHW";
+    case DDERR_OUTOFCAPS:
+      return "DDERR_OUTOFCAPS";
+    case DDERR_OUTOFMEMORY:
+      return "DDERR_OUTOFMEMORY";
+    case DDERR_OUTOFVIDEOMEMORY:
+      return "DDERR_OUTOFVIDEOMEMORY";
+    case DDERR_OVERLAYCANTCLIP:
+      return "DDERR_OVERLAYCANTCLIP";
+    case DDERR_OVERLAYCOLORKEYONLYONEACTIVE:
+      return "DDERR_OVERLAYCOLORKEYONLYONEACTIVE";
+    case DDERR_PALETTEBUSY:
+      return "DDERR_PALETTEBUSY";
+    case DDERR_COLORKEYNOTSET:
+      return "DDERR_COLORKEYNOTSET";
+    case DDERR_SURFACEALREADYATTACHED:
+      return "DDERR_SURFACEALREADYATTACHED";
+    case DDERR_SURFACEALREADYDEPENDENT:
+      return "DDERR_SURFACEALREADYDEPENDENT";
+    case DDERR_SURFACEBUSY:
+      return "DDERR_SURFACEBUSY";
+    case DDERR_CANTLOCKSURFACE:
+      return "DDERR_CANTLOCKSURFACE";
+    case DDERR_SURFACEISOBSCURED:
+      return "DDERR_SURFACEISOBSCURED";
+    case DDERR_SURFACELOST:
+      return "DDERR_SURFACELOST";
+    case DDERR_SURFACENOTATTACHED:
+      return "DDERR_SURFACENOTATTACHED";
+    case DDERR_TOOBIGHEIGHT:
+      return "DDERR_TOOBIGHEIGHT";
+    case DDERR_TOOBIGSIZE:
+      return "DDERR_TOOBIGSIZE";
+    case DDERR_TOOBIGWIDTH:
+      return "DDERR_TOOBIGWIDTH";
+    case DDERR_UNSUPPORTED:
+      return "DDERR_UNSUPPORTED";
+    case DDERR_UNSUPPORTEDFORMAT:
+      return "DDERR_UNSUPPORTEDFORMAT";
+    case DDERR_UNSUPPORTEDMASK:
+      return "DDERR_UNSUPPORTEDMASK";
+    case DDERR_VERTICALBLANKINPROGRESS:
+      return "DDERR_VERTICALBLANKINPROGRESS";
+    case DDERR_WASSTILLDRAWING:
+      return "DDERR_WASSTILLDRAWING";
+    case DDERR_XALIGN:
+      return "DDERR_XALIGN";
+    case DDERR_INVALIDDIRECTDRAWGUID:
+      return "DDERR_INVALIDDIRECTDRAWGUID";
+    case DDERR_DIRECTDRAWALREADYCREATED:
+      return "DDERR_DIRECTDRAWALREADYCREATED";
+    case DDERR_NODIRECTDRAWHW:
+      return "DDERR_NODIRECTDRAWHW";
+    case DDERR_PRIMARYSURFACEALREADYEXISTS:
+      return "DDERR_PRIMARYSURFACEALREADYEXISTS";
+    case DDERR_NOEMULATION:
+      return "DDERR_NOEMULATION";
+    case DDERR_REGIONTOOSMALL:
+      return "DDERR_REGIONTOOSMALL";
+    case DDERR_CLIPPERISUSINGHWND:
+      return "DDERR_CLIPPERISUSINGHWND";
+    case DDERR_NOCLIPPERATTACHED:
+      return "DDERR_NOCLIPPERATTACHED";
+    case DDERR_NOHWND:
+      return "DDERR_NOHWND";
+    case DDERR_HWNDSUBCLASSED:
+      return "DDERR_HWNDSUBCLASSED";
+    case DDERR_HWNDALREADYSET:
+      return "DDERR_HWNDALREADYSET";
+    case DDERR_NOPALETTEATTACHED:
+      return "DDERR_NOPALETTEATTACHED";
+    case DDERR_NOPALETTEHW:
+      return "DDERR_NOPALETTEHW";
+    case DDERR_BLTFASTCANTCLIP:
+      return "DDERR_BLTFASTCANTCLIP";
+    case DDERR_NOBLTHW:
+      return "DDERR_NOBLTHW";
+    case DDERR_NODDROPSHW:
+      return "DDERR_NODDROPSHW";
+    case DDERR_OVERLAYNOTVISIBLE:
+      return "DDERR_OVERLAYNOTVISIBLE";
+    case DDERR_NOOVERLAYDEST:
+      return "DDERR_NOOVERLAYDEST";
+    case DDERR_INVALIDPOSITION:
+      return "DDERR_INVALIDPOSITION";
+    case DDERR_NOTAOVERLAYSURFACE:
+      return "DDERR_NOTAOVERLAYSURFACE";
+    case DDERR_EXCLUSIVEMODEALREADYSET:
+      return "DDERR_EXCLUSIVEMODEALREADYSET";
+    case DDERR_NOTFLIPPABLE:
+      return "DDERR_NOTFLIPPABLE";
+    case DDERR_CANTDUPLICATE:
+      return "DDERR_CANTDUPLICATE";
+    case DDERR_NOTLOCKED:
+      return "DDERR_NOTLOCKED";
+    case DDERR_CANTCREATEDC:
+      return "DDERR_CANTCREATEDC";
+    case DDERR_NODC:
+      return "DDERR_NODC";
+    case DDERR_WRONGMODE:
+      return "DDERR_WRONGMODE";
+    case DDERR_IMPLICITLYCREATED:
+      return "DDERR_IMPLICITLYCREATED";
+    case DDERR_NOTPALETTIZED:
+      return "DDERR_NOTPALETTIZED";
+    case DDERR_UNSUPPORTEDMODE:
+      return "DDERR_UNSUPPORTEDMODE";
+    case DDERR_NOMIPMAPHW:
+      return "DDERR_NOMIPMAPHW";
+    case DDERR_INVALIDSURFACETYPE:
+      return "DDERR_INVALIDSURFACETYPE";
+    case DDERR_DCALREADYCREATED:
+      return "DDERR_DCALREADYCREATED";
+    case DDERR_CANTPAGELOCK:
+      return "DDERR_CANTPAGELOCK";
+    case DDERR_CANTPAGEUNLOCK:
+      return "DDERR_CANTPAGEUNLOCK";
+    case DDERR_NOTPAGELOCKED:
+      return "DDERR_NOTPAGELOCKED";
+    case DDERR_NOTINITIALIZED:
+      return "DDERR_NOTINITIALIZED";
+  }
+  return "Unknown Error";
 }
 
 static gboolean
@@ -1031,7 +1153,6 @@ gst_directdraw_sink_setup_ddraw (GstDirectDrawSink * ddrawsink)
 {
   gboolean bRet = TRUE;
   HRESULT hRes;
-  DDSURFACEDESC2 dd_surface_desc;
 
   /* create an instance of the ddraw object use DDCREATE_EMULATIONONLY as first parameter to
      force Directdraw to use the hardware emulation layer */
@@ -1054,30 +1175,18 @@ gst_directdraw_sink_setup_ddraw (GstDirectDrawSink * ddrawsink)
     return FALSE;
   }
 
-  /*create our primary surface */
-  memset (&dd_surface_desc, 0, sizeof (dd_surface_desc));
-  dd_surface_desc.dwSize = sizeof (dd_surface_desc);
-  dd_surface_desc.dwFlags = DDSD_CAPS;
-  dd_surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
-  hRes = IDirectDraw7_CreateSurface (ddrawsink->ddraw_object, &dd_surface_desc,
-      &ddrawsink->primary_surface, NULL);
-  if (hRes != DD_OK) {
-    GST_ELEMENT_ERROR (ddrawsink, RESOURCE, WRITE,
-        ("Failed to create our primary surface error=%s", DDErrorString (hRes)),
-        (NULL));
-    return FALSE;
-  }
-
   /* setup the clipper object */
   hRes = IDirectDraw7_CreateClipper (ddrawsink->ddraw_object, 0,
       &ddrawsink->clipper, NULL);
   if (hRes == DD_OK) {
     hRes = IDirectDrawClipper_SetHWnd (ddrawsink->clipper, 0,
         ddrawsink->video_window);
-    hRes = IDirectDrawSurface7_SetClipper (ddrawsink->primary_surface,
-        ddrawsink->clipper);
   }
 
+  /* create our primary surface */
+  if (!gst_directdraw_sink_check_primary_surface (ddrawsink))
+    return FALSE;
+
   /* directdraw objects are setup */
   ddrawsink->setup = TRUE;
 
@@ -1171,10 +1280,95 @@ failed:
 }
 
 static gboolean
-gst_directdraw_sink_create_ddraw_surface (GstDirectDrawSink * ddrawsink)
+gst_directdraw_sink_check_primary_surface (GstDirectDrawSink * ddrawsink)
 {
+  HRESULT hres;
   DDSURFACEDESC2 dd_surface_desc;
-  HRESULT hRes;
+
+  /* if our primary surface already exist, check if it's not lost */
+  if (ddrawsink->primary_surface) {
+    if (IDirectDrawSurface7_IsLost (ddrawsink->primary_surface) == DD_OK) {
+      /* no problem with our primary surface */
+      return TRUE;
+    } else {
+      /* our primary surface was lost, try to restore it */
+      if (IDirectDrawSurface7_Restore (ddrawsink->primary_surface) == DD_OK) {
+        /* restore is done */
+        GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink,
+            "Our primary surface" " was restored after lost");
+        return TRUE;
+      } else {
+        /* failed to restore our primary surface, 
+         * probably because the display mode was changed. 
+         * Release this surface and recreate a new one.
+         */
+        GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink,
+            "Our primary surface"
+            " was lost and display mode has changed. Destroy and recreate our surface.");
+        IDirectDrawSurface7_Release (ddrawsink->primary_surface);
+        ddrawsink->primary_surface = NULL;
+
+        /* also release offscreen surface */
+        IDirectDrawSurface7_Release (ddrawsink->offscreen_surface);
+        ddrawsink->offscreen_surface = NULL;
+      }
+    }
+  }
+
+  /* create our primary surface */
+  memset (&dd_surface_desc, 0, sizeof (dd_surface_desc));
+  dd_surface_desc.dwSize = sizeof (dd_surface_desc);
+  dd_surface_desc.dwFlags = DDSD_CAPS;
+  dd_surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
+  hres = IDirectDraw7_CreateSurface (ddrawsink->ddraw_object, &dd_surface_desc,
+      &ddrawsink->primary_surface, NULL);
+  if (hres != DD_OK) {
+    GST_ELEMENT_ERROR (ddrawsink, RESOURCE, WRITE,
+        ("Failed to create our primary surface error=%s", DDErrorString (hres)),
+        (NULL));
+    return FALSE;
+  }
+
+  /* attach our clipper object to the new primary surface */
+  if (ddrawsink->clipper) {
+    hres = IDirectDrawSurface7_SetClipper (ddrawsink->primary_surface,
+        ddrawsink->clipper);
+  }
+
+  return TRUE;
+}
+
+static gboolean
+gst_directdraw_sink_check_offscreen_surface (GstDirectDrawSink * ddrawsink)
+{
+  DDSURFACEDESC2 dd_surface_desc;
+  HRESULT hres;
+
+  /* if our offscreen surface already exist, check if it's not lost */
+  if (ddrawsink->offscreen_surface) {
+    if (IDirectDrawSurface7_IsLost (ddrawsink->offscreen_surface) == DD_OK) {
+      /* no problem with our offscreen surface */
+      return TRUE;
+    } else {
+      /* our offscreen surface was lost, try to restore it */
+      if (IDirectDrawSurface7_Restore (ddrawsink->offscreen_surface) == DD_OK) {
+        /* restore is done */
+        GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink,
+            "Our offscreen surface" " was restored after lost");
+        return TRUE;
+      } else {
+        /* failed to restore our offscreen surface, 
+         * probably because the display mode was changed. 
+         * Release this surface and recreate a new one.
+         */
+        GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink,
+            "Our offscreen surface"
+            " was lost and display mode has changed. Destroy and recreate our surface.");
+        IDirectDrawSurface7_Release (ddrawsink->offscreen_surface);
+        ddrawsink->offscreen_surface = NULL;
+      }
+    }
+  }
 
   memset (&dd_surface_desc, 0, sizeof (dd_surface_desc));
   dd_surface_desc.dwSize = sizeof (dd_surface_desc);
@@ -1185,38 +1379,19 @@ gst_directdraw_sink_create_ddraw_surface (GstDirectDrawSink * ddrawsink)
   memcpy (&(dd_surface_desc.ddpfPixelFormat), &ddrawsink->dd_pixel_format,
       sizeof (DDPIXELFORMAT));
 
-  dd_surface_desc.ddsCaps.dwCaps =
-      DDSCAPS_OFFSCREENPLAIN /*|DDSCAPS_SYSTEMMEMORY */ ;
-  hRes =
-      IDirectDraw7_CreateSurface (ddrawsink->ddraw_object, &dd_surface_desc,
+  dd_surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
+  hres = IDirectDraw7_CreateSurface (ddrawsink->ddraw_object, &dd_surface_desc,
       &ddrawsink->offscreen_surface, NULL);
-  if (hRes != DD_OK) {
+  if (hres != DD_OK) {
     GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
         "create_ddraw_surface:CreateSurface (offscreen surface for buffer_pool) failed %s",
-        DDErrorString (hRes));
+        DDErrorString (hres));
     return FALSE;
   }
-  return TRUE;
-}
-
-static void
-gst_directdraw_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
-    GstClockTime * start, GstClockTime * end)
-{
-  GstDirectDrawSink *ddrawsink;
 
-  ddrawsink = GST_DIRECTDRAW_SINK (bsink);
+  ddrawsink->must_recreate_offscreen = FALSE;
 
-  if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
-    *start = GST_BUFFER_TIMESTAMP (buf);
-    if (GST_BUFFER_DURATION_IS_VALID (buf)) {
-      *end = *start + GST_BUFFER_DURATION (buf);
-    } else {
-      if (ddrawsink->fps_n > 0) {
-        *end = *start + (GST_SECOND * ddrawsink->fps_d) / ddrawsink->fps_n;
-      }
-    }
-  }
+  return TRUE;
 }
 
 static int
@@ -1254,17 +1429,8 @@ EnumModesCallback2 (LPDDSURFACEDESC2 lpDDSurfaceDesc, LPVOID lpContext)
   if ((lpDDSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_RGB) != DDPF_RGB)
     return DDENUMRET_OK;
 
-  format_caps = gst_caps_new_simple ("video/x-raw-rgb",
-      "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,
-      "bpp", G_TYPE_INT, lpDDSurfaceDesc->ddpfPixelFormat.dwRGBBitCount,
-      "depth", G_TYPE_INT,
-      gst_directdraw_sink_get_depth (&lpDDSurfaceDesc->ddpfPixelFormat),
-      "endianness", G_TYPE_INT, G_LITTLE_ENDIAN, "red_mask", G_TYPE_INT,
-      lpDDSurfaceDesc->ddpfPixelFormat.dwRBitMask, "green_mask", G_TYPE_INT,
-      lpDDSurfaceDesc->ddpfPixelFormat.dwGBitMask, "blue_mask", G_TYPE_INT,
-      lpDDSurfaceDesc->ddpfPixelFormat.dwBBitMask, NULL);
+  format_caps =
+      gst_directdraw_sink_create_caps_from_surfacedesc (lpDDSurfaceDesc);
 
   if (format_caps) {
     gst_caps_append (ddrawsink->caps, format_caps);
@@ -1274,6 +1440,49 @@ EnumModesCallback2 (LPDDSURFACEDESC2 lpDDSurfaceDesc, LPVOID lpContext)
 }
 
 static GstCaps *
+gst_directdraw_sink_create_caps_from_surfacedesc (LPDDSURFACEDESC2 desc)
+{
+  GstCaps *caps = NULL;
+  gint endianness = G_LITTLE_ENDIAN;
+  gint depth;
+
+  if ((desc->ddpfPixelFormat.dwFlags & DDPF_RGB) != DDPF_RGB)
+    return NULL;
+
+  depth = gst_directdraw_sink_get_depth (&desc->ddpfPixelFormat);
+
+  if (desc->ddpfPixelFormat.dwRGBBitCount == 24 ||
+      desc->ddpfPixelFormat.dwRGBBitCount == 32) {
+    /* ffmpegcolorspace handles 24/32 bpp RGB as big-endian. */
+    endianness = G_BIG_ENDIAN;
+    desc->ddpfPixelFormat.dwRBitMask =
+        GUINT32_TO_BE (desc->ddpfPixelFormat.dwRBitMask);
+    desc->ddpfPixelFormat.dwGBitMask =
+        GUINT32_TO_BE (desc->ddpfPixelFormat.dwGBitMask);
+    desc->ddpfPixelFormat.dwBBitMask =
+        GUINT32_TO_BE (desc->ddpfPixelFormat.dwBBitMask);
+    if (desc->ddpfPixelFormat.dwRGBBitCount == 24) {
+      desc->ddpfPixelFormat.dwRBitMask >>= 8;
+      desc->ddpfPixelFormat.dwGBitMask >>= 8;
+      desc->ddpfPixelFormat.dwBBitMask >>= 8;
+    }
+  }
+
+  caps = gst_caps_new_simple ("video/x-raw-rgb",
+      "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,
+      "bpp", G_TYPE_INT, desc->ddpfPixelFormat.dwRGBBitCount,
+      "depth", G_TYPE_INT, depth,
+      "endianness", G_TYPE_INT, endianness,
+      "red_mask", G_TYPE_INT, desc->ddpfPixelFormat.dwRBitMask,
+      "green_mask", G_TYPE_INT, desc->ddpfPixelFormat.dwGBitMask,
+      "blue_mask", G_TYPE_INT, desc->ddpfPixelFormat.dwBBitMask, NULL);
+
+  return caps;
+}
+
+static GstCaps *
 gst_directdraw_sink_get_ddrawcaps (GstDirectDrawSink * ddrawsink)
 {
   HRESULT hRes = S_OK;
@@ -1297,13 +1506,14 @@ gst_directdraw_sink_get_ddrawcaps (GstDirectDrawSink * ddrawsink)
   /*  we don't test for DDCAPS_BLTSTRETCH on the hardware as the directdraw emulation layer can do it */
   if (!(ddcaps_hardware.dwCaps & DDCAPS_BLTFOURCC)) {
     DDSURFACEDESC2 surface_desc;
-    gint endianness = G_LITTLE_ENDIAN;
-    gint depth;
 
     GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
         "hardware doesn't support blit from one colorspace to another one. "
         "so we will create a caps with only the current display mode");
 
+    /* save blit caps */
+    ddrawsink->can_blit_between_colorspace = FALSE;
+
     surface_desc.dwSize = sizeof (DDSURFACEDESC);
     hRes = IDirectDraw7_GetDisplayMode (ddrawsink->ddraw_object, &surface_desc);
     if (hRes != DD_OK) {
@@ -1313,36 +1523,8 @@ gst_directdraw_sink_get_ddrawcaps (GstDirectDrawSink * ddrawsink)
       return NULL;
     }
 
-    depth = gst_directdraw_sink_get_depth (&surface_desc.ddpfPixelFormat);
-
-    if (surface_desc.ddpfPixelFormat.dwRGBBitCount == 24 ||
-        surface_desc.ddpfPixelFormat.dwRGBBitCount == 32) {
-      /* ffmpegcolorspace handles 24/32 bpp RGB as big-endian. */
-      endianness = G_BIG_ENDIAN;
-      surface_desc.ddpfPixelFormat.dwRBitMask =
-          GUINT32_TO_BE (surface_desc.ddpfPixelFormat.dwRBitMask);
-      surface_desc.ddpfPixelFormat.dwGBitMask =
-          GUINT32_TO_BE (surface_desc.ddpfPixelFormat.dwGBitMask);
-      surface_desc.ddpfPixelFormat.dwBBitMask =
-          GUINT32_TO_BE (surface_desc.ddpfPixelFormat.dwBBitMask);
-      if (surface_desc.ddpfPixelFormat.dwRGBBitCount == 24) {
-        surface_desc.ddpfPixelFormat.dwRBitMask >>= 8;
-        surface_desc.ddpfPixelFormat.dwGBitMask >>= 8;
-        surface_desc.ddpfPixelFormat.dwBBitMask >>= 8;
-      }
-    }
-
-    format_caps = gst_caps_new_simple ("video/x-raw-rgb",
-        "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,
-        "bpp", G_TYPE_INT, surface_desc.ddpfPixelFormat.dwRGBBitCount,
-        "depth", G_TYPE_INT, depth,
-        "endianness", G_TYPE_INT, endianness,
-        "red_mask", G_TYPE_INT, surface_desc.ddpfPixelFormat.dwRBitMask,
-        "green_mask", G_TYPE_INT, surface_desc.ddpfPixelFormat.dwGBitMask,
-        "blue_mask", G_TYPE_INT, surface_desc.ddpfPixelFormat.dwBBitMask, NULL);
-
+    format_caps =
+        gst_directdraw_sink_create_caps_from_surfacedesc (&surface_desc);
     if (format_caps) {
       gst_caps_append (ddrawsink->caps, format_caps);
     }
@@ -1356,6 +1538,9 @@ gst_directdraw_sink_get_ddrawcaps (GstDirectDrawSink * ddrawsink)
       "the hardware can blit from one colorspace to another, "
       "then enumerate the colorspace supported by the hardware");
 
+  /* save blit caps */
+  ddrawsink->can_blit_between_colorspace = TRUE;
+
   /* enumerate display modes exposed by directdraw object 
      to know supported RGB modes */
   hRes =
@@ -1376,7 +1561,8 @@ gst_directdraw_sink_get_ddrawcaps (GstDirectDrawSink * ddrawsink)
         ("No supported caps found."), (NULL));
     return NULL;
   }
-  //GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink, "returning caps %s", gst_caps_to_string (ddrawsink->caps));
+
+  /*GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink, "returning caps %s", gst_caps_to_string (ddrawsink->caps)); */
 
   return ddrawsink->caps;
 }
@@ -1418,7 +1604,15 @@ gst_directdraw_sink_surface_create (GstDirectDrawSink * ddrawsink,
         "failed getting pixel format from caps %" GST_PTR_FORMAT, caps);
   }
 
-  if (ddrawsink->ddraw_object) {
+  /* disable return of directdraw surface to buffer alloc because actually I have no solution
+   * to handle display mode changes. The problem is that when the display mode is changed
+   * surface's memory is freed then the upstream filter would crash trying to write to this memory.
+   * Directdraw has a system lock (DDLOCK_NOSYSLOCK to disable it) to prevent display mode changes 
+   * when a surface memory is locked but we need to disable this lock to return multiple buffers (surfaces)
+   * and do not lock directdraw API calls.
+   */
+  if (0) {
+/*  if (ddrawsink->ddraw_object) {*/
     /* Creating an internal surface which will be used as GstBuffer, we used
        the detected pixel format and video dimensions */
 
@@ -1518,6 +1712,29 @@ no_sink:
   return;
 }
 
+static gboolean
+gst_directdraw_sink_surface_check (GstDirectDrawSink * ddrawsink,
+    GstDDrawSurface * surface)
+{
+  if (!surface->surface)
+    return TRUE;                /* system memory buffer */
+
+  if (IDirectDrawSurface7_IsLost (surface->surface) == DD_OK) {
+    /* no problem with this surface */
+    return TRUE;
+  } else {
+    /* this surface was lost, try to restore it */
+    if (IDirectDrawSurface7_Restore (ddrawsink->offscreen_surface) == DD_OK) {
+      /* restore is done */
+      GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink, "A surface from our"
+          " bufferpool was restored after lost");
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
 static void
 gst_directdraw_sink_bufferpool_clear (GstDirectDrawSink * ddrawsink)
 {
index 3bd55dd..68a2a14 100644 (file)
@@ -120,6 +120,15 @@ struct _GstDirectDrawSink
 
   /* TRUE when directdraw objects are setup */
   gboolean setup;
+
+  /* TRUE if the hardware support blitting from one colorspace to another */
+  gboolean can_blit_between_colorspace;
+
+  /* this flag is used to force re-creation of our offscreen surface 
+   * it's need when hardware doesn't support fourcc blit and the bit deph
+   * of the current display mode changes.
+   */
+  gboolean must_recreate_offscreen;
 };
 
 struct _GstDirectDrawSinkClass