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