1 /* GStreamer
2 * Copyright (C) 2019 Seungha Yang <seungha.yang@navercorp.com>
3 * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 /**
22 * SECTION:plugin-mediafoundation
23 *
24 * Microsoft MediaFoundation plugin.
25 *
26 * This plugin consists of various hardware/software video encoders
27 * software audio encoders, and video capture (from webcam) elements.
28 *
29 * GstMediaFoundation plugin supports H.264/AVC, H.265/HEVC, VP9, codecs for
30 * hardware-accelerate encoding.
31 *
32 * However, depending on the hardware it runs on, some elements might not be
33 * registered in case that underlying hardware doesn't support the feature.
34 *
35 * Moreover, depending on hardware vendor's MediaFoundation implementation,
36 * secendary GPU may not be usable. In that case, user could use vendor
37 * specific plugins, Intel Media SDK and NVCODEC plugins for example.
38 *
39 * For a system with multiple MediaFoundation compatible hardwares (i.e., GPU),
40 * there can be multiple plugin features having the same role.
41 * Also, there would be additional software video encoder element the system
42 * meets requirement.
43 *
44 * The naming rule for the non-primary encoder is `mf{codec}device{index}enc`
45 * where `index` is an arbitrary index number of hardware starting from 1.
46 *
47 * To get a list of all available elements, user can run
48 * ```sh
49 * gst-inspect-1.0.exe mediafoundation
50 * ```
51 *
52 * Since: 1.18
53 *
54 */
55
56 #ifdef HAVE_CONFIG_H
57 #include "config.h"
58 #endif
59
60 #include "gstmfconfig.h"
61
62 #include <winapifamily.h>
63
64 #include <gst/gst.h>
65 #include <gst/video/video.h>
66 #include "gstmfvideosrc.h"
67 #include "gstmfdevice.h"
68 #include "gstmfutils.h"
69 #include "gstmfh264enc.h"
70 #include "gstmfh265enc.h"
71 #include "gstmfvp9enc.h"
72 #include "gstmfaacenc.h"
73 #include "gstmfmp3enc.h"
74
75 #if GST_MF_HAVE_D3D11
76 #include <gst/d3d11/gstd3d11.h>
77 #include <gstmfplatloader.h>
78 #endif
79
80 GST_DEBUG_CATEGORY (gst_mf_debug);
81 GST_DEBUG_CATEGORY (gst_mf_utils_debug);
82 GST_DEBUG_CATEGORY (gst_mf_source_object_debug);
83 GST_DEBUG_CATEGORY (gst_mf_transform_debug);
84 GST_DEBUG_CATEGORY (gst_mf_video_buffer_debug);
85 GST_DEBUG_CATEGORY (gst_mf_video_enc_debug);
86
87 #define GST_CAT_DEFAULT gst_mf_debug
88
89 static void
plugin_deinit(gpointer data)90 plugin_deinit (gpointer data)
91 {
92 MFShutdown ();
93 }
94
95 #if GST_MF_HAVE_D3D11
96 static GList *
get_d3d11_devices(void)97 get_d3d11_devices (void)
98 {
99 GList *ret = NULL;
100 guint i;
101 HRESULT hr;
102 IMFVideoSampleAllocatorEx *allocator = NULL;
103
104 /* Check whether we can use IMFVideoSampleAllocatorEx interface */
105 hr = GstMFCreateVideoSampleAllocatorEx (&IID_IMFVideoSampleAllocatorEx,
106 &allocator);
107 if (!gst_mf_result (hr)) {
108 GST_DEBUG ("IMFVideoSampleAllocatorEx interface is unavailable");
109 return NULL;
110 } else {
111 IMFVideoSampleAllocatorEx_Release (allocator);
112 }
113
114 /* AMD seems supporting up to 12 cards, and 8 for NVIDIA */
115 for (i = 0; i < 12; i++) {
116 GstD3D11Device *device;
117 gboolean is_hardware = FALSE;
118 const GstD3D11Format *d3d11_format;
119 ID3D11Device *device_handle;
120 D3D11_FEATURE_DATA_D3D11_OPTIONS4 options = { 0, };
121 UINT supported = 0;
122
123 device = gst_d3d11_device_new (i, D3D11_CREATE_DEVICE_VIDEO_SUPPORT);
124
125 if (!device)
126 break;
127
128 g_object_get (device, "hardware", &is_hardware, NULL);
129
130 if (!is_hardware) {
131 GST_DEBUG_OBJECT (device, "Given d3d11 device is not for hardware");
132 gst_object_unref (device);
133 continue;
134 }
135
136 /* device can support NV12 format? */
137 d3d11_format =
138 gst_d3d11_device_format_from_gst (device, GST_VIDEO_FORMAT_NV12);
139 if (!d3d11_format || d3d11_format->dxgi_format != DXGI_FORMAT_NV12) {
140 GST_DEBUG_OBJECT (device,
141 "Given d3d11 device cannot support NV12 format");
142 gst_object_unref (device);
143 continue;
144 }
145
146 /* device can support ExtendedNV12SharedTextureSupported?
147 *
148 * NOTE: we will make use of per encoder object d3d11 device without
149 * sharing it in a pipeline because MF needs D3D11_CREATE_DEVICE_VIDEO_SUPPORT
150 * but the flag doesn't used for the other our use cases.
151 * So we need texture sharing feature so that we can copy d3d11 texture into
152 * MF specific texture pool without download texture */
153
154 device_handle = gst_d3d11_device_get_device_handle (device);
155 hr = ID3D11Device_CheckFeatureSupport (device_handle,
156 D3D11_FEATURE_D3D11_OPTIONS4, &options, sizeof (options));
157 if (!gst_d3d11_result (hr, device) ||
158 !options.ExtendedNV12SharedTextureSupported) {
159 GST_DEBUG_OBJECT (device,
160 "Given d3d11 device cannot support NV12 format for shared texture");
161 gst_object_unref (device);
162 continue;
163 }
164
165 /* can we bind NV12 texture for encoder? */
166 hr = ID3D11Device_CheckFormatSupport (device_handle,
167 DXGI_FORMAT_NV12, &supported);
168
169 if (!gst_d3d11_result (hr, device)) {
170 GST_DEBUG_OBJECT (device, "Couldn't query format support");
171 gst_object_unref (device);
172 continue;
173 } else if ((supported & D3D11_FORMAT_SUPPORT_VIDEO_ENCODER) == 0) {
174 GST_DEBUG_OBJECT (device, "We cannot bind NV12 format for encoding");
175 gst_object_unref (device);
176 continue;
177 }
178
179 ret = g_list_append (ret, device);
180 }
181
182 return ret;
183 }
184 #endif
185
186 static gboolean
plugin_init(GstPlugin * plugin)187 plugin_init (GstPlugin * plugin)
188 {
189 HRESULT hr;
190 GstRank rank = GST_RANK_SECONDARY;
191 GList *device_list = NULL;
192
193 GST_DEBUG_CATEGORY_INIT (gst_mf_debug, "mf", 0, "media foundation");
194 GST_DEBUG_CATEGORY_INIT (gst_mf_utils_debug,
195 "mfutils", 0, "media foundation utility functions");
196 GST_DEBUG_CATEGORY_INIT (gst_mf_source_object_debug,
197 "mfsourceobject", 0, "mfsourceobject");
198 GST_DEBUG_CATEGORY_INIT (gst_mf_transform_debug,
199 "mftransform", 0, "mftransform");
200 GST_DEBUG_CATEGORY_INIT (gst_mf_video_buffer_debug,
201 "mfvideobuffer", 0, "mfvideobuffer");
202 GST_DEBUG_CATEGORY_INIT (gst_mf_video_enc_debug,
203 "mfvideoenc", 0, "mfvideoenc");
204
205 hr = MFStartup (MF_VERSION, MFSTARTUP_NOSOCKET);
206 if (!gst_mf_result (hr)) {
207 GST_WARNING ("MFStartup failure, hr: 0x%x", hr);
208 return TRUE;
209 }
210
211 /* mfvideosrc should be primary rank for UWP */
212 #if (GST_MF_WINAPI_APP && !GST_MF_WINAPI_DESKTOP)
213 rank = GST_RANK_PRIMARY + 1;
214 #endif
215
216 /* FIXME: In order to create MFT for a specific GPU, MFTEnum2() API is
217 * required API but it's desktop only.
218 * So, resulting MFT and D3D11 might not be compatible in case of multi-GPU
219 * environment on UWP. */
220 #if GST_MF_HAVE_D3D11
221 if (gst_mf_plat_load_library ())
222 device_list = get_d3d11_devices ();
223 #endif
224
225 gst_element_register (plugin, "mfvideosrc", rank, GST_TYPE_MF_VIDEO_SRC);
226 gst_device_provider_register (plugin, "mfdeviceprovider",
227 rank, GST_TYPE_MF_DEVICE_PROVIDER);
228
229 gst_mf_h264_enc_plugin_init (plugin, GST_RANK_SECONDARY, device_list);
230 gst_mf_h265_enc_plugin_init (plugin, GST_RANK_SECONDARY, device_list);
231 gst_mf_vp9_enc_plugin_init (plugin, GST_RANK_SECONDARY, device_list);
232
233 if (device_list)
234 g_list_free_full (device_list, gst_object_unref);
235
236 gst_mf_aac_enc_plugin_init (plugin, GST_RANK_SECONDARY);
237 gst_mf_mp3_enc_plugin_init (plugin, GST_RANK_SECONDARY);
238
239 /* So that call MFShutdown() when this plugin is no more used
240 * (i.e., gst_deinit). Otherwise valgrind-like tools would complain
241 * about un-released media foundation resources.
242 *
243 * NOTE: MFStartup and MFShutdown can be called multiple times, but the number
244 * of each MFStartup and MFShutdown call should be identical. This rule is
245 * simliar to that of CoInitialize/CoUninitialize pair */
246 g_object_set_data_full (G_OBJECT (plugin),
247 "plugin-mediafoundation-shutdown", "shutdown-data",
248 (GDestroyNotify) plugin_deinit);
249
250 return TRUE;
251 }
252
253 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
254 GST_VERSION_MINOR,
255 mediafoundation,
256 "Microsoft Media Foundation plugin",
257 plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
258