• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) 2021 Seungha Yang <seungha@centricular.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include <gst/gst.h>
25 #include <windows.h>
26 
27 static GstDevice *
enum_devices(gboolean only_show,gint monitor_idx,guint64 monitor_handle)28 enum_devices (gboolean only_show, gint monitor_idx, guint64 monitor_handle)
29 {
30   GstDeviceMonitor *monitor;
31   GstCaps *caps;
32   GList *devices, *iter;
33   gboolean ret;
34   guint i;
35   GstDevice *target = nullptr;
36 
37   monitor = gst_device_monitor_new ();
38 
39   /* Filtering by using d3d11 memory caps with "Source/Monitor" class */
40   caps = gst_caps_from_string ("video/x-raw(memory:D3D11Memory)");
41   ret = gst_device_monitor_add_filter (monitor, "Source/Monitor", caps);
42   gst_caps_unref (caps);
43 
44   if (!ret) {
45     gst_object_unref (monitor);
46     g_warning ("Failed to setup device monitor");
47     return nullptr;
48   }
49 
50   gst_device_monitor_start (monitor);
51   devices = gst_device_monitor_get_devices (monitor);
52 
53   if (!devices) {
54     g_warning ("No detected d3d11 monitor device");
55     gst_device_monitor_stop (monitor);
56     gst_object_unref (monitor);
57     return nullptr;
58   }
59 
60   gst_println ("Found %d monitor device(s)", g_list_length (devices));
61 
62   for (iter = devices, i = 0; iter; iter = g_list_next (iter), i++) {
63     GstDevice *dev = GST_DEVICE (iter->data);
64     GstStructure *s;
65     guint disp_coord_left, disp_coord_top, disp_coord_right, disp_coord_bottom;
66     gchar *name = nullptr;
67     gchar *adapter_desc = nullptr;
68     guint64 hmonitor;
69     HMONITOR handle;
70     gboolean primary;
71 
72     s = gst_device_get_properties (dev);
73 
74     gst_structure_get (s,
75         "display.coordinates.left", G_TYPE_INT, &disp_coord_left,
76         "display.coordinates.top", G_TYPE_INT, &disp_coord_top,
77         "display.coordinates.right", G_TYPE_INT, &disp_coord_right,
78         "display.coordinates.bottom", G_TYPE_INT, &disp_coord_bottom,
79         "device.adapter.description", G_TYPE_STRING, &adapter_desc,
80         "device.hmonitor", G_TYPE_UINT64, &hmonitor,
81         "device.primary", G_TYPE_BOOLEAN, &primary, nullptr);
82 
83     name = gst_device_get_display_name (dev);
84 
85     handle = (HMONITOR) hmonitor;
86 
87     gst_println ("Monitor %d (%s - %s):", i, name, adapter_desc);
88     gst_println ("  HMONITOR: %p (%" G_GUINT64_FORMAT ")", handle, hmonitor);
89     gst_println ("  Display Coordinates (left:top:right:bottom): %d:%d:%d:%d\n",
90         disp_coord_left, disp_coord_top, disp_coord_right, disp_coord_bottom);
91 
92     g_free (adapter_desc);
93     g_free (name);
94 
95     if (!only_show && !target) {
96       if (monitor_handle != 0) {
97         if (monitor_handle == hmonitor) {
98           target = (GstDevice *) gst_object_ref (dev);
99         }
100       } else if (monitor_idx < 0) {
101         if (primary) {
102           target = (GstDevice *) gst_object_ref (dev);
103         }
104       } else if (monitor_idx == i) {
105         target = (GstDevice *) gst_object_ref (dev);
106       }
107 
108       if (target)
109         gst_println ("Found target monitor device");
110     }
111   }
112   g_list_free_full (devices, gst_object_unref);
113 
114   return target;
115 }
116 
117 static gboolean
bus_msg(GstBus * bus,GstMessage * msg,GMainLoop * loop)118 bus_msg (GstBus * bus, GstMessage * msg, GMainLoop * loop)
119 {
120   switch (GST_MESSAGE_TYPE (msg)) {
121     case GST_MESSAGE_ERROR:{
122       GError *err;
123       gchar *dbg;
124 
125       gst_message_parse_error (msg, &err, &dbg);
126       g_printerr ("ERROR %s \n", err->message);
127       if (dbg != NULL)
128         g_printerr ("ERROR debug information: %s\n", dbg);
129       g_clear_error (&err);
130       g_free (dbg);
131 
132       g_main_loop_quit (loop);
133       break;
134     }
135     default:
136       break;
137   }
138 
139   return TRUE;
140 }
141 
142 gint
main(gint argc,gchar ** argv)143 main (gint argc, gchar ** argv)
144 {
145   GstElement *pipeline, *src, *queue, *sink;
146   GstElement *pipeline_1 = nullptr, *src_1, *queue_1, *sink_1;
147   GMainLoop *loop;
148   gboolean ret;
149   gboolean show_devices = FALSE;
150   gboolean multi_pipelines = FALSE;
151   gboolean show_cursor = FALSE;
152   gint64 hmonitor = 0;
153   gint monitor_index = -1;
154   GError *err = nullptr;
155   GstDevice *device;
156   GOptionContext *option_ctx;
157   GOptionEntry options[] = {
158     {"show-devices", 0, 0, G_OPTION_ARG_NONE, &show_devices,
159         "Display available monitor devices", nullptr},
160     {"hmonitor", 0, 0, G_OPTION_ARG_INT64, &hmonitor,
161         "Address of HMONITOR handle", nullptr},
162     {"index", 0, 0, G_OPTION_ARG_INT, &monitor_index,
163         "Monitor index to capture (-1 for primary monitor)", nullptr},
164     {"multi-pipelines", 0, 0, G_OPTION_ARG_NONE, &multi_pipelines,
165         "Run two separate pipelines for capturing a single monitor", nullptr},
166     {"show-cursor", 0, 0, G_OPTION_ARG_NONE, &show_cursor,
167         "Draw mouse cursor", nullptr},
168     {nullptr}
169   };
170 
171   option_ctx = g_option_context_new ("D3D11 screen capture example");
172   g_option_context_add_main_entries (option_ctx, options, NULL);
173   g_option_context_add_group (option_ctx, gst_init_get_option_group ());
174   ret = g_option_context_parse (option_ctx, &argc, &argv, &err);
175   g_option_context_free (option_ctx);
176 
177   if (!ret) {
178     g_printerr ("option parsing failed: %s\n", err->message);
179     g_clear_error (&err);
180     return 1;
181   }
182 
183   device = enum_devices (show_devices, monitor_index, (guint64) hmonitor);
184   if (show_devices) {
185     gst_clear_object (&device);
186     return 0;
187   }
188 
189   if (!device) {
190     gst_println ("Failed to find monitor device");
191     return 1;
192   }
193 
194   src = gst_device_create_element (device, nullptr);
195   if (!src) {
196     g_warning ("Failed to create d3d11screencapture element");
197     return 1;
198   }
199 
200   g_object_set (src, "show-cursor", show_cursor, nullptr);
201 
202   if (multi_pipelines) {
203     src_1 = gst_device_create_element (device, nullptr);
204     if (!src_1) {
205       g_warning ("Failed to create second d3d11screencapture element");
206       return 1;
207     }
208 
209     g_object_set (src_1, "show-cursor", show_cursor, nullptr);
210   }
211 
212   gst_object_unref (device);
213 
214   loop = g_main_loop_new (nullptr, FALSE);
215   pipeline = gst_pipeline_new (nullptr);
216 
217   queue = gst_element_factory_make ("queue", nullptr);
218   sink = gst_element_factory_make ("d3d11videosink", nullptr);
219 
220   gst_bin_add_many (GST_BIN (pipeline), src, queue, sink, nullptr);
221   gst_element_link_many (src, queue, sink, nullptr);
222 
223   gst_bus_add_watch (GST_ELEMENT_BUS (pipeline), (GstBusFunc) bus_msg, loop);
224   gst_element_set_state (pipeline, GST_STATE_PLAYING);
225 
226   if (multi_pipelines) {
227     pipeline_1 = gst_pipeline_new (nullptr);
228 
229     queue_1 = gst_element_factory_make ("queue", nullptr);
230     sink_1 = gst_element_factory_make ("d3d11videosink", nullptr);
231 
232     gst_bin_add_many (GST_BIN (pipeline_1), src_1, queue_1, sink_1, nullptr);
233     gst_element_link_many (src_1, queue_1, sink_1, nullptr);
234 
235     gst_bus_add_watch (GST_ELEMENT_BUS (pipeline_1), (GstBusFunc) bus_msg, loop);
236     gst_element_set_state (pipeline_1, GST_STATE_PLAYING);
237   }
238 
239   g_main_loop_run (loop);
240 
241   gst_element_set_state (pipeline, GST_STATE_NULL);
242   gst_bus_remove_watch (GST_ELEMENT_BUS (pipeline));
243   gst_object_unref (pipeline);
244 
245   if (multi_pipelines) {
246     gst_element_set_state (pipeline_1, GST_STATE_NULL);
247     gst_bus_remove_watch (GST_ELEMENT_BUS (pipeline));
248     gst_object_unref (pipeline_1);
249   }
250 
251   g_main_loop_unref (loop);
252 
253   return 0;
254 }
255