diff mbox series

[FFmpeg-devel,2/2] libavdevice/gdigrab: create a separate thread for handling window messages

Message ID tencent_DA0B9E6F50FC8DA7B4BBF9426A70BADE9F08@qq.com
State New
Headers show
Series [FFmpeg-devel,1/2] libavdevice/gdigrab: make region window click-throughable
Related show

Checks

Context Check Description
andriy/x86_make success Make finished
andriy/x86_make_fate success Make fate finished
andriy/PPC64_make success Make finished
andriy/PPC64_make_fate success Make fate finished

Commit Message

1160386205@qq.com March 31, 2021, 11:49 a.m. UTC
From: He Yang <1160386205@qq.com>

Windows message processing should start as soon as the window is created.
otherwise, Windows may think ffmpeg is not responding.
for example the window may got stucked (by clicking at it) in the previous
code while ffmpeg is querying the user whether to overwrite the existing
file or not.

I move the window creation and message handling process to a separate
thread to solve this probelm.

Signed-off-by: He Yang <1160386205@qq.com>
---
 libavdevice/gdigrab.c | 105 +++++++++++++++++++++++++++++-------------
 1 file changed, 73 insertions(+), 32 deletions(-)
diff mbox series

Patch

diff --git a/libavdevice/gdigrab.c b/libavdevice/gdigrab.c
index e9d646999f..3abd20d899 100644
--- a/libavdevice/gdigrab.c
+++ b/libavdevice/gdigrab.c
@@ -33,7 +33,9 @@ 
 #include "libavutil/opt.h"
 #include "libavutil/time.h"
 #include <windows.h>
+#include <process.h>
 
+#define WM_REGION_WND_DESTROY (WM_USER + 1)
 /**
  * GDI Device Demuxer context
  */
@@ -61,7 +63,9 @@  struct gdigrab {
     void      *buffer;      /**< The buffer containing the bitmap image data */
     RECT       clip_rect;   /**< The subarea of the screen or window to clip */
 
-    HWND       region_hwnd; /**< Handle of the region border window */
+    HANDLE     hthread;            /**< Thread handle of the region border window */
+    HWND       region_hwnd;        /**< Handle of the region border window */
+    HANDLE     event_wnd_creation; /**< Event handle to notify CreateWindow has been called */
 
     int cursor_error_printed;
 };
@@ -104,23 +108,30 @@  gdigrab_region_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
 
         EndPaint(hwnd, &ps);
         break;
+    case WM_REGION_WND_DESTROY:
+        DestroyWindow(hwnd);
+        break;
+    case WM_DESTROY:
+        PostQuitMessage(0);
+        break;
     default:
         return DefWindowProc(hwnd, msg, wparam, lparam);
     }
-    return 0;
+    return DefWindowProc(hwnd, msg, wparam, lparam);
 }
 
 /**
- * Initialize the region outline window.
+ * Owner thread of the region outline window.
  *
- * @param s1 The format context.
- * @param gdigrab gdigrab context.
- * @return 0 success, !0 failure
+ * @param arg gdigrab context.
+ * @return Thread exit code.
  */
-static int
-gdigrab_region_wnd_init(AVFormatContext *s1, struct gdigrab *gdigrab)
+static unsigned __stdcall
+gdigrab_region_wnd_thread(void *arg)
 {
     HWND hwnd;
+    MSG msg;
+    struct gdigrab *gdigrab = (struct gdigrab *)arg;
     RECT rect = gdigrab->clip_rect;
     HRGN region = NULL;
     HRGN region_interior = NULL;
@@ -139,9 +150,9 @@  gdigrab_region_wnd_init(AVFormatContext *s1, struct gdigrab *gdigrab)
             rect.right - rect.left, rect.bottom - rect.top,
             NULL, NULL, NULL, NULL);
     if (!hwnd) {
-        WIN32_API_ERROR("Could not create region display window");
         goto error;
     }
+    gdigrab->region_hwnd = hwnd;
 
     // Set the window transparency to 255 (opaque)
     SetLayeredWindowAttributes(hwnd, 0, 255, LWA_ALPHA);
@@ -155,7 +166,6 @@  gdigrab_region_wnd_init(AVFormatContext *s1, struct gdigrab *gdigrab)
             rect.bottom - rect.top - REGION_WND_BORDER);
     CombineRgn(region, region, region_interior, RGN_DIFF);
     if (!SetWindowRgn(hwnd, region, FALSE)) {
-        WIN32_API_ERROR("Could not set window region");
         goto error;
     }
     // The "region" memory is now owned by the window
