sys/directdraw/gstdirectdrawsink.c: Make sure we create our internal window only...
authorJulien Moutte <julien@moutte.net>
Sat, 5 Jan 2008 21:20:08 +0000 (21:20 +0000)
committerJulien Moutte <julien@moutte.net>
Sat, 5 Jan 2008 21:20:08 +0000 (21:20 +0000)
Original commit message from CVS:
2008-01-05  Julien Moutte  <julien@fluendo.com>

* sys/directdraw/gstdirectdrawsink.c:
(gst_directdraw_sink_set_window_id),
(gst_directdraw_sink_set_caps),
(gst_directdraw_sink_change_state),
(gst_directdraw_sink_buffer_alloc),
(gst_directdraw_sink_draw_borders),
(gst_directdraw_sink_show_frame),
(gst_directdraw_sink_setup_ddraw),
(gst_directdraw_sink_window_thread),
(gst_directdraw_sink_get_ddrawcaps),
(gst_directdraw_sink_surface_create): Make sure we create our
internal window only when we need it. That will give a chance to
the application to get the prepare-xwindow-id bus message. Draw
black borders when keeping aspect ratio. Handle the case where
our
rendering window disappears (closed or errors) like other sinks
do. Various 80 columns fixes, improve state change order. That
element could need some more love.

ChangeLog
sys/directdraw/gstdirectdrawsink.c

index 305c3dcfb2282eec3f0262129a1f64e745428307..ccdfeea220b385dc109138e75cde07bc24f210eb 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,22 @@
+2008-01-05  Julien Moutte  <julien@fluendo.com>
+
+       * sys/directdraw/gstdirectdrawsink.c:
+       (gst_directdraw_sink_set_window_id),
+       (gst_directdraw_sink_set_caps), (gst_directdraw_sink_change_state),
+       (gst_directdraw_sink_buffer_alloc),
+       (gst_directdraw_sink_draw_borders),
+       (gst_directdraw_sink_show_frame),
+       (gst_directdraw_sink_setup_ddraw),
+       (gst_directdraw_sink_window_thread),
+       (gst_directdraw_sink_get_ddrawcaps),
+       (gst_directdraw_sink_surface_create): Make sure we create our
+       internal window only when we need it. That will give a chance to
+       the application to get the prepare-xwindow-id bus message. Draw
+       black borders when keeping aspect ratio. Handle the case where our
+       rendering window disappears (closed or errors) like other sinks
+       do. Various 80 columns fixes, improve state change order. That
+       element could need some more love.
+
 2008-01-04  Sebastian Dröge  <slomo@circular-chaos.org>
 
        * ext/taglib/gstapev2mux.h:
index 65c3df2338ed1190ef03e93608b495d245c60aba..78406d27e871994121966f9fc4daad28e5815e6b 100644 (file)
@@ -146,13 +146,24 @@ gst_directdraw_sink_set_window_id (GstXOverlay * overlay, ULONG window_id)
 {
   GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (overlay);
 
+  GST_OBJECT_LOCK (ddrawsink);
   /* check if we are already using this window id */
-  if (ddrawsink->video_window == (HWND) window_id)
+  if (ddrawsink->video_window == (HWND) window_id) {
+    GST_OBJECT_UNLOCK (ddrawsink);
     return;
+  }
 
   if (window_id) {
     HRESULT hres;
 
+    /* If we had an internal window, close it first */
+    if (ddrawsink->video_window && ddrawsink->our_video_window) {
+      /* Trick to let the event thread know that it has to die silently */
+      ddrawsink->our_video_window = FALSE;
+      /* Post quit message and wait for our event window thread */
+      PostMessage (ddrawsink->video_window, WM_QUIT, 0, 0);
+    }
+
     ddrawsink->video_window = (HWND) window_id;
     ddrawsink->our_video_window = FALSE;
     if (ddrawsink->setup) {
@@ -161,6 +172,9 @@ gst_directdraw_sink_set_window_id (GstXOverlay * overlay, ULONG window_id)
           ddrawsink->video_window);
     }
   }
+  /* FIXME: Handle the case where window_id is 0 and we want the sink to 
+   * create a new window when playback was already started (after set_caps) */
+  GST_OBJECT_UNLOCK (ddrawsink);
 }
 
 static void
