netapi32: Avoid process heap allocations.
[wine.git] / dlls / mfmediaengine / main.c
blob98504c5e2697a9ab100822005c7507f52a951c10
1 /*
2 * Copyright 2019 Jactry Zeng for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
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 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #define COBJMACROS
21 #include <math.h>
22 #include <stdarg.h>
24 #include "windef.h"
25 #include "winbase.h"
27 #include "mfapi.h"
28 #include "mfmediaengine.h"
29 #include "mferror.h"
30 #include "dxgi.h"
31 #include "d3d11.h"
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
37 static BOOL mf_array_reserve(void **elements, size_t *capacity, size_t count, size_t size)
39 size_t new_capacity, max_capacity;
40 void *new_elements;
42 if (count <= *capacity)
43 return TRUE;
45 max_capacity = ~(SIZE_T)0 / size;
46 if (count > max_capacity)
47 return FALSE;
49 new_capacity = max(4, *capacity);
50 while (new_capacity < count && new_capacity <= max_capacity / 2)
51 new_capacity *= 2;
52 if (new_capacity < count)
53 new_capacity = max_capacity;
55 if (!(new_elements = realloc(*elements, new_capacity * size)))
56 return FALSE;
58 *elements = new_elements;
59 *capacity = new_capacity;
61 return TRUE;
64 enum media_engine_mode
66 MEDIA_ENGINE_INVALID,
67 MEDIA_ENGINE_AUDIO_MODE,
68 MEDIA_ENGINE_RENDERING_MODE,
69 MEDIA_ENGINE_FRAME_SERVER_MODE,
72 /* Used with create flags. */
73 enum media_engine_flags
75 /* MF_MEDIA_ENGINE_CREATEFLAGS_MASK is 0x1f. */
76 FLAGS_ENGINE_SHUT_DOWN = 0x20,
77 FLAGS_ENGINE_AUTO_PLAY = 0x40,
78 FLAGS_ENGINE_LOOP = 0x80,
79 FLAGS_ENGINE_PAUSED = 0x100,
80 FLAGS_ENGINE_WAITING = 0x200,
81 FLAGS_ENGINE_MUTED = 0x400,
82 FLAGS_ENGINE_HAS_AUDIO = 0x800,
83 FLAGS_ENGINE_HAS_VIDEO = 0x1000,
84 FLAGS_ENGINE_FIRST_FRAME = 0x2000,
85 FLAGS_ENGINE_IS_ENDED = 0x4000,
86 FLAGS_ENGINE_NEW_FRAME = 0x8000,
87 FLAGS_ENGINE_SOURCE_PENDING = 0x10000,
88 FLAGS_ENGINE_PLAY_PENDING = 0x20000,
91 struct vec3
93 float x, y, z;
96 struct color
98 float r, g, b, a;
101 static const struct vec3 fullquad[] =
103 {-1.0f, -1.0f, 0.0f},
104 {-1.0f, 1.0f, 0.0f},
105 { 1.0f, -1.0f, 0.0f},
106 { 1.0f, 1.0f, 0.0f},
109 struct rect
111 float left, top, right, bottom;
114 struct media_engine
116 IMFMediaEngine IMFMediaEngine_iface;
117 IMFAsyncCallback session_events;
118 IMFAsyncCallback load_handler;
119 IMFSampleGrabberSinkCallback grabber_callback;
120 LONG refcount;
121 IMFMediaEngineNotify *callback;
122 IMFAttributes *attributes;
123 IMFDXGIDeviceManager *device_manager;
124 HANDLE device_handle;
125 enum media_engine_mode mode;
126 unsigned int flags;
127 double playback_rate;
128 double default_playback_rate;
129 double volume;
130 double duration;
131 MF_MEDIA_ENGINE_NETWORK network_state;
132 MF_MEDIA_ENGINE_ERR error_code;
133 HRESULT extended_code;
134 MF_MEDIA_ENGINE_READY ready_state;
135 MF_MEDIA_ENGINE_PRELOAD preload;
136 IMFMediaSession *session;
137 IMFPresentationClock *clock;
138 IMFSourceResolver *resolver;
139 BSTR current_source;
140 struct
142 LONGLONG pts;
143 SIZE size;
144 SIZE ratio;
145 TOPOID node_id;
146 BYTE *buffer;
147 UINT buffer_size;
148 DXGI_FORMAT output_format;
150 struct
152 ID3D11Buffer *vb;
153 ID3D11Buffer *ps_cb;
154 ID3D11Texture2D *source;
155 ID3D11ShaderResourceView *srv;
156 ID3D11SamplerState *sampler;
157 ID3D11InputLayout *input_layout;
158 ID3D11VertexShader *vs;
159 ID3D11PixelShader *ps;
160 struct vec3 quad[4];
161 struct
163 struct rect dst;
164 struct rect src;
165 struct color backcolor;
166 } cb;
167 } d3d11;
168 } video_frame;
169 CRITICAL_SECTION cs;
172 static void media_engine_release_video_frame_resources(struct media_engine *engine)
174 if (engine->video_frame.d3d11.vb)
175 ID3D11Buffer_Release(engine->video_frame.d3d11.vb);
176 if (engine->video_frame.d3d11.ps_cb)
177 ID3D11Buffer_Release(engine->video_frame.d3d11.ps_cb);
178 if (engine->video_frame.d3d11.source)
179 ID3D11Texture2D_Release(engine->video_frame.d3d11.source);
180 if (engine->video_frame.d3d11.srv)
181 ID3D11ShaderResourceView_Release(engine->video_frame.d3d11.srv);
182 if (engine->video_frame.d3d11.sampler)
183 ID3D11SamplerState_Release(engine->video_frame.d3d11.sampler);
184 if (engine->video_frame.d3d11.input_layout)
185 ID3D11InputLayout_Release(engine->video_frame.d3d11.input_layout);
186 if (engine->video_frame.d3d11.vs)
187 ID3D11VertexShader_Release(engine->video_frame.d3d11.vs);
188 if (engine->video_frame.d3d11.ps)
189 ID3D11PixelShader_Release(engine->video_frame.d3d11.ps);
191 memset(&engine->video_frame.d3d11, 0, sizeof(engine->video_frame.d3d11));
192 memcpy(engine->video_frame.d3d11.quad, fullquad, sizeof(fullquad));
195 static HRESULT media_engine_lock_d3d_device(struct media_engine *engine, ID3D11Device **device)
197 HRESULT hr;
199 if (!engine->device_manager)
201 FIXME("Device manager wasn't set.\n");
202 return E_UNEXPECTED;
205 if (!engine->device_handle)
207 if (FAILED(hr = IMFDXGIDeviceManager_OpenDeviceHandle(engine->device_manager, &engine->device_handle)))
209 WARN("Failed to open device handle, hr %#x.\n", hr);
210 return hr;
214 hr = IMFDXGIDeviceManager_LockDevice(engine->device_manager, engine->device_handle, &IID_ID3D11Device,
215 (void **)device, TRUE);
216 if (hr == MF_E_DXGI_NEW_VIDEO_DEVICE)
218 IMFDXGIDeviceManager_CloseDeviceHandle(engine->device_manager, engine->device_handle);
219 engine->device_handle = NULL;
221 media_engine_release_video_frame_resources(engine);
223 if (FAILED(hr = IMFDXGIDeviceManager_OpenDeviceHandle(engine->device_manager, &engine->device_handle)))
225 WARN("Failed to open a device handle, hr %#x.\n", hr);
226 return hr;
228 hr = IMFDXGIDeviceManager_LockDevice(engine->device_manager, engine->device_handle, &IID_ID3D11Device,
229 (void **)device, TRUE);
232 return hr;
235 static void media_engine_unlock_d3d_device(struct media_engine *engine, ID3D11Device *device)
237 ID3D11Device_Release(device);
238 IMFDXGIDeviceManager_UnlockDevice(engine->device_manager, engine->device_handle, FALSE);
241 static HRESULT media_engine_create_d3d11_video_frame_resources(struct media_engine *engine, ID3D11Device *device)
243 static const D3D11_INPUT_ELEMENT_DESC layout_desc[] =
245 { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
247 static const DWORD vs_code[] =
249 #if 0
250 float4 main(float4 position : POSITION) : SV_POSITION
252 return position;
254 #endif
255 0x43425844, 0xa7a2f22d, 0x83ff2560, 0xe61638bd, 0x87e3ce90, 0x00000001, 0x000000d8, 0x00000003,
256 0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
257 0x00000000, 0x00000000, 0x00000003, 0x00000000, 0x00000f0f, 0x49534f50, 0x4e4f4954, 0xababab00,
258 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000001, 0x00000003,
259 0x00000000, 0x0000000f, 0x505f5653, 0x5449534f, 0x004e4f49, 0x52444853, 0x0000003c, 0x00010040,
260 0x0000000f, 0x0300005f, 0x001010f2, 0x00000000, 0x04000067, 0x001020f2, 0x00000000, 0x00000001,
261 0x05000036, 0x001020f2, 0x00000000, 0x00101e46, 0x00000000, 0x0100003e,
263 static const DWORD ps_code[] =
265 #if 0
266 Texture2D t;
267 SamplerState s;
268 float4 dst;
269 float4 src;
270 float4 backcolor;
272 float4 main(float4 position : SV_POSITION) : SV_TARGET
274 float2 p;
276 if (position.x < dst.x || position.x > dst.z) return backcolor;
277 if (position.y < dst.y || position.y > dst.w) return backcolor;
278 p.x = (position.x - dst.x) / (dst.z - dst.x);
279 p.y = 1.0f - (position.y - dst.y) / (dst.w - dst.y);
280 p.x = src.x + p.x * (src.z - src.x);
281 p.y = src.y + p.y * (src.w - src.y);
282 return t.Sample(s, p);
284 #endif
285 0x43425844, 0x5892e3b1, 0x24c17f7c, 0x9999f143, 0x49667872, 0x00000001, 0x0000032c, 0x00000003,
286 0x0000002c, 0x00000060, 0x00000094, 0x4e475349, 0x0000002c, 0x00000001, 0x00000008, 0x00000020,
287 0x00000000, 0x00000001, 0x00000003, 0x00000000, 0x0000030f, 0x505f5653, 0x5449534f, 0x004e4f49,
288 0x4e47534f, 0x0000002c, 0x00000001, 0x00000008, 0x00000020, 0x00000000, 0x00000000, 0x00000003,
289 0x00000000, 0x0000000f, 0x545f5653, 0x45475241, 0xabab0054, 0x52444853, 0x00000290, 0x00000040,
290 0x000000a4, 0x04000059, 0x00208e46, 0x00000000, 0x00000003, 0x0300005a, 0x00106000, 0x00000000,
291 0x04001858, 0x00107000, 0x00000000, 0x00005555, 0x04002064, 0x00101032, 0x00000000, 0x00000001,
292 0x03000065, 0x001020f2, 0x00000000, 0x02000068, 0x00000002, 0x08000031, 0x00100012, 0x00000000,
293 0x0010100a, 0x00000000, 0x0020800a, 0x00000000, 0x00000000, 0x08000031, 0x00100022, 0x00000000,
294 0x0020802a, 0x00000000, 0x00000000, 0x0010100a, 0x00000000, 0x0700003c, 0x00100012, 0x00000000,
295 0x0010001a, 0x00000000, 0x0010000a, 0x00000000, 0x0304001f, 0x0010000a, 0x00000000, 0x06000036,
296 0x001020f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000002, 0x0100003e, 0x01000015, 0x08000031,
297 0x00100012, 0x00000000, 0x0010101a, 0x00000000, 0x0020801a, 0x00000000, 0x00000000, 0x08000031,
298 0x00100022, 0x00000000, 0x0020803a, 0x00000000, 0x00000000, 0x0010101a, 0x00000000, 0x0700003c,
299 0x00100012, 0x00000000, 0x0010001a, 0x00000000, 0x0010000a, 0x00000000, 0x0304001f, 0x0010000a,
300 0x00000000, 0x06000036, 0x001020f2, 0x00000000, 0x00208e46, 0x00000000, 0x00000002, 0x0100003e,
301 0x01000015, 0x09000000, 0x00100032, 0x00000000, 0x00101046, 0x00000000, 0x80208046, 0x00000041,
302 0x00000000, 0x00000000, 0x0a000000, 0x001000c2, 0x00000000, 0x80208406, 0x00000041, 0x00000000,
303 0x00000000, 0x00208ea6, 0x00000000, 0x00000000, 0x0700000e, 0x00100032, 0x00000000, 0x00100046,
304 0x00000000, 0x00100ae6, 0x00000000, 0x08000000, 0x00100022, 0x00000000, 0x8010001a, 0x00000041,
305 0x00000000, 0x00004001, 0x3f800000, 0x0a000000, 0x001000c2, 0x00000000, 0x80208406, 0x00000041,
306 0x00000000, 0x00000001, 0x00208ea6, 0x00000000, 0x00000001, 0x0a000032, 0x00100012, 0x00000001,
307 0x0010000a, 0x00000000, 0x0010002a, 0x00000000, 0x0020800a, 0x00000000, 0x00000001, 0x0a000032,
308 0x00100022, 0x00000001, 0x0010001a, 0x00000000, 0x0010003a, 0x00000000, 0x0020801a, 0x00000000,
309 0x00000001, 0x09000045, 0x001020f2, 0x00000000, 0x00100046, 0x00000001, 0x00107e46, 0x00000000,
310 0x00106000, 0x00000000, 0x0100003e,
312 D3D11_SUBRESOURCE_DATA resource_data;
313 D3D11_TEXTURE2D_DESC texture_desc;
314 D3D11_SAMPLER_DESC sampler_desc;
315 D3D11_BUFFER_DESC buffer_desc;
316 HRESULT hr;
318 if (engine->video_frame.d3d11.source)
319 return S_OK;
321 /* Default vertex buffer, updated on first transfer call. */
322 buffer_desc.ByteWidth = sizeof(engine->video_frame.d3d11.quad);
323 buffer_desc.Usage = D3D11_USAGE_DEFAULT;
324 buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
325 buffer_desc.CPUAccessFlags = 0;
326 buffer_desc.MiscFlags = 0;
327 buffer_desc.StructureByteStride = 0;
329 resource_data.pSysMem = engine->video_frame.d3d11.quad;
330 resource_data.SysMemPitch = 0;
331 resource_data.SysMemSlicePitch = 0;
333 if (FAILED(hr = ID3D11Device_CreateBuffer(device, &buffer_desc, &resource_data, &engine->video_frame.d3d11.vb)))
335 WARN("Failed to create a vertex buffer, hr %#x.\n", hr);
336 goto failed;
339 buffer_desc.ByteWidth = sizeof(engine->video_frame.d3d11.cb);
340 buffer_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
342 if (FAILED(hr = ID3D11Device_CreateBuffer(device, &buffer_desc, NULL, &engine->video_frame.d3d11.ps_cb)))
344 WARN("Failed to create a buffer, hr %#x.\n", hr);
345 goto failed;
348 /* Source texture. */
349 texture_desc.Width = engine->video_frame.size.cx;
350 texture_desc.Height = engine->video_frame.size.cy;
351 texture_desc.MipLevels = 1;
352 texture_desc.ArraySize = 1;
353 texture_desc.Format = engine->video_frame.output_format;
354 texture_desc.SampleDesc.Count = 1;
355 texture_desc.SampleDesc.Quality = 0;
356 texture_desc.Usage = D3D11_USAGE_DEFAULT;
357 texture_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
358 texture_desc.CPUAccessFlags = 0;
359 texture_desc.MiscFlags = 0;
361 if (FAILED(hr = ID3D11Device_CreateTexture2D(device, &texture_desc, NULL, &engine->video_frame.d3d11.source)))
363 WARN("Failed to create source texture, hr %#x.\n", hr);
364 goto failed;
367 if (FAILED(hr = ID3D11Device_CreateShaderResourceView(device, (ID3D11Resource *)engine->video_frame.d3d11.source,
368 NULL, &engine->video_frame.d3d11.srv)))
370 WARN("Failed to create SRV, hr %#x.\n", hr);
371 goto failed;
374 /* Sampler state. */
375 memset(&sampler_desc, 0, sizeof(sampler_desc));
376 sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
377 sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
378 sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
379 sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
381 if (FAILED(hr = ID3D11Device_CreateSamplerState(device, &sampler_desc, &engine->video_frame.d3d11.sampler)))
383 WARN("Failed to create a sampler state, hr %#x.\n", hr);
384 goto failed;
387 /* Input layout */
388 if (FAILED(hr = ID3D11Device_CreateInputLayout(device, layout_desc, ARRAY_SIZE(layout_desc), vs_code, sizeof(vs_code),
389 &engine->video_frame.d3d11.input_layout)))
391 WARN("Failed to create input layout, hr %#x.\n", hr);
392 goto failed;
395 /* Shaders */
396 if (FAILED(hr = ID3D11Device_CreateVertexShader(device, vs_code, sizeof(vs_code), NULL, &engine->video_frame.d3d11.vs)))
398 WARN("Failed to create the vertex shader, hr %#x.\n", hr);
399 goto failed;
402 if (FAILED(hr = ID3D11Device_CreatePixelShader(device, ps_code, sizeof(ps_code), NULL, &engine->video_frame.d3d11.ps)))
404 WARN("Failed to create the pixel shader, hr %#x.\n", hr);
405 goto failed;
408 failed:
410 return hr;
413 struct range
415 double start;
416 double end;
419 struct time_range
421 IMFMediaTimeRange IMFMediaTimeRange_iface;
422 LONG refcount;
424 struct range *ranges;
425 size_t count;
426 size_t capacity;
429 static struct time_range *impl_from_IMFMediaTimeRange(IMFMediaTimeRange *iface)
431 return CONTAINING_RECORD(iface, struct time_range, IMFMediaTimeRange_iface);
434 struct media_error
436 IMFMediaError IMFMediaError_iface;
437 LONG refcount;
438 unsigned int code;
439 HRESULT extended_code;
442 static struct media_error *impl_from_IMFMediaError(IMFMediaError *iface)
444 return CONTAINING_RECORD(iface, struct media_error, IMFMediaError_iface);
447 static HRESULT WINAPI media_error_QueryInterface(IMFMediaError *iface, REFIID riid, void **obj)
449 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
451 if (IsEqualIID(riid, &IID_IMFMediaError) ||
452 IsEqualIID(riid, &IID_IUnknown))
454 *obj = iface;
455 IMFMediaError_AddRef(iface);
456 return S_OK;
459 WARN("Unsupported interface %s.\n", debugstr_guid(riid));
460 *obj = NULL;
461 return E_NOINTERFACE;
464 static ULONG WINAPI media_error_AddRef(IMFMediaError *iface)
466 struct media_error *me = impl_from_IMFMediaError(iface);
467 ULONG refcount = InterlockedIncrement(&me->refcount);
469 TRACE("%p, refcount %u.\n", iface, refcount);
471 return refcount;
474 static ULONG WINAPI media_error_Release(IMFMediaError *iface)
476 struct media_error *me = impl_from_IMFMediaError(iface);
477 ULONG refcount = InterlockedDecrement(&me->refcount);
479 TRACE("%p, refcount %u.\n", iface, refcount);
481 if (!refcount)
482 free(me);
484 return refcount;
487 static USHORT WINAPI media_error_GetErrorCode(IMFMediaError *iface)
489 struct media_error *me = impl_from_IMFMediaError(iface);
490 TRACE("%p.\n", iface);
491 return me->code;
494 static HRESULT WINAPI media_error_GetExtendedErrorCode(IMFMediaError *iface)
496 struct media_error *me = impl_from_IMFMediaError(iface);
497 TRACE("%p.\n", iface);
498 return me->extended_code;
501 static HRESULT WINAPI media_error_SetErrorCode(IMFMediaError *iface, MF_MEDIA_ENGINE_ERR code)
503 struct media_error *me = impl_from_IMFMediaError(iface);
505 TRACE("%p, %u.\n", iface, code);
507 if ((unsigned int)code > MF_MEDIA_ENGINE_ERR_ENCRYPTED)
508 return E_INVALIDARG;
510 me->code = code;
512 return S_OK;
515 static HRESULT WINAPI media_error_SetExtendedErrorCode(IMFMediaError *iface, HRESULT code)
517 struct media_error *me = impl_from_IMFMediaError(iface);
519 TRACE("%p, %#x.\n", iface, code);
521 me->extended_code = code;
523 return S_OK;
526 static const IMFMediaErrorVtbl media_error_vtbl =
528 media_error_QueryInterface,
529 media_error_AddRef,
530 media_error_Release,
531 media_error_GetErrorCode,
532 media_error_GetExtendedErrorCode,
533 media_error_SetErrorCode,
534 media_error_SetExtendedErrorCode,
537 static HRESULT create_media_error(IMFMediaError **ret)
539 struct media_error *object;
541 *ret = NULL;
543 if (!(object = calloc(1, sizeof(*object))))
544 return E_OUTOFMEMORY;
546 object->IMFMediaError_iface.lpVtbl = &media_error_vtbl;
547 object->refcount = 1;
549 *ret = &object->IMFMediaError_iface;
551 return S_OK;
554 static HRESULT WINAPI time_range_QueryInterface(IMFMediaTimeRange *iface, REFIID riid, void **obj)
556 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
558 if (IsEqualIID(riid, &IID_IMFMediaTimeRange) ||
559 IsEqualIID(riid, &IID_IUnknown))
561 *obj = iface;
562 IMFMediaTimeRange_AddRef(iface);
563 return S_OK;
566 WARN("Unsupported interface %s.\n", debugstr_guid(riid));
567 *obj = NULL;
568 return E_NOINTERFACE;
571 static ULONG WINAPI time_range_AddRef(IMFMediaTimeRange *iface)
573 struct time_range *range = impl_from_IMFMediaTimeRange(iface);
574 ULONG refcount = InterlockedIncrement(&range->refcount);
576 TRACE("%p, refcount %u.\n", iface, refcount);
578 return refcount;
581 static ULONG WINAPI time_range_Release(IMFMediaTimeRange *iface)
583 struct time_range *range = impl_from_IMFMediaTimeRange(iface);
584 ULONG refcount = InterlockedDecrement(&range->refcount);
586 TRACE("%p, refcount %u.\n", iface, refcount);
588 if (!refcount)
590 free(range->ranges);
591 free(range);
594 return refcount;
597 static DWORD WINAPI time_range_GetLength(IMFMediaTimeRange *iface)
599 struct time_range *range = impl_from_IMFMediaTimeRange(iface);
601 TRACE("%p.\n", iface);
603 return range->count;
606 static HRESULT WINAPI time_range_GetStart(IMFMediaTimeRange *iface, DWORD idx, double *start)
608 struct time_range *range = impl_from_IMFMediaTimeRange(iface);
610 TRACE("%p, %u, %p.\n", iface, idx, start);
612 if (idx >= range->count)
613 return E_INVALIDARG;
615 *start = range->ranges[idx].start;
617 return S_OK;
620 static HRESULT WINAPI time_range_GetEnd(IMFMediaTimeRange *iface, DWORD idx, double *end)
622 struct time_range *range = impl_from_IMFMediaTimeRange(iface);
624 TRACE("%p, %u, %p.\n", iface, idx, end);
626 if (idx >= range->count)
627 return E_INVALIDARG;
629 *end = range->ranges[idx].end;
631 return S_OK;
634 static BOOL WINAPI time_range_ContainsTime(IMFMediaTimeRange *iface, double time)
636 struct time_range *range = impl_from_IMFMediaTimeRange(iface);
637 size_t i;
639 TRACE("%p, %.8e.\n", iface, time);
641 for (i = 0; i < range->count; ++i)
643 if (time >= range->ranges[i].start && time <= range->ranges[i].end)
644 return TRUE;
647 return FALSE;
650 static HRESULT WINAPI time_range_AddRange(IMFMediaTimeRange *iface, double start, double end)
652 struct time_range *range = impl_from_IMFMediaTimeRange(iface);
654 TRACE("%p, %.8e, %.8e.\n", iface, start, end);
656 if (range->count)
658 FIXME("Range merging is not implemented.\n");
659 return E_NOTIMPL;
662 if (!mf_array_reserve((void **)&range->ranges, &range->capacity, range->count + 1, sizeof(*range->ranges)))
663 return E_OUTOFMEMORY;
665 range->ranges[range->count].start = start;
666 range->ranges[range->count].end = end;
667 range->count++;
669 return S_OK;
672 static HRESULT WINAPI time_range_Clear(IMFMediaTimeRange *iface)
674 struct time_range *range = impl_from_IMFMediaTimeRange(iface);
676 TRACE("%p.\n", iface);
678 range->count = 0;
680 return S_OK;
683 static const IMFMediaTimeRangeVtbl time_range_vtbl =
685 time_range_QueryInterface,
686 time_range_AddRef,
687 time_range_Release,
688 time_range_GetLength,
689 time_range_GetStart,
690 time_range_GetEnd,
691 time_range_ContainsTime,
692 time_range_AddRange,
693 time_range_Clear,
696 static HRESULT create_time_range(IMFMediaTimeRange **range)
698 struct time_range *object;
700 object = calloc(1, sizeof(*object));
701 if (!object)
702 return E_OUTOFMEMORY;
704 object->IMFMediaTimeRange_iface.lpVtbl = &time_range_vtbl;
705 object->refcount = 1;
707 *range = &object->IMFMediaTimeRange_iface;
709 return S_OK;
712 static void media_engine_set_flag(struct media_engine *engine, unsigned int mask, BOOL value)
714 if (value)
715 engine->flags |= mask;
716 else
717 engine->flags &= ~mask;
720 static inline struct media_engine *impl_from_IMFMediaEngine(IMFMediaEngine *iface)
722 return CONTAINING_RECORD(iface, struct media_engine, IMFMediaEngine_iface);
725 static struct media_engine *impl_from_session_events_IMFAsyncCallback(IMFAsyncCallback *iface)
727 return CONTAINING_RECORD(iface, struct media_engine, session_events);
730 static struct media_engine *impl_from_load_handler_IMFAsyncCallback(IMFAsyncCallback *iface)
732 return CONTAINING_RECORD(iface, struct media_engine, load_handler);
735 static struct media_engine *impl_from_IMFSampleGrabberSinkCallback(IMFSampleGrabberSinkCallback *iface)
737 return CONTAINING_RECORD(iface, struct media_engine, grabber_callback);
740 static unsigned int get_gcd(unsigned int a, unsigned int b)
742 unsigned int m;
744 while (b)
746 m = a % b;
747 a = b;
748 b = m;
751 return a;
754 static void media_engine_get_frame_size(struct media_engine *engine, IMFTopology *topology)
756 IMFMediaTypeHandler *handler;
757 IMFMediaType *media_type;
758 IMFStreamDescriptor *sd;
759 IMFTopologyNode *node;
760 unsigned int gcd;
761 UINT64 size;
762 HRESULT hr;
764 engine->video_frame.size.cx = 0;
765 engine->video_frame.size.cy = 0;
766 engine->video_frame.ratio.cx = 1;
767 engine->video_frame.ratio.cy = 1;
769 if (FAILED(IMFTopology_GetNodeByID(topology, engine->video_frame.node_id, &node)))
770 return;
772 hr = IMFTopologyNode_GetUnknown(node, &MF_TOPONODE_STREAM_DESCRIPTOR,
773 &IID_IMFStreamDescriptor, (void **)&sd);
774 IMFTopologyNode_Release(node);
775 if (FAILED(hr))
776 return;
778 hr = IMFStreamDescriptor_GetMediaTypeHandler(sd, &handler);
779 IMFStreamDescriptor_Release(sd);
780 if (FAILED(hr))
781 return;
783 hr = IMFMediaTypeHandler_GetCurrentMediaType(handler, &media_type);
784 IMFMediaTypeHandler_Release(handler);
785 if (FAILED(hr))
787 WARN("Failed to get current media type %#x.\n", hr);
788 return;
791 IMFMediaType_GetUINT64(media_type, &MF_MT_FRAME_SIZE, &size);
793 engine->video_frame.size.cx = size >> 32;
794 engine->video_frame.size.cy = size;
796 if ((gcd = get_gcd(engine->video_frame.size.cx, engine->video_frame.size.cy)))
798 engine->video_frame.ratio.cx = engine->video_frame.size.cx / gcd;
799 engine->video_frame.ratio.cy = engine->video_frame.size.cy / gcd;
802 IMFMediaType_Release(media_type);
805 static HRESULT WINAPI media_engine_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj)
807 if (IsEqualIID(riid, &IID_IMFAsyncCallback) ||
808 IsEqualIID(riid, &IID_IUnknown))
810 *obj = iface;
811 IMFAsyncCallback_AddRef(iface);
812 return S_OK;
815 WARN("Unsupported interface %s.\n", debugstr_guid(riid));
816 *obj = NULL;
817 return E_NOINTERFACE;
820 static ULONG WINAPI media_engine_session_events_AddRef(IMFAsyncCallback *iface)
822 struct media_engine *engine = impl_from_session_events_IMFAsyncCallback(iface);
823 return IMFMediaEngine_AddRef(&engine->IMFMediaEngine_iface);
826 static ULONG WINAPI media_engine_session_events_Release(IMFAsyncCallback *iface)
828 struct media_engine *engine = impl_from_session_events_IMFAsyncCallback(iface);
829 return IMFMediaEngine_Release(&engine->IMFMediaEngine_iface);
832 static HRESULT WINAPI media_engine_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue)
834 return E_NOTIMPL;
837 static HRESULT WINAPI media_engine_session_events_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
839 struct media_engine *engine = impl_from_session_events_IMFAsyncCallback(iface);
840 IMFMediaEvent *event = NULL;
841 MediaEventType event_type;
842 HRESULT hr;
844 if (FAILED(hr = IMFMediaSession_EndGetEvent(engine->session, result, &event)))
846 WARN("Failed to get session event, hr %#x.\n", hr);
847 goto failed;
850 if (FAILED(hr = IMFMediaEvent_GetType(event, &event_type)))
852 WARN("Failed to get event type, hr %#x.\n", hr);
853 goto failed;
856 switch (event_type)
858 case MEBufferingStarted:
859 case MEBufferingStopped:
861 IMFMediaEngineNotify_EventNotify(engine->callback, event_type == MEBufferingStarted ?
862 MF_MEDIA_ENGINE_EVENT_BUFFERINGSTARTED : MF_MEDIA_ENGINE_EVENT_BUFFERINGENDED, 0, 0);
863 break;
864 case MESessionTopologyStatus:
866 UINT32 topo_status = 0;
867 IMFTopology *topology;
868 PROPVARIANT value;
870 IMFMediaEvent_GetUINT32(event, &MF_EVENT_TOPOLOGY_STATUS, &topo_status);
871 if (topo_status != MF_TOPOSTATUS_READY)
872 break;
874 value.vt = VT_EMPTY;
875 if (FAILED(IMFMediaEvent_GetValue(event, &value)))
876 break;
878 if (value.vt != VT_UNKNOWN)
880 PropVariantClear(&value);
881 break;
884 topology = (IMFTopology *)value.punkVal;
886 EnterCriticalSection(&engine->cs);
888 engine->ready_state = MF_MEDIA_ENGINE_READY_HAVE_METADATA;
890 media_engine_get_frame_size(engine, topology);
892 IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_DURATIONCHANGE, 0, 0);
893 IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_LOADEDMETADATA, 0, 0);
895 engine->ready_state = MF_MEDIA_ENGINE_READY_HAVE_ENOUGH_DATA;
897 IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_LOADEDDATA, 0, 0);
898 IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_CANPLAY, 0, 0);
900 LeaveCriticalSection(&engine->cs);
902 PropVariantClear(&value);
904 break;
906 case MESessionStarted:
908 IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_PLAYING, 0, 0);
909 break;
910 case MESessionEnded:
912 EnterCriticalSection(&engine->cs);
913 media_engine_set_flag(engine, FLAGS_ENGINE_FIRST_FRAME, FALSE);
914 media_engine_set_flag(engine, FLAGS_ENGINE_IS_ENDED, TRUE);
915 engine->video_frame.pts = MINLONGLONG;
916 LeaveCriticalSection(&engine->cs);
918 IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_ENDED, 0, 0);
919 break;
922 failed:
924 if (event)
925 IMFMediaEvent_Release(event);
927 if (FAILED(hr = IMFMediaSession_BeginGetEvent(engine->session, iface, NULL)))
928 WARN("Failed to subscribe to session events, hr %#x.\n", hr);
930 return S_OK;
933 static const IMFAsyncCallbackVtbl media_engine_session_events_vtbl =
935 media_engine_callback_QueryInterface,
936 media_engine_session_events_AddRef,
937 media_engine_session_events_Release,
938 media_engine_callback_GetParameters,
939 media_engine_session_events_Invoke,
942 static ULONG WINAPI media_engine_load_handler_AddRef(IMFAsyncCallback *iface)
944 struct media_engine *engine = impl_from_load_handler_IMFAsyncCallback(iface);
945 return IMFMediaEngine_AddRef(&engine->IMFMediaEngine_iface);
948 static ULONG WINAPI media_engine_load_handler_Release(IMFAsyncCallback *iface)
950 struct media_engine *engine = impl_from_load_handler_IMFAsyncCallback(iface);
951 return IMFMediaEngine_Release(&engine->IMFMediaEngine_iface);
954 static HRESULT media_engine_create_source_node(IMFMediaSource *source, IMFPresentationDescriptor *pd, IMFStreamDescriptor *sd,
955 IMFTopologyNode **node)
957 HRESULT hr;
959 if (FAILED(hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, node)))
960 return hr;
962 IMFTopologyNode_SetUnknown(*node, &MF_TOPONODE_SOURCE, (IUnknown *)source);
963 IMFTopologyNode_SetUnknown(*node, &MF_TOPONODE_PRESENTATION_DESCRIPTOR, (IUnknown *)pd);
964 IMFTopologyNode_SetUnknown(*node, &MF_TOPONODE_STREAM_DESCRIPTOR, (IUnknown *)sd);
966 return S_OK;
969 static HRESULT media_engine_create_audio_renderer(struct media_engine *engine, IMFTopologyNode **node)
971 unsigned int category, role;
972 IMFActivate *sar_activate;
973 HRESULT hr;
975 *node = NULL;
977 if (FAILED(hr = MFCreateAudioRendererActivate(&sar_activate)))
978 return hr;
980 /* Configuration attributes keys differ between Engine and SAR. */
981 if (SUCCEEDED(IMFAttributes_GetUINT32(engine->attributes, &MF_MEDIA_ENGINE_AUDIO_CATEGORY, &category)))
982 IMFActivate_SetUINT32(sar_activate, &MF_AUDIO_RENDERER_ATTRIBUTE_STREAM_CATEGORY, category);
983 if (SUCCEEDED(IMFAttributes_GetUINT32(engine->attributes, &MF_MEDIA_ENGINE_AUDIO_ENDPOINT_ROLE, &role)))
984 IMFActivate_SetUINT32(sar_activate, &MF_AUDIO_RENDERER_ATTRIBUTE_ENDPOINT_ROLE, role);
986 if (SUCCEEDED(hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, node)))
988 IMFTopologyNode_SetObject(*node, (IUnknown *)sar_activate);
989 IMFTopologyNode_SetUINT32(*node, &MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE);
992 IMFActivate_Release(sar_activate);
994 return hr;
997 static HRESULT media_engine_create_video_renderer(struct media_engine *engine, IMFTopologyNode **node)
999 DXGI_FORMAT output_format;
1000 IMFMediaType *media_type;
1001 IMFActivate *activate;
1002 GUID subtype;
1003 HRESULT hr;
1005 *node = NULL;
1007 if (FAILED(IMFAttributes_GetUINT32(engine->attributes, &MF_MEDIA_ENGINE_VIDEO_OUTPUT_FORMAT, &output_format)))
1009 WARN("Output format was not specified.\n");
1010 return E_FAIL;
1013 memcpy(&subtype, &MFVideoFormat_Base, sizeof(subtype));
1014 if (!(subtype.Data1 = MFMapDXGIFormatToDX9Format(output_format)))
1016 WARN("Unrecognized output format %#x.\n", output_format);
1017 return E_FAIL;
1020 if (FAILED(hr = MFCreateMediaType(&media_type)))
1021 return hr;
1023 IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video);
1024 IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &subtype);
1026 hr = MFCreateSampleGrabberSinkActivate(media_type, &engine->grabber_callback, &activate);
1027 IMFMediaType_Release(media_type);
1028 if (FAILED(hr))
1029 return hr;
1031 if (SUCCEEDED(hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, node)))
1033 IMFTopologyNode_SetObject(*node, (IUnknown *)activate);
1034 IMFTopologyNode_SetUINT32(*node, &MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE);
1037 IMFActivate_Release(activate);
1039 engine->video_frame.output_format = output_format;
1041 return hr;
1044 static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMediaSource *source)
1046 IMFStreamDescriptor *sd_audio = NULL, *sd_video = NULL;
1047 unsigned int stream_count = 0, i;
1048 IMFPresentationDescriptor *pd;
1049 IMFTopology *topology;
1050 UINT64 duration;
1051 HRESULT hr;
1053 media_engine_release_video_frame_resources(engine);
1055 if (FAILED(hr = IMFMediaSource_CreatePresentationDescriptor(source, &pd)))
1056 return hr;
1058 if (FAILED(hr = IMFPresentationDescriptor_GetStreamDescriptorCount(pd, &stream_count)))
1059 WARN("Failed to get stream count, hr %#x.\n", hr);
1061 /* Enable first video stream and first audio stream. */
1063 for (i = 0; i < stream_count; ++i)
1065 IMFMediaTypeHandler *type_handler;
1066 IMFStreamDescriptor *sd;
1067 BOOL selected;
1069 IMFPresentationDescriptor_DeselectStream(pd, i);
1071 if (sd_audio && sd_video)
1072 continue;
1074 IMFPresentationDescriptor_GetStreamDescriptorByIndex(pd, i, &selected, &sd);
1076 if (SUCCEEDED(IMFStreamDescriptor_GetMediaTypeHandler(sd, &type_handler)))
1078 GUID major = { 0 };
1080 IMFMediaTypeHandler_GetMajorType(type_handler, &major);
1082 if (IsEqualGUID(&major, &MFMediaType_Audio) && !sd_audio)
1084 sd_audio = sd;
1085 IMFStreamDescriptor_AddRef(sd_audio);
1086 IMFPresentationDescriptor_SelectStream(pd, i);
1088 else if (IsEqualGUID(&major, &MFMediaType_Video) && !sd_video && !(engine->flags & MF_MEDIA_ENGINE_AUDIOONLY))
1090 sd_video = sd;
1091 IMFStreamDescriptor_AddRef(sd_video);
1092 IMFPresentationDescriptor_SelectStream(pd, i);
1095 IMFMediaTypeHandler_Release(type_handler);
1099 if (!sd_video && !sd_audio)
1101 IMFPresentationDescriptor_Release(pd);
1102 return E_UNEXPECTED;
1105 media_engine_set_flag(engine, FLAGS_ENGINE_HAS_VIDEO, !!sd_video);
1106 media_engine_set_flag(engine, FLAGS_ENGINE_HAS_AUDIO, !!sd_audio);
1108 /* Assume live source if duration was not provided. */
1109 if (SUCCEEDED(IMFPresentationDescriptor_GetUINT64(pd, &MF_PD_DURATION, &duration)))
1111 /* Convert 100ns to seconds. */
1112 engine->duration = duration / 10000000;
1114 else
1115 engine->duration = INFINITY;
1117 if (SUCCEEDED(hr = MFCreateTopology(&topology)))
1119 IMFTopologyNode *sar_node = NULL, *audio_src = NULL;
1120 IMFTopologyNode *grabber_node = NULL, *video_src = NULL;
1122 if (sd_audio)
1124 if (FAILED(hr = media_engine_create_source_node(source, pd, sd_audio, &audio_src)))
1125 WARN("Failed to create audio source node, hr %#x.\n", hr);
1127 if (FAILED(hr = media_engine_create_audio_renderer(engine, &sar_node)))
1128 WARN("Failed to create audio renderer node, hr %#x.\n", hr);
1130 if (sar_node && audio_src)
1132 IMFTopology_AddNode(topology, audio_src);
1133 IMFTopology_AddNode(topology, sar_node);
1134 IMFTopologyNode_ConnectOutput(audio_src, 0, sar_node, 0);
1137 if (sar_node)
1138 IMFTopologyNode_Release(sar_node);
1139 if (audio_src)
1140 IMFTopologyNode_Release(audio_src);
1143 if (SUCCEEDED(hr) && sd_video)
1145 if (FAILED(hr = media_engine_create_source_node(source, pd, sd_video, &video_src)))
1146 WARN("Failed to create video source node, hr %#x.\n", hr);
1148 if (FAILED(hr = media_engine_create_video_renderer(engine, &grabber_node)))
1149 WARN("Failed to create video grabber node, hr %#x.\n", hr);
1151 if (grabber_node && video_src)
1153 IMFTopology_AddNode(topology, video_src);
1154 IMFTopology_AddNode(topology, grabber_node);
1155 IMFTopologyNode_ConnectOutput(video_src, 0, grabber_node, 0);
1158 if (SUCCEEDED(hr))
1159 IMFTopologyNode_GetTopoNodeID(video_src, &engine->video_frame.node_id);
1161 if (grabber_node)
1162 IMFTopologyNode_Release(grabber_node);
1163 if (video_src)
1164 IMFTopologyNode_Release(video_src);
1167 IMFTopology_SetUINT32(topology, &MF_TOPOLOGY_ENUMERATE_SOURCE_TYPES, TRUE);
1169 if (SUCCEEDED(hr))
1170 hr = IMFMediaSession_SetTopology(engine->session, MFSESSION_SETTOPOLOGY_IMMEDIATE, topology);
1173 if (topology)
1174 IMFTopology_Release(topology);
1176 if (sd_video)
1177 IMFStreamDescriptor_Release(sd_video);
1178 if (sd_audio)
1179 IMFStreamDescriptor_Release(sd_audio);
1181 IMFPresentationDescriptor_Release(pd);
1183 return hr;
1186 static void media_engine_start_playback(struct media_engine *engine)
1188 PROPVARIANT var;
1190 var.vt = VT_EMPTY;
1191 IMFMediaSession_Start(engine->session, &GUID_NULL, &var);
1194 static HRESULT WINAPI media_engine_load_handler_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result)
1196 struct media_engine *engine = impl_from_load_handler_IMFAsyncCallback(iface);
1197 unsigned int start_playback;
1198 MF_OBJECT_TYPE obj_type;
1199 IMFMediaSource *source;
1200 IUnknown *object = NULL;
1201 HRESULT hr;
1203 EnterCriticalSection(&engine->cs);
1205 engine->network_state = MF_MEDIA_ENGINE_NETWORK_LOADING;
1206 IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_LOADSTART, 0, 0);
1208 start_playback = engine->flags & FLAGS_ENGINE_PLAY_PENDING;
1209 media_engine_set_flag(engine, FLAGS_ENGINE_SOURCE_PENDING | FLAGS_ENGINE_PLAY_PENDING, FALSE);
1211 if (FAILED(hr = IMFSourceResolver_EndCreateObjectFromURL(engine->resolver, result, &obj_type, &object)))
1212 WARN("Failed to create source object, hr %#x.\n", hr);
1214 if (object)
1216 if (SUCCEEDED(hr = IUnknown_QueryInterface(object, &IID_IMFMediaSource, (void **)&source)))
1218 hr = media_engine_create_topology(engine, source);
1219 IMFMediaSource_Release(source);
1221 IUnknown_Release(object);
1224 if (SUCCEEDED(hr))
1226 engine->network_state = MF_MEDIA_ENGINE_NETWORK_IDLE;
1227 if (start_playback)
1228 media_engine_start_playback(engine);
1230 else
1232 engine->network_state = MF_MEDIA_ENGINE_NETWORK_NO_SOURCE;
1233 engine->error_code = MF_MEDIA_ENGINE_ERR_SRC_NOT_SUPPORTED;
1234 engine->extended_code = hr;
1235 IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_ERROR, engine->error_code,
1236 engine->extended_code);
1239 LeaveCriticalSection(&engine->cs);
1241 return S_OK;
1244 static const IMFAsyncCallbackVtbl media_engine_load_handler_vtbl =
1246 media_engine_callback_QueryInterface,
1247 media_engine_load_handler_AddRef,
1248 media_engine_load_handler_Release,
1249 media_engine_callback_GetParameters,
1250 media_engine_load_handler_Invoke,
1253 static HRESULT WINAPI media_engine_QueryInterface(IMFMediaEngine *iface, REFIID riid, void **obj)
1255 TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj);
1257 if (IsEqualIID(riid, &IID_IMFMediaEngine) ||
1258 IsEqualIID(riid, &IID_IUnknown))
1260 *obj = iface;
1261 IMFMediaEngine_AddRef(iface);
1262 return S_OK;
1265 WARN("Unsupported interface %s.\n", debugstr_guid(riid));
1266 *obj = NULL;
1267 return E_NOINTERFACE;
1270 static ULONG WINAPI media_engine_AddRef(IMFMediaEngine *iface)
1272 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1273 ULONG refcount = InterlockedIncrement(&engine->refcount);
1275 TRACE("%p, refcount %u.\n", iface, refcount);
1277 return refcount;
1280 static void free_media_engine(struct media_engine *engine)
1282 if (engine->callback)
1283 IMFMediaEngineNotify_Release(engine->callback);
1284 if (engine->clock)
1285 IMFPresentationClock_Release(engine->clock);
1286 if (engine->session)
1287 IMFMediaSession_Release(engine->session);
1288 if (engine->attributes)
1289 IMFAttributes_Release(engine->attributes);
1290 if (engine->resolver)
1291 IMFSourceResolver_Release(engine->resolver);
1292 media_engine_release_video_frame_resources(engine);
1293 if (engine->device_manager)
1295 IMFDXGIDeviceManager_CloseDeviceHandle(engine->device_manager, engine->device_handle);
1296 IMFDXGIDeviceManager_Release(engine->device_manager);
1298 SysFreeString(engine->current_source);
1299 DeleteCriticalSection(&engine->cs);
1300 free(engine->video_frame.buffer);
1301 free(engine);
1304 static ULONG WINAPI media_engine_Release(IMFMediaEngine *iface)
1306 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1307 ULONG refcount = InterlockedDecrement(&engine->refcount);
1309 TRACE("%p, refcount %u.\n", iface, refcount);
1311 if (!refcount)
1312 free_media_engine(engine);
1314 return refcount;
1317 static HRESULT WINAPI media_engine_GetError(IMFMediaEngine *iface, IMFMediaError **error)
1319 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1320 HRESULT hr = S_OK;
1322 TRACE("%p, %p.\n", iface, error);
1324 *error = NULL;
1326 EnterCriticalSection(&engine->cs);
1327 if (engine->flags & FLAGS_ENGINE_SHUT_DOWN)
1328 hr = MF_E_SHUTDOWN;
1329 else if (engine->error_code)
1331 if (SUCCEEDED(hr = create_media_error(error)))
1333 IMFMediaError_SetErrorCode(*error, engine->error_code);
1334 IMFMediaError_SetExtendedErrorCode(*error, engine->extended_code);
1337 LeaveCriticalSection(&engine->cs);
1339 return hr;
1342 static HRESULT WINAPI media_engine_SetErrorCode(IMFMediaEngine *iface, MF_MEDIA_ENGINE_ERR code)
1344 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1345 HRESULT hr = S_OK;
1347 TRACE("%p, %u.\n", iface, code);
1349 if ((unsigned int)code > MF_MEDIA_ENGINE_ERR_ENCRYPTED)
1350 return E_INVALIDARG;
1352 EnterCriticalSection(&engine->cs);
1353 if (engine->flags & FLAGS_ENGINE_SHUT_DOWN)
1354 hr = MF_E_SHUTDOWN;
1355 else
1356 engine->error_code = code;
1357 LeaveCriticalSection(&engine->cs);
1359 return hr;
1362 static HRESULT WINAPI media_engine_SetSourceElements(IMFMediaEngine *iface, IMFMediaEngineSrcElements *elements)
1364 FIXME("(%p, %p): stub.\n", iface, elements);
1366 return E_NOTIMPL;
1369 static HRESULT WINAPI media_engine_SetSource(IMFMediaEngine *iface, BSTR url)
1371 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1372 HRESULT hr = S_OK;
1374 TRACE("%p, %s.\n", iface, debugstr_w(url));
1376 EnterCriticalSection(&engine->cs);
1378 if (engine->flags & FLAGS_ENGINE_SHUT_DOWN)
1379 hr = MF_E_SHUTDOWN;
1380 else
1382 SysFreeString(engine->current_source);
1383 engine->current_source = NULL;
1384 if (url)
1385 engine->current_source = SysAllocString(url);
1387 engine->ready_state = MF_MEDIA_ENGINE_READY_HAVE_NOTHING;
1389 IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_PURGEQUEUEDEVENTS, 0, 0);
1391 engine->network_state = MF_MEDIA_ENGINE_NETWORK_NO_SOURCE;
1393 if (url)
1395 IPropertyStore *props = NULL;
1396 unsigned int flags;
1398 flags = MF_RESOLUTION_MEDIASOURCE;
1399 if (engine->flags & MF_MEDIA_ENGINE_DISABLE_LOCAL_PLUGINS)
1400 flags |= MF_RESOLUTION_DISABLE_LOCAL_PLUGINS;
1402 IMFAttributes_GetUnknown(engine->attributes, &MF_MEDIA_ENGINE_SOURCE_RESOLVER_CONFIG_STORE,
1403 &IID_IPropertyStore, (void **)&props);
1404 hr = IMFSourceResolver_BeginCreateObjectFromURL(engine->resolver, url, flags, props, NULL,
1405 &engine->load_handler, NULL);
1406 if (SUCCEEDED(hr))
1407 media_engine_set_flag(engine, FLAGS_ENGINE_SOURCE_PENDING, TRUE);
1409 if (props)
1410 IPropertyStore_Release(props);
1414 LeaveCriticalSection(&engine->cs);
1416 return hr;
1419 static HRESULT WINAPI media_engine_GetCurrentSource(IMFMediaEngine *iface, BSTR *url)
1421 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1422 HRESULT hr = S_OK;
1424 TRACE("%p, %p.\n", iface, url);
1426 *url = NULL;
1428 EnterCriticalSection(&engine->cs);
1429 if (engine->current_source)
1431 if (!(*url = SysAllocString(engine->current_source)))
1432 hr = E_OUTOFMEMORY;
1434 LeaveCriticalSection(&engine->cs);
1436 return hr;
1439 static USHORT WINAPI media_engine_GetNetworkState(IMFMediaEngine *iface)
1441 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1443 TRACE("%p.\n", iface);
1445 return engine->network_state;
1448 static MF_MEDIA_ENGINE_PRELOAD WINAPI media_engine_GetPreload(IMFMediaEngine *iface)
1450 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1451 MF_MEDIA_ENGINE_PRELOAD preload;
1453 TRACE("%p.\n", iface);
1455 EnterCriticalSection(&engine->cs);
1456 preload = engine->preload;
1457 LeaveCriticalSection(&engine->cs);
1459 return preload;
1462 static HRESULT WINAPI media_engine_SetPreload(IMFMediaEngine *iface, MF_MEDIA_ENGINE_PRELOAD preload)
1464 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1466 TRACE("%p, %d.\n", iface, preload);
1468 EnterCriticalSection(&engine->cs);
1469 engine->preload = preload;
1470 LeaveCriticalSection(&engine->cs);
1472 return S_OK;
1475 static HRESULT WINAPI media_engine_GetBuffered(IMFMediaEngine *iface, IMFMediaTimeRange **range)
1477 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1478 HRESULT hr;
1480 TRACE("%p, %p.\n", iface, range);
1482 if (FAILED(hr = create_time_range(range)))
1483 return hr;
1485 EnterCriticalSection(&engine->cs);
1486 if (!isnan(engine->duration))
1487 hr = IMFMediaTimeRange_AddRange(*range, 0.0, engine->duration);
1488 LeaveCriticalSection(&engine->cs);
1490 return hr;
1493 static HRESULT WINAPI media_engine_Load(IMFMediaEngine *iface)
1495 FIXME("(%p): stub.\n", iface);
1497 return E_NOTIMPL;
1500 static HRESULT WINAPI media_engine_CanPlayType(IMFMediaEngine *iface, BSTR type, MF_MEDIA_ENGINE_CANPLAY *answer)
1502 FIXME("(%p, %s, %p): stub.\n", iface, debugstr_w(type), answer);
1504 return E_NOTIMPL;
1507 static USHORT WINAPI media_engine_GetReadyState(IMFMediaEngine *iface)
1509 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1510 unsigned short state;
1512 TRACE("%p.\n", iface);
1514 EnterCriticalSection(&engine->cs);
1515 state = engine->ready_state;
1516 LeaveCriticalSection(&engine->cs);
1518 return state;
1521 static BOOL WINAPI media_engine_IsSeeking(IMFMediaEngine *iface)
1523 FIXME("(%p): stub.\n", iface);
1525 return FALSE;
1528 static double WINAPI media_engine_GetCurrentTime(IMFMediaEngine *iface)
1530 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1531 double ret = 0.0;
1532 MFTIME clocktime;
1534 TRACE("%p.\n", iface);
1536 EnterCriticalSection(&engine->cs);
1537 if (engine->flags & FLAGS_ENGINE_IS_ENDED)
1539 ret = engine->duration;
1541 else if (SUCCEEDED(IMFPresentationClock_GetTime(engine->clock, &clocktime)))
1543 ret = (double)clocktime / 10000000.0;
1545 LeaveCriticalSection(&engine->cs);
1547 return ret;
1550 static HRESULT WINAPI media_engine_SetCurrentTime(IMFMediaEngine *iface, double time)
1552 FIXME("(%p, %f): stub.\n", iface, time);
1554 return E_NOTIMPL;
1557 static double WINAPI media_engine_GetStartTime(IMFMediaEngine *iface)
1559 FIXME("(%p): stub.\n", iface);
1561 return 0.0;
1564 static double WINAPI media_engine_GetDuration(IMFMediaEngine *iface)
1566 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1567 double value;
1569 TRACE("%p.\n", iface);
1571 EnterCriticalSection(&engine->cs);
1572 value = engine->duration;
1573 LeaveCriticalSection(&engine->cs);
1575 return value;
1578 static BOOL WINAPI media_engine_IsPaused(IMFMediaEngine *iface)
1580 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1581 BOOL value;
1583 TRACE("%p.\n", iface);
1585 EnterCriticalSection(&engine->cs);
1586 value = !!(engine->flags & FLAGS_ENGINE_PAUSED);
1587 LeaveCriticalSection(&engine->cs);
1589 return value;
1592 static double WINAPI media_engine_GetDefaultPlaybackRate(IMFMediaEngine *iface)
1594 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1595 double rate;
1597 TRACE("%p.\n", iface);
1599 EnterCriticalSection(&engine->cs);
1600 rate = engine->default_playback_rate;
1601 LeaveCriticalSection(&engine->cs);
1603 return rate;
1606 static HRESULT WINAPI media_engine_SetDefaultPlaybackRate(IMFMediaEngine *iface, double rate)
1608 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1609 HRESULT hr = S_OK;
1611 TRACE("%p, %f.\n", iface, rate);
1613 EnterCriticalSection(&engine->cs);
1614 if (engine->flags & FLAGS_ENGINE_SHUT_DOWN)
1615 hr = MF_E_SHUTDOWN;
1616 else if (engine->default_playback_rate != rate)
1618 engine->default_playback_rate = rate;
1619 IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_RATECHANGE, 0, 0);
1621 LeaveCriticalSection(&engine->cs);
1623 return hr;
1626 static double WINAPI media_engine_GetPlaybackRate(IMFMediaEngine *iface)
1628 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1629 double rate;
1631 TRACE("%p.\n", iface);
1633 EnterCriticalSection(&engine->cs);
1634 rate = engine->playback_rate;
1635 LeaveCriticalSection(&engine->cs);
1637 return rate;
1640 static HRESULT WINAPI media_engine_SetPlaybackRate(IMFMediaEngine *iface, double rate)
1642 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1643 HRESULT hr = S_OK;
1645 TRACE("%p, %f.\n", iface, rate);
1647 EnterCriticalSection(&engine->cs);
1648 if (engine->flags & FLAGS_ENGINE_SHUT_DOWN)
1649 hr = MF_E_SHUTDOWN;
1650 else if (engine->playback_rate != rate)
1652 engine->playback_rate = rate;
1653 IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_RATECHANGE, 0, 0);
1655 LeaveCriticalSection(&engine->cs);
1657 return hr;
1660 static HRESULT WINAPI media_engine_GetPlayed(IMFMediaEngine *iface, IMFMediaTimeRange **played)
1662 FIXME("(%p, %p): stub.\n", iface, played);
1664 return E_NOTIMPL;
1667 static HRESULT WINAPI media_engine_GetSeekable(IMFMediaEngine *iface, IMFMediaTimeRange **seekable)
1669 FIXME("(%p, %p): stub.\n", iface, seekable);
1671 return E_NOTIMPL;
1674 static BOOL WINAPI media_engine_IsEnded(IMFMediaEngine *iface)
1676 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1677 BOOL value;
1679 TRACE("%p.\n", iface);
1681 EnterCriticalSection(&engine->cs);
1682 value = !!(engine->flags & FLAGS_ENGINE_IS_ENDED);
1683 LeaveCriticalSection(&engine->cs);
1685 return value;
1688 static BOOL WINAPI media_engine_GetAutoPlay(IMFMediaEngine *iface)
1690 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1691 BOOL value;
1693 TRACE("%p.\n", iface);
1695 EnterCriticalSection(&engine->cs);
1696 value = !!(engine->flags & FLAGS_ENGINE_AUTO_PLAY);
1697 LeaveCriticalSection(&engine->cs);
1699 return value;
1702 static HRESULT WINAPI media_engine_SetAutoPlay(IMFMediaEngine *iface, BOOL autoplay)
1704 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1706 FIXME("(%p, %d): stub.\n", iface, autoplay);
1708 EnterCriticalSection(&engine->cs);
1709 media_engine_set_flag(engine, FLAGS_ENGINE_AUTO_PLAY, autoplay);
1710 LeaveCriticalSection(&engine->cs);
1712 return S_OK;
1715 static BOOL WINAPI media_engine_GetLoop(IMFMediaEngine *iface)
1717 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1718 BOOL value;
1720 TRACE("%p.\n", iface);
1722 EnterCriticalSection(&engine->cs);
1723 value = !!(engine->flags & FLAGS_ENGINE_LOOP);
1724 LeaveCriticalSection(&engine->cs);
1726 return value;
1729 static HRESULT WINAPI media_engine_SetLoop(IMFMediaEngine *iface, BOOL loop)
1731 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1733 FIXME("(%p, %d): stub.\n", iface, loop);
1735 EnterCriticalSection(&engine->cs);
1736 media_engine_set_flag(engine, FLAGS_ENGINE_LOOP, loop);
1737 LeaveCriticalSection(&engine->cs);
1739 return S_OK;
1742 static HRESULT WINAPI media_engine_Play(IMFMediaEngine *iface)
1744 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1746 TRACE("%p.\n", iface);
1748 EnterCriticalSection(&engine->cs);
1750 IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_PURGEQUEUEDEVENTS, 0, 0);
1752 if (!(engine->flags & FLAGS_ENGINE_WAITING))
1754 media_engine_set_flag(engine, FLAGS_ENGINE_PAUSED | FLAGS_ENGINE_IS_ENDED, FALSE);
1755 IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_PLAY, 0, 0);
1757 if (!(engine->flags & FLAGS_ENGINE_SOURCE_PENDING))
1758 media_engine_start_playback(engine);
1759 else
1760 media_engine_set_flag(engine, FLAGS_ENGINE_PLAY_PENDING, TRUE);
1762 media_engine_set_flag(engine, FLAGS_ENGINE_WAITING, TRUE);
1765 IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_WAITING, 0, 0);
1767 LeaveCriticalSection(&engine->cs);
1769 return S_OK;
1772 static HRESULT WINAPI media_engine_Pause(IMFMediaEngine *iface)
1774 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1776 TRACE("%p.\n", iface);
1778 EnterCriticalSection(&engine->cs);
1780 if (!(engine->flags & FLAGS_ENGINE_PAUSED))
1782 media_engine_set_flag(engine, FLAGS_ENGINE_WAITING | FLAGS_ENGINE_IS_ENDED, FALSE);
1783 media_engine_set_flag(engine, FLAGS_ENGINE_PAUSED, TRUE);
1785 IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_TIMEUPDATE, 0, 0);
1786 IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_PAUSE, 0, 0);
1789 IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_PURGEQUEUEDEVENTS, 0, 0);
1791 LeaveCriticalSection(&engine->cs);
1793 return S_OK;
1796 static BOOL WINAPI media_engine_GetMuted(IMFMediaEngine *iface)
1798 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1799 BOOL ret;
1801 TRACE("%p.\n", iface);
1803 EnterCriticalSection(&engine->cs);
1804 ret = !!(engine->flags & FLAGS_ENGINE_MUTED);
1805 LeaveCriticalSection(&engine->cs);
1807 return ret;
1810 static HRESULT WINAPI media_engine_SetMuted(IMFMediaEngine *iface, BOOL muted)
1812 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1813 HRESULT hr = S_OK;
1815 TRACE("%p, %d.\n", iface, muted);
1817 EnterCriticalSection(&engine->cs);
1818 if (engine->flags & FLAGS_ENGINE_SHUT_DOWN)
1819 hr = MF_E_SHUTDOWN;
1820 else if (!!(engine->flags & FLAGS_ENGINE_MUTED) ^ !!muted)
1822 media_engine_set_flag(engine, FLAGS_ENGINE_MUTED, muted);
1823 IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_VOLUMECHANGE, 0, 0);
1825 LeaveCriticalSection(&engine->cs);
1827 return hr;
1830 static double WINAPI media_engine_GetVolume(IMFMediaEngine *iface)
1832 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1833 double volume;
1835 TRACE("%p.\n", iface);
1837 EnterCriticalSection(&engine->cs);
1838 volume = engine->volume;
1839 LeaveCriticalSection(&engine->cs);
1841 return volume;
1844 static HRESULT WINAPI media_engine_SetVolume(IMFMediaEngine *iface, double volume)
1846 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1847 HRESULT hr = S_OK;
1849 TRACE("%p, %f.\n", iface, volume);
1851 EnterCriticalSection(&engine->cs);
1852 if (engine->flags & FLAGS_ENGINE_SHUT_DOWN)
1853 hr = MF_E_SHUTDOWN;
1854 else if (volume != engine->volume)
1856 engine->volume = volume;
1857 IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_VOLUMECHANGE, 0, 0);
1859 LeaveCriticalSection(&engine->cs);
1861 return hr;
1864 static BOOL WINAPI media_engine_HasVideo(IMFMediaEngine *iface)
1866 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1867 BOOL value;
1869 TRACE("%p.\n", iface);
1871 EnterCriticalSection(&engine->cs);
1872 value = !!(engine->flags & FLAGS_ENGINE_HAS_VIDEO);
1873 LeaveCriticalSection(&engine->cs);
1875 return value;
1878 static BOOL WINAPI media_engine_HasAudio(IMFMediaEngine *iface)
1880 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1881 BOOL value;
1883 TRACE("%p.\n", iface);
1885 EnterCriticalSection(&engine->cs);
1886 value = !!(engine->flags & FLAGS_ENGINE_HAS_AUDIO);
1887 LeaveCriticalSection(&engine->cs);
1889 return value;
1892 static HRESULT WINAPI media_engine_GetNativeVideoSize(IMFMediaEngine *iface, DWORD *cx, DWORD *cy)
1894 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1895 HRESULT hr = S_OK;
1897 TRACE("%p, %p, %p.\n", iface, cx, cy);
1899 if (!cx && !cy)
1900 return E_INVALIDARG;
1902 EnterCriticalSection(&engine->cs);
1904 if (engine->flags & FLAGS_ENGINE_SHUT_DOWN)
1905 hr = MF_E_SHUTDOWN;
1906 else if (!engine->video_frame.size.cx && !engine->video_frame.size.cy)
1907 hr = E_FAIL;
1908 else
1910 if (cx) *cx = engine->video_frame.size.cx;
1911 if (cy) *cy = engine->video_frame.size.cy;
1914 LeaveCriticalSection(&engine->cs);
1916 return hr;
1919 static HRESULT WINAPI media_engine_GetVideoAspectRatio(IMFMediaEngine *iface, DWORD *cx, DWORD *cy)
1921 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1922 HRESULT hr = S_OK;
1924 TRACE("%p, %p, %p.\n", iface, cx, cy);
1926 if (!cx && !cy)
1927 return E_INVALIDARG;
1929 EnterCriticalSection(&engine->cs);
1931 if (engine->flags & FLAGS_ENGINE_SHUT_DOWN)
1932 hr = MF_E_SHUTDOWN;
1933 else if (!engine->video_frame.size.cx && !engine->video_frame.size.cy)
1934 hr = E_FAIL;
1935 else
1937 if (cx) *cx = engine->video_frame.ratio.cx;
1938 if (cy) *cy = engine->video_frame.ratio.cy;
1941 LeaveCriticalSection(&engine->cs);
1943 return hr;
1946 static HRESULT WINAPI media_engine_Shutdown(IMFMediaEngine *iface)
1948 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
1949 HRESULT hr = S_OK;
1951 FIXME("(%p): stub.\n", iface);
1953 EnterCriticalSection(&engine->cs);
1954 if (engine->flags & FLAGS_ENGINE_SHUT_DOWN)
1955 hr = MF_E_SHUTDOWN;
1956 else
1958 media_engine_set_flag(engine, FLAGS_ENGINE_SHUT_DOWN, TRUE);
1959 IMFMediaSession_Shutdown(engine->session);
1961 LeaveCriticalSection(&engine->cs);
1963 return hr;
1966 static void set_rect(struct rect *rect, float left, float top, float right, float bottom)
1968 rect->left = left;
1969 rect->top = top;
1970 rect->right = right;
1971 rect->bottom = bottom;
1974 static void media_engine_adjust_destination_for_ratio(const struct media_engine *engine,
1975 struct rect *src_n, struct rect *dst)
1977 float dst_width = dst->right - dst->left, dst_height = dst->bottom - dst->top;
1978 D3D11_TEXTURE2D_DESC source_desc;
1979 float src_width, src_height;
1980 struct rect src;
1982 ID3D11Texture2D_GetDesc(engine->video_frame.d3d11.source, &source_desc);
1983 set_rect(&src, src_n->left * source_desc.Width, src_n->top * source_desc.Height,
1984 src_n->right * source_desc.Width, src_n->bottom * source_desc.Height);
1986 src_width = src.right - src.left;
1987 src_height = src.bottom - src.top;
1989 if (src_width * dst_height > dst_width * src_height)
1991 /* src is "wider" than dst. */
1992 float dst_center = (dst->top + dst->bottom) / 2.0f;
1993 float scaled_height = src_height * dst_width / src_width;
1995 dst->top = dst_center - scaled_height / 2.0f;
1996 dst->bottom = dst->top + scaled_height;
1998 else if (src_width * dst_height < dst_width * src_height)
2000 /* src is "taller" than dst. */
2001 float dst_center = (dst->left + dst->right) / 2.0f;
2002 float scaled_width = src_width * dst_height / src_height;
2004 dst->left = dst_center - scaled_width / 2.0f;
2005 dst->right = dst->left + scaled_width;
2009 static void media_engine_update_d3d11_frame_surface(ID3D11DeviceContext *context, struct media_engine *engine)
2011 D3D11_TEXTURE2D_DESC surface_desc;
2013 if (!(engine->flags & FLAGS_ENGINE_NEW_FRAME))
2014 return;
2016 ID3D11Texture2D_GetDesc(engine->video_frame.d3d11.source, &surface_desc);
2018 switch (surface_desc.Format)
2020 case DXGI_FORMAT_B8G8R8A8_UNORM:
2021 case DXGI_FORMAT_B8G8R8X8_UNORM:
2022 surface_desc.Width *= 4;
2023 break;
2024 default:
2025 FIXME("Unsupported format %#x.\n", surface_desc.Format);
2026 surface_desc.Width = 0;
2029 if (engine->video_frame.buffer_size == surface_desc.Width * surface_desc.Height)
2031 ID3D11DeviceContext_UpdateSubresource(context, (ID3D11Resource *)engine->video_frame.d3d11.source,
2032 0, NULL, engine->video_frame.buffer, surface_desc.Width, 0);
2035 media_engine_set_flag(engine, FLAGS_ENGINE_NEW_FRAME, FALSE);
2038 static HRESULT media_engine_transfer_to_d3d11_texture(struct media_engine *engine, ID3D11Texture2D *texture,
2039 const MFVideoNormalizedRect *src_rect, const RECT *dst_rect, const MFARGB *color)
2041 static const float black[] = {0.0f, 0.0f, 0.0f, 0.0f};
2042 ID3D11Device *device, *dst_device;
2043 ID3D11DeviceContext *context;
2044 ID3D11RenderTargetView *rtv;
2045 unsigned int stride, offset;
2046 D3D11_TEXTURE2D_DESC desc;
2047 BOOL device_mismatch;
2048 struct vec3 quad[4];
2049 D3D11_VIEWPORT vp;
2050 struct rect src, dst;
2051 struct color backcolor;
2052 HRESULT hr;
2053 RECT rect;
2055 if (FAILED(hr = media_engine_lock_d3d_device(engine, &device)))
2056 return hr;
2058 if (FAILED(hr = media_engine_create_d3d11_video_frame_resources(engine, device)))
2060 WARN("Failed to create d3d resources, hr %#x.\n", hr);
2061 goto done;
2064 ID3D11Texture2D_GetDevice(texture, &dst_device);
2065 device_mismatch = device != dst_device;
2066 ID3D11Device_Release(dst_device);
2068 if (device_mismatch)
2070 WARN("Destination target from different device.\n");
2071 hr = E_UNEXPECTED;
2072 goto done;
2075 ID3D11Texture2D_GetDesc(texture, &desc);
2077 if (FAILED(hr = ID3D11Device_CreateRenderTargetView(device, (ID3D11Resource *)texture, NULL, &rtv)))
2079 WARN("Failed to create an rtv, hr %#x.\n", hr);
2080 goto done;
2083 ID3D11Device_GetImmediateContext(device, &context);
2085 /* Whole destination is cleared, regardless of specified rectangle. */
2086 ID3D11DeviceContext_ClearRenderTargetView(context, rtv, black);
2088 if (dst_rect)
2090 rect.left = max(0, dst_rect->left);
2091 rect.top = max(0, dst_rect->top);
2092 rect.right = min(desc.Width, dst_rect->right);
2093 rect.bottom = min(desc.Height, dst_rect->bottom);
2095 quad[0].x = 2.0f * rect.left / desc.Width - 1.0f;
2096 quad[0].y = -2.0f * rect.bottom / desc.Height + 1.0f;
2097 quad[0].z = 0.0f;
2099 quad[1].x = quad[0].x;
2100 quad[1].y = -2.0f * rect.top / desc.Height + 1.0f;
2101 quad[1].z = 0.0f;
2103 quad[2].x = 2.0f * rect.right / desc.Width - 1.0f;
2104 quad[2].y = quad[0].y;
2105 quad[2].z = 0.0f;
2107 quad[3].x = quad[2].x;
2108 quad[3].y = quad[1].y;
2109 quad[3].z = 0.0f;
2111 set_rect(&dst, dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom);
2113 else
2115 memcpy(quad, fullquad, sizeof(quad));
2116 set_rect(&dst, 0.0f, 0.0f, desc.Width, desc.Height);
2119 if (src_rect)
2120 memcpy(&src, src_rect, sizeof(src));
2121 else
2122 set_rect(&src, 0.0f, 0.0f, 1.0f, 1.0f);
2124 media_engine_adjust_destination_for_ratio(engine, &src, &dst);
2126 if (memcmp(quad, engine->video_frame.d3d11.quad, sizeof(quad)))
2128 memcpy(engine->video_frame.d3d11.quad, quad, sizeof(quad));
2129 ID3D11DeviceContext_UpdateSubresource(context, (ID3D11Resource *)engine->video_frame.d3d11.vb, 0, NULL, quad, 0, 0);
2132 if (color)
2134 backcolor.r = color->rgbRed / 255.0f;
2135 backcolor.g = color->rgbGreen / 255.0f;
2136 backcolor.b = color->rgbBlue / 255.0f;
2137 backcolor.a = color->rgbAlpha / 255.0f;
2139 else
2140 memcpy(&backcolor, black, sizeof(backcolor));
2142 if (memcmp(&dst, &engine->video_frame.d3d11.cb.dst, sizeof(dst)) ||
2143 memcmp(&src, &engine->video_frame.d3d11.cb.src, sizeof(src)) ||
2144 memcmp(&backcolor, &engine->video_frame.d3d11.cb.backcolor, sizeof(backcolor)))
2146 memcpy(&engine->video_frame.d3d11.cb.dst, &dst, sizeof(dst));
2147 memcpy(&engine->video_frame.d3d11.cb.src, &src, sizeof(src));
2148 memcpy(&engine->video_frame.d3d11.cb.backcolor, &backcolor, sizeof(backcolor));
2150 ID3D11DeviceContext_UpdateSubresource(context, (ID3D11Resource *)engine->video_frame.d3d11.ps_cb, 0, NULL,
2151 &engine->video_frame.d3d11.cb, 0, 0);
2154 /* Update with new frame contents */
2155 media_engine_update_d3d11_frame_surface(context, engine);
2157 vp.TopLeftX = 0.0f;
2158 vp.TopLeftY = 0.0f;
2159 vp.Width = desc.Width;
2160 vp.Height = desc.Height;
2161 vp.MinDepth = 0.0f;
2162 vp.MaxDepth = 1.0f;
2163 ID3D11DeviceContext_RSSetViewports(context, 1, &vp);
2165 ID3D11DeviceContext_IASetInputLayout(context, engine->video_frame.d3d11.input_layout);
2166 ID3D11DeviceContext_IASetPrimitiveTopology(context, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
2167 stride = sizeof(*quad);
2168 offset = 0;
2169 ID3D11DeviceContext_IASetVertexBuffers(context, 0, 1, &engine->video_frame.d3d11.vb, &stride, &offset);
2170 ID3D11DeviceContext_VSSetShader(context, engine->video_frame.d3d11.vs, NULL, 0);
2171 ID3D11DeviceContext_PSSetShader(context, engine->video_frame.d3d11.ps, NULL, 0);
2172 ID3D11DeviceContext_PSSetShaderResources(context, 0, 1, &engine->video_frame.d3d11.srv);
2173 ID3D11DeviceContext_PSSetConstantBuffers(context, 0, 1, &engine->video_frame.d3d11.ps_cb);
2174 ID3D11DeviceContext_PSSetSamplers(context, 0, 1, &engine->video_frame.d3d11.sampler);
2175 ID3D11DeviceContext_OMSetRenderTargets(context, 1, &rtv, NULL);
2177 ID3D11DeviceContext_Draw(context, 4, 0);
2179 ID3D11RenderTargetView_Release(rtv);
2180 ID3D11DeviceContext_Release(context);
2182 done:
2183 media_engine_unlock_d3d_device(engine, device);
2185 return hr;
2188 static HRESULT WINAPI media_engine_TransferVideoFrame(IMFMediaEngine *iface, IUnknown *surface,
2189 const MFVideoNormalizedRect *src_rect, const RECT *dst_rect, const MFARGB *color)
2191 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
2192 ID3D11Texture2D *texture;
2193 HRESULT hr = E_NOINTERFACE;
2195 TRACE("%p, %p, %s, %s, %p.\n", iface, surface, src_rect ? wine_dbg_sprintf("(%f,%f)-(%f,%f)",
2196 src_rect->left, src_rect->top, src_rect->right, src_rect->bottom) : "(null)",
2197 wine_dbgstr_rect(dst_rect), color);
2199 EnterCriticalSection(&engine->cs);
2201 if (SUCCEEDED(IUnknown_QueryInterface(surface, &IID_ID3D11Texture2D, (void **)&texture)))
2203 hr = media_engine_transfer_to_d3d11_texture(engine, texture, src_rect, dst_rect, color);
2204 ID3D11Texture2D_Release(texture);
2206 else
2208 FIXME("Unsupported destination type.\n");
2211 LeaveCriticalSection(&engine->cs);
2213 return hr;
2216 static HRESULT WINAPI media_engine_OnVideoStreamTick(IMFMediaEngine *iface, LONGLONG *pts)
2218 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
2219 HRESULT hr;
2221 TRACE("%p, %p.\n", iface, pts);
2223 EnterCriticalSection(&engine->cs);
2225 if (engine->flags & FLAGS_ENGINE_SHUT_DOWN)
2226 hr = MF_E_SHUTDOWN;
2227 else if (!pts)
2228 hr = E_POINTER;
2229 else
2231 *pts = engine->video_frame.pts;
2232 hr = *pts == MINLONGLONG ? S_FALSE : S_OK;
2235 LeaveCriticalSection(&engine->cs);
2237 return hr;
2240 static const IMFMediaEngineVtbl media_engine_vtbl =
2242 media_engine_QueryInterface,
2243 media_engine_AddRef,
2244 media_engine_Release,
2245 media_engine_GetError,
2246 media_engine_SetErrorCode,
2247 media_engine_SetSourceElements,
2248 media_engine_SetSource,
2249 media_engine_GetCurrentSource,
2250 media_engine_GetNetworkState,
2251 media_engine_GetPreload,
2252 media_engine_SetPreload,
2253 media_engine_GetBuffered,
2254 media_engine_Load,
2255 media_engine_CanPlayType,
2256 media_engine_GetReadyState,
2257 media_engine_IsSeeking,
2258 media_engine_GetCurrentTime,
2259 media_engine_SetCurrentTime,
2260 media_engine_GetStartTime,
2261 media_engine_GetDuration,
2262 media_engine_IsPaused,
2263 media_engine_GetDefaultPlaybackRate,
2264 media_engine_SetDefaultPlaybackRate,
2265 media_engine_GetPlaybackRate,
2266 media_engine_SetPlaybackRate,
2267 media_engine_GetPlayed,
2268 media_engine_GetSeekable,
2269 media_engine_IsEnded,
2270 media_engine_GetAutoPlay,
2271 media_engine_SetAutoPlay,
2272 media_engine_GetLoop,
2273 media_engine_SetLoop,
2274 media_engine_Play,
2275 media_engine_Pause,
2276 media_engine_GetMuted,
2277 media_engine_SetMuted,
2278 media_engine_GetVolume,
2279 media_engine_SetVolume,
2280 media_engine_HasVideo,
2281 media_engine_HasAudio,
2282 media_engine_GetNativeVideoSize,
2283 media_engine_GetVideoAspectRatio,
2284 media_engine_Shutdown,
2285 media_engine_TransferVideoFrame,
2286 media_engine_OnVideoStreamTick,
2289 static HRESULT WINAPI media_engine_grabber_callback_QueryInterface(IMFSampleGrabberSinkCallback *iface,
2290 REFIID riid, void **obj)
2292 if (IsEqualIID(riid, &IID_IMFSampleGrabberSinkCallback) ||
2293 IsEqualIID(riid, &IID_IUnknown))
2295 *obj = iface;
2296 IMFSampleGrabberSinkCallback_AddRef(iface);
2297 return S_OK;
2300 *obj = NULL;
2301 return E_NOINTERFACE;
2304 static ULONG WINAPI media_engine_grabber_callback_AddRef(IMFSampleGrabberSinkCallback *iface)
2306 struct media_engine *engine = impl_from_IMFSampleGrabberSinkCallback(iface);
2307 return IMFMediaEngine_AddRef(&engine->IMFMediaEngine_iface);
2310 static ULONG WINAPI media_engine_grabber_callback_Release(IMFSampleGrabberSinkCallback *iface)
2312 struct media_engine *engine = impl_from_IMFSampleGrabberSinkCallback(iface);
2313 return IMFMediaEngine_Release(&engine->IMFMediaEngine_iface);
2316 static HRESULT WINAPI media_engine_grabber_callback_OnClockStart(IMFSampleGrabberSinkCallback *iface,
2317 MFTIME systime, LONGLONG start_offset)
2319 return S_OK;
2322 static HRESULT WINAPI media_engine_grabber_callback_OnClockStop(IMFSampleGrabberSinkCallback *iface,
2323 MFTIME systime)
2325 struct media_engine *engine = impl_from_IMFSampleGrabberSinkCallback(iface);
2327 EnterCriticalSection(&engine->cs);
2328 media_engine_set_flag(engine, FLAGS_ENGINE_FIRST_FRAME, FALSE);
2329 engine->video_frame.pts = MINLONGLONG;
2330 LeaveCriticalSection(&engine->cs);
2332 return S_OK;
2335 static HRESULT WINAPI media_engine_grabber_callback_OnClockPause(IMFSampleGrabberSinkCallback *iface,
2336 MFTIME systime)
2338 return S_OK;
2341 static HRESULT WINAPI media_engine_grabber_callback_OnClockRestart(IMFSampleGrabberSinkCallback *iface,
2342 MFTIME systime)
2344 return S_OK;
2347 static HRESULT WINAPI media_engine_grabber_callback_OnClockSetRate(IMFSampleGrabberSinkCallback *iface,
2348 MFTIME systime, float rate)
2350 return S_OK;
2353 static HRESULT WINAPI media_engine_grabber_callback_OnSetPresentationClock(IMFSampleGrabberSinkCallback *iface,
2354 IMFPresentationClock *clock)
2356 return S_OK;
2359 static HRESULT WINAPI media_engine_grabber_callback_OnProcessSample(IMFSampleGrabberSinkCallback *iface,
2360 REFGUID major_type, DWORD sample_flags, LONGLONG sample_time, LONGLONG sample_duration,
2361 const BYTE *buffer, DWORD buffer_size)
2363 struct media_engine *engine = impl_from_IMFSampleGrabberSinkCallback(iface);
2365 EnterCriticalSection(&engine->cs);
2367 if (!(engine->flags & FLAGS_ENGINE_FIRST_FRAME))
2369 IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_FIRSTFRAMEREADY, 0, 0);
2370 media_engine_set_flag(engine, FLAGS_ENGINE_FIRST_FRAME, TRUE);
2372 engine->video_frame.pts = sample_time;
2373 if (engine->video_frame.buffer_size < buffer_size)
2375 free(engine->video_frame.buffer);
2376 if ((engine->video_frame.buffer = malloc(buffer_size)))
2377 engine->video_frame.buffer_size = buffer_size;
2379 if (engine->video_frame.buffer)
2381 memcpy(engine->video_frame.buffer, buffer, buffer_size);
2382 engine->flags |= FLAGS_ENGINE_NEW_FRAME;
2385 LeaveCriticalSection(&engine->cs);
2387 return S_OK;
2390 static HRESULT WINAPI media_engine_grabber_callback_OnShutdown(IMFSampleGrabberSinkCallback *iface)
2392 return S_OK;
2395 static const IMFSampleGrabberSinkCallbackVtbl media_engine_grabber_callback_vtbl =
2397 media_engine_grabber_callback_QueryInterface,
2398 media_engine_grabber_callback_AddRef,
2399 media_engine_grabber_callback_Release,
2400 media_engine_grabber_callback_OnClockStart,
2401 media_engine_grabber_callback_OnClockStop,
2402 media_engine_grabber_callback_OnClockPause,
2403 media_engine_grabber_callback_OnClockRestart,
2404 media_engine_grabber_callback_OnClockSetRate,
2405 media_engine_grabber_callback_OnSetPresentationClock,
2406 media_engine_grabber_callback_OnProcessSample,
2407 media_engine_grabber_callback_OnShutdown,
2410 static HRESULT WINAPI media_engine_factory_QueryInterface(IMFMediaEngineClassFactory *iface, REFIID riid, void **obj)
2412 if (IsEqualIID(riid, &IID_IMFMediaEngineClassFactory) ||
2413 IsEqualIID(riid, &IID_IUnknown))
2415 *obj = iface;
2416 IMFMediaEngineClassFactory_AddRef(iface);
2417 return S_OK;
2420 WARN("Unsupported interface %s.\n", debugstr_guid(riid));
2421 *obj = NULL;
2422 return E_NOINTERFACE;
2425 static ULONG WINAPI media_engine_factory_AddRef(IMFMediaEngineClassFactory *iface)
2427 return 2;
2430 static ULONG WINAPI media_engine_factory_Release(IMFMediaEngineClassFactory *iface)
2432 return 1;
2435 static HRESULT init_media_engine(DWORD flags, IMFAttributes *attributes, struct media_engine *engine)
2437 DXGI_FORMAT output_format;
2438 UINT64 playback_hwnd;
2439 IMFClock *clock;
2440 HRESULT hr;
2442 engine->IMFMediaEngine_iface.lpVtbl = &media_engine_vtbl;
2443 engine->session_events.lpVtbl = &media_engine_session_events_vtbl;
2444 engine->load_handler.lpVtbl = &media_engine_load_handler_vtbl;
2445 engine->grabber_callback.lpVtbl = &media_engine_grabber_callback_vtbl;
2446 engine->refcount = 1;
2447 engine->flags = (flags & MF_MEDIA_ENGINE_CREATEFLAGS_MASK) | FLAGS_ENGINE_PAUSED;
2448 engine->default_playback_rate = 1.0;
2449 engine->playback_rate = 1.0;
2450 engine->volume = 1.0;
2451 engine->duration = NAN;
2452 engine->video_frame.pts = MINLONGLONG;
2453 InitializeCriticalSection(&engine->cs);
2455 hr = IMFAttributes_GetUnknown(attributes, &MF_MEDIA_ENGINE_CALLBACK, &IID_IMFMediaEngineNotify,
2456 (void **)&engine->callback);
2457 if (FAILED(hr))
2458 return hr;
2460 IMFAttributes_GetUnknown(attributes, &MF_MEDIA_ENGINE_DXGI_MANAGER, &IID_IMFDXGIDeviceManager,
2461 (void **)&engine->device_manager);
2463 if (FAILED(hr = MFCreateMediaSession(NULL, &engine->session)))
2464 return hr;
2466 if (FAILED(hr = IMFMediaSession_GetClock(engine->session, &clock)))
2467 return hr;
2469 hr = IMFClock_QueryInterface(clock, &IID_IMFPresentationClock, (void **)&engine->clock);
2470 IMFClock_Release(clock);
2471 if (FAILED(hr))
2472 return hr;
2474 if (FAILED(hr = IMFMediaSession_BeginGetEvent(engine->session, &engine->session_events, NULL)))
2475 return hr;
2477 if (FAILED(hr = MFCreateSourceResolver(&engine->resolver)))
2478 return hr;
2480 if (FAILED(hr = MFCreateAttributes(&engine->attributes, 0)))
2481 return hr;
2483 if (FAILED(hr = IMFAttributes_CopyAllItems(attributes, engine->attributes)))
2484 return hr;
2486 IMFAttributes_GetUINT64(attributes, &MF_MEDIA_ENGINE_PLAYBACK_HWND, &playback_hwnd);
2487 hr = IMFAttributes_GetUINT32(attributes, &MF_MEDIA_ENGINE_VIDEO_OUTPUT_FORMAT, &output_format);
2488 if (playback_hwnd) /* FIXME: handle MF_MEDIA_ENGINE_PLAYBACK_VISUAL */
2489 engine->mode = MEDIA_ENGINE_RENDERING_MODE;
2490 else
2492 if (SUCCEEDED(hr))
2493 engine->mode = MEDIA_ENGINE_FRAME_SERVER_MODE;
2494 else
2495 engine->mode = MEDIA_ENGINE_AUDIO_MODE;
2498 return S_OK;
2501 static HRESULT WINAPI media_engine_factory_CreateInstance(IMFMediaEngineClassFactory *iface, DWORD flags,
2502 IMFAttributes *attributes, IMFMediaEngine **engine)
2504 struct media_engine *object;
2505 HRESULT hr;
2507 TRACE("%p, %#x, %p, %p.\n", iface, flags, attributes, engine);
2509 if (!attributes || !engine)
2510 return E_POINTER;
2512 object = calloc(1, sizeof(*object));
2513 if (!object)
2514 return E_OUTOFMEMORY;
2516 hr = init_media_engine(flags, attributes, object);
2517 if (FAILED(hr))
2519 free_media_engine(object);
2520 return hr;
2523 *engine = &object->IMFMediaEngine_iface;
2525 return S_OK;
2528 static HRESULT WINAPI media_engine_factory_CreateTimeRange(IMFMediaEngineClassFactory *iface,
2529 IMFMediaTimeRange **range)
2531 TRACE("%p, %p.\n", iface, range);
2533 return create_time_range(range);
2536 static HRESULT WINAPI media_engine_factory_CreateError(IMFMediaEngineClassFactory *iface, IMFMediaError **error)
2538 TRACE("%p, %p.\n", iface, error);
2540 return create_media_error(error);
2543 static const IMFMediaEngineClassFactoryVtbl media_engine_factory_vtbl =
2545 media_engine_factory_QueryInterface,
2546 media_engine_factory_AddRef,
2547 media_engine_factory_Release,
2548 media_engine_factory_CreateInstance,
2549 media_engine_factory_CreateTimeRange,
2550 media_engine_factory_CreateError,
2553 static IMFMediaEngineClassFactory media_engine_factory = { &media_engine_factory_vtbl };
2555 static HRESULT WINAPI classfactory_QueryInterface(IClassFactory *iface, REFIID riid, void **obj)
2557 TRACE("%s, %p.\n", debugstr_guid(riid), obj);
2559 if (IsEqualGUID(riid, &IID_IClassFactory) ||
2560 IsEqualGUID(riid, &IID_IUnknown))
2562 IClassFactory_AddRef(iface);
2563 *obj = iface;
2564 return S_OK;
2567 WARN("interface %s not implemented.\n", debugstr_guid(riid));
2568 *obj = NULL;
2569 return E_NOINTERFACE;
2572 static ULONG WINAPI classfactory_AddRef(IClassFactory *iface)
2574 return 2;
2577 static ULONG WINAPI classfactory_Release(IClassFactory *iface)
2579 return 1;
2582 static HRESULT WINAPI classfactory_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID riid, void **obj)
2584 TRACE("%p, %s, %p.\n", outer, debugstr_guid(riid), obj);
2586 *obj = NULL;
2588 if (outer)
2589 return CLASS_E_NOAGGREGATION;
2591 return IMFMediaEngineClassFactory_QueryInterface(&media_engine_factory, riid, obj);
2594 static HRESULT WINAPI classfactory_LockServer(IClassFactory *iface, BOOL dolock)
2596 FIXME("(%d): stub.\n", dolock);
2597 return S_OK;
2600 static const IClassFactoryVtbl class_factory_vtbl =
2602 classfactory_QueryInterface,
2603 classfactory_AddRef,
2604 classfactory_Release,
2605 classfactory_CreateInstance,
2606 classfactory_LockServer,
2609 static IClassFactory classfactory = { &class_factory_vtbl };
2611 HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID riid, void **obj)
2613 TRACE("%s, %s, %p.\n", debugstr_guid(clsid), debugstr_guid(riid), obj);
2615 if (IsEqualGUID(clsid, &CLSID_MFMediaEngineClassFactory))
2616 return IClassFactory_QueryInterface(&classfactory, riid, obj);
2618 WARN("Unsupported class %s.\n", debugstr_guid(clsid));
2619 *obj = NULL;
2620 return CLASS_E_CLASSNOTAVAILABLE;