@@ -166,7 +176,12 @@  gdigrab_region_wnd_init(AVFormatContext *s1, struct gdigrab *gdigrab)
 
     ShowWindow(hwnd, SW_SHOW);
 
-    gdigrab->region_hwnd = hwnd;
+    SetEvent(gdigrab->event_wnd_creation);
+
+    // Message loop
+    while (GetMessage(&msg, 0, 0, 0)) {
+        DispatchMessage(&msg);
+    }
 
     return 0;
 
@@ -177,41 +192,71 @@  error:
         DeleteObject(region_interior);
     if (hwnd)
         DestroyWindow(hwnd);
+
+    gdigrab->region_hwnd = NULL;
+    SetEvent(gdigrab->event_wnd_creation);
     return 1;
 }
 
 /**
- * Cleanup/free the region outline window.
+ * Initialize the region outline window.
  *
  * @param s1 The format context.
  * @param gdigrab gdigrab context.
+ * @return 0 success, !0 failure
  */
-static void
-gdigrab_region_wnd_destroy(AVFormatContext *s1, struct gdigrab *gdigrab)
+static int
+gdigrab_region_wnd_init(AVFormatContext *s1, struct gdigrab *gdigrab)
 {
-    if (gdigrab->region_hwnd)
-        DestroyWindow(gdigrab->region_hwnd);
-    gdigrab->region_hwnd = NULL;
+    HANDLE hthread;
+    HANDLE hevent;
+
+    hevent = CreateEvent(NULL, FALSE, FALSE, NULL);
+    if (!hevent) {
+        WIN32_API_ERROR("Could not create event");
+        return 1;
+    }
+    gdigrab->event_wnd_creation = hevent;
+
+    hthread = (HANDLE)_beginthreadex(NULL, 0, gdigrab_region_wnd_thread, gdigrab, 0, NULL);
+    if (!hthread) {
+        WIN32_API_ERROR("Could not create thread for region display window");
+        CloseHandle(gdigrab->event_wnd_creation);
+        gdigrab->event_wnd_creation = hevent = NULL;
+        return 1;
+    }
+    gdigrab->hthread = hthread;
+
+    WaitForSingleObject(gdigrab->event_wnd_creation, INFINITE);
+    CloseHandle(gdigrab->event_wnd_creation);
+    gdigrab->event_wnd_creation = NULL;
+
+    if (!gdigrab->region_hwnd) {
+        WIN32_API_ERROR("Could not create window or set window region");
+        CloseHandle(gdigrab->hthread);
+        gdigrab->hthread = NULL;
+        return 1;
+    }
+    return 0;
 }
 
 /**
- * Process the Windows message queue.
- *
- * This is important to prevent Windows from thinking the window has become
- * unresponsive. As well, things like WM_PAINT (to actually draw the window
- * contents) are handled from the message queue context.
+ * Cleanup/free the region outline window.
  *
  * @param s1 The format context.
  * @param gdigrab gdigrab context.
  */
 static void
-gdigrab_region_wnd_update(AVFormatContext *s1, struct gdigrab *gdigrab)
+gdigrab_region_wnd_destroy(AVFormatContext *s1, struct gdigrab *gdigrab)
 {
-    HWND hwnd = gdigrab->region_hwnd;
-    MSG msg;
-
-    while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE)) {
-        DispatchMessage(&msg);
+    if (gdigrab->region_hwnd) {
+        SendMessage(gdigrab->region_hwnd, WM_REGION_WND_DESTROY, 0, 0);
+        gdigrab->region_hwnd = NULL;
+    }
+    if(gdigrab->hthread) {
+        WaitForSingleObject(gdigrab->hthread, INFINITE);
+        CloseHandle(gdigrab->hthread);
+        gdigrab->hthread = NULL;
     }
 }
 
@@ -548,10 +593,6 @@  static int gdigrab_read_packet(AVFormatContext *s1, AVPacket *pkt)
     /* Calculate the time of the next frame */
     time_frame += INT64_C(1000000);
 
-    /* Run Window message processing queue */
-    if (gdigrab->show_region)
-        gdigrab_region_wnd_update(s1, gdigrab);
-
     /* wait based on the frame rate */
     for (;;) {
         curtime = av_gettime_relative();