@@ -476,6 +490,11 @@ gst_directdraw_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
     gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (ddrawsink));
   }
 
+  /* If we still don't have a window at that stage we create our own */
+  if (!ddrawsink->video_window) {
+    gst_directdraw_sink_create_default_window (ddrawsink);
+  }
+
   /* if we are rendering to our own window, resize it to video size */
   if (ddrawsink->video_window && ddrawsink->our_video_window) {
     SetWindowPos (ddrawsink->video_window, NULL,
@@ -499,26 +518,28 @@ static GstStateChangeReturn
 gst_directdraw_sink_change_state (GstElement * element,
     GstStateChange transition)
 {
-  GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (element);;
+  GstDirectDrawSink *ddrawsink = GST_DIRECTDRAW_SINK (element);
+  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
 
   switch (transition) {
     case GST_STATE_CHANGE_NULL_TO_READY:
-      if (ddrawsink->video_window == NULL)
-        if (!gst_directdraw_sink_create_default_window (ddrawsink))
-          return GST_STATE_CHANGE_FAILURE;
-
-      if (!gst_directdraw_sink_setup_ddraw (ddrawsink))
-        return GST_STATE_CHANGE_FAILURE;
+      if (!gst_directdraw_sink_setup_ddraw (ddrawsink)) {
+        ret = GST_STATE_CHANGE_FAILURE;
+        goto beach;
+      }
 
-      if (!(ddrawsink->caps = gst_directdraw_sink_get_ddrawcaps (ddrawsink)))
-        return GST_STATE_CHANGE_FAILURE;
-      break;
-    case GST_STATE_CHANGE_READY_TO_PAUSED:
-      break;
-    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+      if (!(ddrawsink->caps = gst_directdraw_sink_get_ddrawcaps (ddrawsink))) {
+        ret = GST_STATE_CHANGE_FAILURE;
+        goto beach;
+      }
       break;
-    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+    default:
       break;
+  }
+
+  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+  switch (transition) {
     case GST_STATE_CHANGE_PAUSED_TO_READY:
       ddrawsink->fps_n = 0;
       ddrawsink->fps_d = 1;
@@ -531,9 +552,12 @@ gst_directdraw_sink_change_state (GstElement * element,
       if (ddrawsink->setup)
         gst_directdraw_sink_cleanup (ddrawsink);
       break;
+    default:
+      break;
   }
 
-  return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+beach:
+  return ret;
 }
 
 static GstFlowReturn
@@ -547,7 +571,7 @@ gst_directdraw_sink_buffer_alloc (GstBaseSink * bsink, guint64 offset,
   gboolean buffercaps_unref = FALSE;
 
   GST_CAT_INFO_OBJECT (directdrawsink_debug, ddrawsink,
-      "a buffer of %d bytes was requested", size);
+      "a buffer of %u bytes was requested", size);
 
   g_mutex_lock (ddrawsink->pool_lock);
 
@@ -687,6 +711,63 @@ gst_directdraw_sink_buffer_alloc (GstBaseSink * bsink, guint64 offset,
   return ret;
 }
 
+static void
+gst_directdraw_sink_draw_borders (GstDirectDrawSink * ddrawsink, RECT dst_rect)
+{
+  RECT win_rect, fill_rect;
+  POINT win_point;
+  HDC hdc;
+
+  g_return_if_fail (GST_IS_DIRECTDRAW_SINK (ddrawsink));
+
+  /* Get the target window rect */
+  win_point.x = 0;
+  win_point.y = 0;
+  ClientToScreen (ddrawsink->video_window, &win_point);
+  GetClientRect (ddrawsink->video_window, &win_rect);
+  OffsetRect (&win_rect, win_point.x, win_point.y);
+
+  /* We acquire a drawing context */
+  if (IDirectDrawSurface7_GetDC (ddrawsink->primary_surface, &hdc) == DD_OK) {
+    HBRUSH brush = CreateSolidBrush (RGB (0, 0, 0));
+
+    /* Left border */
+    if (dst_rect.left > win_rect.left) {
+      fill_rect.left = win_rect.left;
+      fill_rect.top = win_rect.top;
+      fill_rect.bottom = win_rect.bottom;
+      fill_rect.right = dst_rect.left;
+      FillRect (hdc, &fill_rect, brush);
+    }
+    /* Right border */
+    if (dst_rect.right < win_rect.right) {
+      fill_rect.top = win_rect.top;
+      fill_rect.left = dst_rect.right;
+      fill_rect.bottom = win_rect.bottom;
+      fill_rect.right = win_rect.right;
+      FillRect (hdc, &fill_rect, brush);
+    }
+    /* Top border */
+    if (dst_rect.top > win_rect.top) {
+      fill_rect.top = win_rect.top;
+      fill_rect.left = win_rect.left;
+      fill_rect.right = win_rect.right;
+      fill_rect.bottom = dst_rect.top;
+      FillRect (hdc, &fill_rect, brush);
+    }
+    /* Bottom border */
+    if (dst_rect.bottom < win_rect.bottom) {
+      fill_rect.top = dst_rect.bottom;
+      fill_rect.left = win_rect.left;
+      fill_rect.right = win_rect.right;
+      fill_rect.bottom = win_rect.bottom;
+      FillRect (hdc, &fill_rect, brush);
+    }
+    DeleteObject (brush);
+    IDirectDrawSurface7_ReleaseDC (ddrawsink->primary_surface, hdc);
+  }
+}
+
 static GstFlowReturn
 gst_directdraw_sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
 {
@@ -711,6 +792,15 @@ gst_directdraw_sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
     return GST_FLOW_ERROR;
 
   /* get the video window position */
+  GST_OBJECT_LOCK (ddrawsink);
+  if (G_UNLIKELY (!ddrawsink->video_window)) {
+    GST_OBJECT_UNLOCK (ddrawsink);
+    GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
+        "gst_directdraw_sink_show_frame our video window disappeared");
+    GST_ELEMENT_ERROR (ddrawsink, RESOURCE, NOT_FOUND,
+        ("Output window was closed"), (NULL));
+    return GST_FLOW_ERROR;
+  }
   dest_surf_point.x = 0;
   dest_surf_point.y = 0;
   ClientToScreen (ddrawsink->video_window, &dest_surf_point);
@@ -725,7 +815,9 @@ gst_directdraw_sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
     src_rect.right = ddrawsink->video_width;
     gst_directdraw_sink_center_rect (ddrawsink, src_rect, destsurf_rect,
         &destsurf_rect);
+    gst_directdraw_sink_draw_borders (ddrawsink, destsurf_rect);
   }
+  GST_OBJECT_UNLOCK (ddrawsink);
 
   if (ddrawsink->must_recreate_offscreen && ddrawsink->offscreen_surface) {
     IDirectDrawSurface7_Release (ddrawsink->offscreen_surface);
@@ -780,17 +872,18 @@ gst_directdraw_sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
       return GST_FLOW_ERROR;
     }
 
-    /* blit to primary surface ( Blt will scale the video the dest rect surface if needed */
+    /* blit to primary surface ( Blt will scale the video the dest rect surface
+     * if needed */
     hRes = IDirectDrawSurface7_Blt (ddrawsink->primary_surface, &destsurf_rect,
         ddrawsink->offscreen_surface, NULL, DDBLT_WAIT, NULL);
-    if (hRes != DD_OK)
+    if (hRes != DD_OK)          /* FIXME: Is it really safe to continue past here ? */
       GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
           "IDirectDrawSurface7_Blt (object's offscreen surface) " "returned %s",
           DDErrorString (hRes));
 
   } else {
-    /* We are receiving a directdraw surface (previously returned by our buffer pool
-       So we will simply blit it on the primary surface */
+    /* We are receiving a directdraw surface (previously returned by our buffer
+     * pool so we will simply blit it on the primary surface */
     GstDDrawSurface *surface = NULL;
 
     surface = GST_DDRAWSURFACE (buf);
@@ -802,7 +895,7 @@ gst_directdraw_sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
     /* blit to our primary surface */
     hRes = IDirectDrawSurface7_Blt (ddrawsink->primary_surface, &destsurf_rect,
         surface->surface, NULL, DDBLT_WAIT, NULL);
-    if (hRes != DD_OK)
+    if (hRes != DD_OK)          /* FIXME: Is it really safe to continue past here ? */
       GST_CAT_WARNING_OBJECT (directdrawsink_debug, ddrawsink,
           "IDirectDrawSurface7_Blt (offscreen surface from buffer_alloc) "
           "returned %s", DDErrorString (hRes));
@@ -1158,8 +1251,8 @@ gst_directdraw_sink_setup_ddraw (GstDirectDrawSink * ddrawsink)
   gboolean bRet = TRUE;
   HRESULT hRes;
 
-  /* create an instance of the ddraw object use DDCREATE_EMULATIONONLY as first parameter to
-     force Directdraw to use the hardware emulation layer */
+  /* create an instance of the ddraw object use DDCREATE_EMULATIONONLY as first
+   * parameter to force Directdraw to use the hardware emulation layer */
   hRes = DirectDrawCreateEx ( /*DDCREATE_EMULATIONONLY */ 0,
       (void **) &ddrawsink->ddraw_object, &IID_IDirectDraw7, NULL);
   if (hRes != DD_OK || ddrawsink->ddraw_object == NULL) {
@@ -1171,7 +1264,7 @@ gst_directdraw_sink_setup_ddraw (GstDirectDrawSink * ddrawsink)
 
   /* set cooperative level */
   hRes = IDirectDraw7_SetCooperativeLevel (ddrawsink->ddraw_object,
-      ddrawsink->video_window, DDSCL_NORMAL);
+      NULL, DDSCL_NORMAL);
   if (hRes != DD_OK) {
     GST_ELEMENT_ERROR (ddrawsink, RESOURCE, WRITE,
         ("Failed to set the set the cooperative level error=%s",
@@ -1182,10 +1275,6 @@ gst_directdraw_sink_setup_ddraw (GstDirectDrawSink * ddrawsink)
   /* 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);
-  }
 
   /* create our primary surface */
   if (!gst_directdraw_sink_check_primary_surface (ddrawsink))
@@ -1236,6 +1325,9 @@ gst_directdraw_sink_window_thread (GstDirectDrawSink * ddrawsink)
   if (ddrawsink->video_window == NULL)
     return FALSE;
 
+  /* Set the clipper on that window */
+  IDirectDrawClipper_SetHWnd (ddrawsink->clipper, 0, ddrawsink->video_window);
+
   /* signal application we create a window */
   gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (ddrawsink),
       (gulong) ddrawsink->video_window);
@@ -1246,8 +1338,18 @@ gst_directdraw_sink_window_thread (GstDirectDrawSink * ddrawsink)
   while (1) {
     MSG msg;
 
-    if (!GetMessage (&msg, ddrawsink->video_window, 0, 0))
+    if (GetMessage (&msg, ddrawsink->video_window, 0, 0) <= 0) {
+      GST_CAT_LOG_OBJECT (directdrawsink_debug, ddrawsink,
+          "our window received WM_QUIT or error.");
+      /* The window could have changed, if it is not ours anymore we don't 
+       * overwrite the current video window with NULL */
+      if (ddrawsink->our_video_window) {
+        GST_OBJECT_LOCK (ddrawsink);
+        ddrawsink->video_window = NULL;
+        GST_OBJECT_UNLOCK (ddrawsink);
+      }
       break;
+    }
     DispatchMessage (&msg);
   }
 
@@ -1511,7 +1613,8 @@ gst_directdraw_sink_get_ddrawcaps (GstDirectDrawSink * ddrawsink)
   IDirectDraw7_GetCaps (ddrawsink->ddraw_object, &ddcaps_hardware,
       &ddcaps_emulation);
 
-  /*  we don't test for DDCAPS_BLTSTRETCH on the hardware as the directdraw emulation layer can do it */
+  /* 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;
     DDSURFACEDESC2 *sd;
@@ -1574,7 +1677,8 @@ gst_directdraw_sink_get_ddrawcaps (GstDirectDrawSink * ddrawsink)
     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;
 }
@@ -1619,12 +1723,13 @@ gst_directdraw_sink_surface_create (GstDirectDrawSink * ddrawsink,
         "failed getting pixel format from caps %" GST_PTR_FORMAT, caps);
   }
 
-  /* 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.
+  /* 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) {*/