mfmediaengine: Handle Play() when called before topology is set.
[wine.git] / dlls / mfmediaengine / main.c
bloba555d0dad99da6a642df8ed9df2ed3c3e8ca6cba
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 surface_desc.Width *= 4;
2022 break;
2023 default:
2024 FIXME("Unsupported format %#x.\n", surface_desc.Format);
2025 surface_desc.Width = 0;
2028 if (engine->video_frame.buffer_size == surface_desc.Width * surface_desc.Height)
2030 ID3D11DeviceContext_UpdateSubresource(context, (ID3D11Resource *)engine->video_frame.d3d11.source,
2031 0, NULL, engine->video_frame.buffer, surface_desc.Width, 0);
2034 media_engine_set_flag(engine, FLAGS_ENGINE_NEW_FRAME, FALSE);
2037 static HRESULT media_engine_transfer_to_d3d11_texture(struct media_engine *engine, ID3D11Texture2D *texture,
2038 const MFVideoNormalizedRect *src_rect, const RECT *dst_rect, const MFARGB *color)
2040 static const float black[] = {0.0f, 0.0f, 0.0f, 0.0f};
2041 ID3D11Device *device, *dst_device;
2042 ID3D11DeviceContext *context;
2043 ID3D11RenderTargetView *rtv;
2044 unsigned int stride, offset;
2045 D3D11_TEXTURE2D_DESC desc;
2046 BOOL device_mismatch;
2047 struct vec3 quad[4];
2048 D3D11_VIEWPORT vp;
2049 struct rect src, dst;
2050 struct color backcolor;
2051 HRESULT hr;
2052 RECT rect;
2054 if (FAILED(hr = media_engine_lock_d3d_device(engine, &device)))
2055 return hr;
2057 if (FAILED(hr = media_engine_create_d3d11_video_frame_resources(engine, device)))
2059 WARN("Failed to create d3d resources, hr %#x.\n", hr);
2060 goto done;
2063 ID3D11Texture2D_GetDevice(texture, &dst_device);
2064 device_mismatch = device != dst_device;
2065 ID3D11Device_Release(dst_device);
2067 if (device_mismatch)
2069 WARN("Destination target from different device.\n");
2070 hr = E_UNEXPECTED;
2071 goto done;
2074 ID3D11Texture2D_GetDesc(texture, &desc);
2076 if (FAILED(hr = ID3D11Device_CreateRenderTargetView(device, (ID3D11Resource *)texture, NULL, &rtv)))
2078 WARN("Failed to create an rtv, hr %#x.\n", hr);
2079 goto done;
2082 ID3D11Device_GetImmediateContext(device, &context);
2084 /* Whole destination is cleared, regardless of specified rectangle. */
2085 ID3D11DeviceContext_ClearRenderTargetView(context, rtv, black);
2087 if (dst_rect)
2089 rect.left = max(0, dst_rect->left);
2090 rect.top = max(0, dst_rect->top);
2091 rect.right = min(desc.Width, dst_rect->right);
2092 rect.bottom = min(desc.Height, dst_rect->bottom);
2094 quad[0].x = 2.0f * rect.left / desc.Width - 1.0f;
2095 quad[0].y = -2.0f * rect.bottom / desc.Height + 1.0f;
2096 quad[0].z = 0.0f;
2098 quad[1].x = quad[0].x;
2099 quad[1].y = -2.0f * rect.top / desc.Height + 1.0f;
2100 quad[1].z = 0.0f;
2102 quad[2].x = 2.0f * rect.right / desc.Width - 1.0f;
2103 quad[2].y = quad[0].y;
2104 quad[2].z = 0.0f;
2106 quad[3].x = quad[2].x;
2107 quad[3].y = quad[1].y;
2108 quad[3].z = 0.0f;
2110 set_rect(&dst, dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom);
2112 else
2114 memcpy(quad, fullquad, sizeof(quad));
2115 set_rect(&dst, 0.0f, 0.0f, desc.Width, desc.Height);
2118 if (src_rect)
2119 memcpy(&src, src_rect, sizeof(src));
2120 else
2121 set_rect(&src, 0.0f, 0.0f, 1.0f, 1.0f);
2123 media_engine_adjust_destination_for_ratio(engine, &src, &dst);
2125 if (memcmp(quad, engine->video_frame.d3d11.quad, sizeof(quad)))
2127 memcpy(engine->video_frame.d3d11.quad, quad, sizeof(quad));
2128 ID3D11DeviceContext_UpdateSubresource(context, (ID3D11Resource *)engine->video_frame.d3d11.vb, 0, NULL, quad, 0, 0);
2131 if (color)
2133 backcolor.r = color->rgbRed / 255.0f;
2134 backcolor.g = color->rgbGreen / 255.0f;
2135 backcolor.b = color->rgbBlue / 255.0f;
2136 backcolor.a = color->rgbAlpha / 255.0f;
2138 else
2139 memcpy(&backcolor, black, sizeof(backcolor));
2141 if (memcmp(&dst, &engine->video_frame.d3d11.cb.dst, sizeof(dst)) ||
2142 memcmp(&src, &engine->video_frame.d3d11.cb.src, sizeof(src)) ||
2143 memcmp(&backcolor, &engine->video_frame.d3d11.cb.backcolor, sizeof(backcolor)))
2145 memcpy(&engine->video_frame.d3d11.cb.dst, &dst, sizeof(dst));
2146 memcpy(&engine->video_frame.d3d11.cb.src, &src, sizeof(src));
2147 memcpy(&engine->video_frame.d3d11.cb.backcolor, &backcolor, sizeof(backcolor));
2149 ID3D11DeviceContext_UpdateSubresource(context, (ID3D11Resource *)engine->video_frame.d3d11.ps_cb, 0, NULL,
2150 &engine->video_frame.d3d11.cb, 0, 0);
2153 /* Update with new frame contents */
2154 media_engine_update_d3d11_frame_surface(context, engine);
2156 vp.TopLeftX = 0.0f;
2157 vp.TopLeftY = 0.0f;
2158 vp.Width = desc.Width;
2159 vp.Height = desc.Height;
2160 vp.MinDepth = 0.0f;
2161 vp.MaxDepth = 1.0f;
2162 ID3D11DeviceContext_RSSetViewports(context, 1, &vp);
2164 ID3D11DeviceContext_IASetInputLayout(context, engine->video_frame.d3d11.input_layout);
2165 ID3D11DeviceContext_IASetPrimitiveTopology(context, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
2166 stride = sizeof(*quad);
2167 offset = 0;
2168 ID3D11DeviceContext_IASetVertexBuffers(context, 0, 1, &engine->video_frame.d3d11.vb, &stride, &offset);
2169 ID3D11DeviceContext_VSSetShader(context, engine->video_frame.d3d11.vs, NULL, 0);
2170 ID3D11DeviceContext_PSSetShader(context, engine->video_frame.d3d11.ps, NULL, 0);
2171 ID3D11DeviceContext_PSSetShaderResources(context, 0, 1, &engine->video_frame.d3d11.srv);
2172 ID3D11DeviceContext_PSSetConstantBuffers(context, 0, 1, &engine->video_frame.d3d11.ps_cb);
2173 ID3D11DeviceContext_PSSetSamplers(context, 0, 1, &engine->video_frame.d3d11.sampler);
2174 ID3D11DeviceContext_OMSetRenderTargets(context, 1, &rtv, NULL);
2176 ID3D11DeviceContext_Draw(context, 4, 0);
2178 ID3D11RenderTargetView_Release(rtv);
2179 ID3D11DeviceContext_Release(context);
2181 done:
2182 media_engine_unlock_d3d_device(engine, device);
2184 return hr;
2187 static HRESULT WINAPI media_engine_TransferVideoFrame(IMFMediaEngine *iface, IUnknown *surface,
2188 const MFVideoNormalizedRect *src_rect, const RECT *dst_rect, const MFARGB *color)
2190 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
2191 ID3D11Texture2D *texture;
2192 HRESULT hr = E_NOINTERFACE;
2194 TRACE("%p, %p, %s, %s, %p.\n", iface, surface, src_rect ? wine_dbg_sprintf("(%f,%f)-(%f,%f)",
2195 src_rect->left, src_rect->top, src_rect->right, src_rect->bottom) : "(null)",
2196 wine_dbgstr_rect(dst_rect), color);
2198 EnterCriticalSection(&engine->cs);
2200 if (SUCCEEDED(IUnknown_QueryInterface(surface, &IID_ID3D11Texture2D, (void **)&texture)))
2202 hr = media_engine_transfer_to_d3d11_texture(engine, texture, src_rect, dst_rect, color);
2203 ID3D11Texture2D_Release(texture);
2205 else
2207 FIXME("Unsupported destination type.\n");
2210 LeaveCriticalSection(&engine->cs);
2212 return hr;
2215 static HRESULT WINAPI media_engine_OnVideoStreamTick(IMFMediaEngine *iface, LONGLONG *pts)
2217 struct media_engine *engine = impl_from_IMFMediaEngine(iface);
2218 HRESULT hr;
2220 TRACE("%p, %p.\n", iface, pts);
2222 EnterCriticalSection(&engine->cs);
2224 if (engine->flags & FLAGS_ENGINE_SHUT_DOWN)
2225 hr = MF_E_SHUTDOWN;
2226 else if (!pts)
2227 hr = E_POINTER;
2228 else
2230 *pts = engine->video_frame.pts;
2231 hr = *pts == MINLONGLONG ? S_FALSE : S_OK;
2234 LeaveCriticalSection(&engine->cs);
2236 return hr;
2239 static const IMFMediaEngineVtbl media_engine_vtbl =
2241 media_engine_QueryInterface,
2242 media_engine_AddRef,
2243 media_engine_Release,
2244 media_engine_GetError,
2245 media_engine_SetErrorCode,
2246 media_engine_SetSourceElements,
2247 media_engine_SetSource,
2248 media_engine_GetCurrentSource,
2249 media_engine_GetNetworkState,
2250 media_engine_GetPreload,
2251 media_engine_SetPreload,
2252 media_engine_GetBuffered,
2253 media_engine_Load,
2254 media_engine_CanPlayType,
2255 media_engine_GetReadyState,
2256 media_engine_IsSeeking,
2257 media_engine_GetCurrentTime,
2258 media_engine_SetCurrentTime,
2259 media_engine_GetStartTime,
2260 media_engine_GetDuration,
2261 media_engine_IsPaused,
2262 media_engine_GetDefaultPlaybackRate,
2263 media_engine_SetDefaultPlaybackRate,
2264 media_engine_GetPlaybackRate,
2265 media_engine_SetPlaybackRate,
2266 media_engine_GetPlayed,
2267 media_engine_GetSeekable,
2268 media_engine_IsEnded,
2269 media_engine_GetAutoPlay,
2270 media_engine_SetAutoPlay,
2271 media_engine_GetLoop,
2272 media_engine_SetLoop,
2273 media_engine_Play,
2274 media_engine_Pause,
2275 media_engine_GetMuted,
2276 media_engine_SetMuted,
2277 media_engine_GetVolume,
2278 media_engine_SetVolume,
2279 media_engine_HasVideo,
2280 media_engine_HasAudio,
2281 media_engine_GetNativeVideoSize,
2282 media_engine_GetVideoAspectRatio,
2283 media_engine_Shutdown,
2284 media_engine_TransferVideoFrame,
2285 media_engine_OnVideoStreamTick,
2288 static HRESULT WINAPI media_engine_grabber_callback_QueryInterface(IMFSampleGrabberSinkCallback *iface,
2289 REFIID riid, void **obj)
2291 if (IsEqualIID(riid, &IID_IMFSampleGrabberSinkCallback) ||
2292 IsEqualIID(riid, &IID_IUnknown))
2294 *obj = iface;
2295 IMFSampleGrabberSinkCallback_AddRef(iface);
2296 return S_OK;
2299 *obj = NULL;
2300 return E_NOINTERFACE;
2303 static ULONG WINAPI media_engine_grabber_callback_AddRef(IMFSampleGrabberSinkCallback *iface)
2305 struct media_engine *engine = impl_from_IMFSampleGrabberSinkCallback(iface);
2306 return IMFMediaEngine_AddRef(&engine->IMFMediaEngine_iface);
2309 static ULONG WINAPI media_engine_grabber_callback_Release(IMFSampleGrabberSinkCallback *iface)
2311 struct media_engine *engine = impl_from_IMFSampleGrabberSinkCallback(iface);
2312 return IMFMediaEngine_Release(&engine->IMFMediaEngine_iface);
2315 static HRESULT WINAPI media_engine_grabber_callback_OnClockStart(IMFSampleGrabberSinkCallback *iface,
2316 MFTIME systime, LONGLONG start_offset)
2318 return S_OK;
2321 static HRESULT WINAPI media_engine_grabber_callback_OnClockStop(IMFSampleGrabberSinkCallback *iface,
2322 MFTIME systime)
2324 struct media_engine *engine = impl_from_IMFSampleGrabberSinkCallback(iface);
2326 EnterCriticalSection(&engine->cs);
2327 media_engine_set_flag(engine, FLAGS_ENGINE_FIRST_FRAME, FALSE);
2328 engine->video_frame.pts = MINLONGLONG;
2329 LeaveCriticalSection(&engine->cs);
2331 return S_OK;
2334 static HRESULT WINAPI media_engine_grabber_callback_OnClockPause(IMFSampleGrabberSinkCallback *iface,
2335 MFTIME systime)
2337 return S_OK;
2340 static HRESULT WINAPI media_engine_grabber_callback_OnClockRestart(IMFSampleGrabberSinkCallback *iface,
2341 MFTIME systime)
2343 return S_OK;
2346 static HRESULT WINAPI media_engine_grabber_callback_OnClockSetRate(IMFSampleGrabberSinkCallback *iface,
2347 MFTIME systime, float rate)
2349 return S_OK;
2352 static HRESULT WINAPI media_engine_grabber_callback_OnSetPresentationClock(IMFSampleGrabberSinkCallback *iface,
2353 IMFPresentationClock *clock)
2355 return S_OK;
2358 static HRESULT WINAPI media_engine_grabber_callback_OnProcessSample(IMFSampleGrabberSinkCallback *iface,
2359 REFGUID major_type, DWORD sample_flags, LONGLONG sample_time, LONGLONG sample_duration,
2360 const BYTE *buffer, DWORD buffer_size)
2362 struct media_engine *engine = impl_from_IMFSampleGrabberSinkCallback(iface);
2364 EnterCriticalSection(&engine->cs);
2366 if (!(engine->flags & FLAGS_ENGINE_FIRST_FRAME))
2368 IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_FIRSTFRAMEREADY, 0, 0);
2369 media_engine_set_flag(engine, FLAGS_ENGINE_FIRST_FRAME, TRUE);
2371 engine->video_frame.pts = sample_time;
2372 if (engine->video_frame.buffer_size < buffer_size)
2374 free(engine->video_frame.buffer);
2375 if ((engine->video_frame.buffer = malloc(buffer_size)))
2376 engine->video_frame.buffer_size = buffer_size;
2378 if (engine->video_frame.buffer)
2380 memcpy(engine->video_frame.buffer, buffer, buffer_size);
2381 engine->flags |= FLAGS_ENGINE_NEW_FRAME;
2384 LeaveCriticalSection(&engine->cs);
2386 return S_OK;
2389 static HRESULT WINAPI media_engine_grabber_callback_OnShutdown(IMFSampleGrabberSinkCallback *iface)
2391 return S_OK;
2394 static const IMFSampleGrabberSinkCallbackVtbl media_engine_grabber_callback_vtbl =
2396 media_engine_grabber_callback_QueryInterface,
2397 media_engine_grabber_callback_AddRef,
2398 media_engine_grabber_callback_Release,
2399 media_engine_grabber_callback_OnClockStart,
2400 media_engine_grabber_callback_OnClockStop,
2401 media_engine_grabber_callback_OnClockPause,
2402 media_engine_grabber_callback_OnClockRestart,
2403 media_engine_grabber_callback_OnClockSetRate,
2404 media_engine_grabber_callback_OnSetPresentationClock,
2405 media_engine_grabber_callback_OnProcessSample,
2406 media_engine_grabber_callback_OnShutdown,
2409 static HRESULT WINAPI media_engine_factory_QueryInterface(IMFMediaEngineClassFactory *iface, REFIID riid, void **obj)
2411 if (IsEqualIID(riid, &IID_IMFMediaEngineClassFactory) ||
2412 IsEqualIID(riid, &IID_IUnknown))
2414 *obj = iface;
2415 IMFMediaEngineClassFactory_AddRef(iface);
2416 return S_OK;
2419 WARN("Unsupported interface %s.\n", debugstr_guid(riid));
2420 *obj = NULL;
2421 return E_NOINTERFACE;
2424 static ULONG WINAPI media_engine_factory_AddRef(IMFMediaEngineClassFactory *iface)
2426 return 2;
2429 static ULONG WINAPI media_engine_factory_Release(IMFMediaEngineClassFactory *iface)
2431 return 1;
2434 static HRESULT init_media_engine(DWORD flags, IMFAttributes *attributes, struct media_engine *engine)
2436 DXGI_FORMAT output_format;
2437 UINT64 playback_hwnd;
2438 IMFClock *clock;
2439 HRESULT hr;
2441 engine->IMFMediaEngine_iface.lpVtbl = &media_engine_vtbl;
2442 engine->session_events.lpVtbl = &media_engine_session_events_vtbl;
2443 engine->load_handler.lpVtbl = &media_engine_load_handler_vtbl;
2444 engine->grabber_callback.lpVtbl = &media_engine_grabber_callback_vtbl;
2445 engine->refcount = 1;
2446 engine->flags = (flags & MF_MEDIA_ENGINE_CREATEFLAGS_MASK) | FLAGS_ENGINE_PAUSED;
2447 engine->default_playback_rate = 1.0;
2448 engine->playback_rate = 1.0;
2449 engine->volume = 1.0;
2450 engine->duration = NAN;
2451 engine->video_frame.pts = MINLONGLONG;
2452 InitializeCriticalSection(&engine->cs);
2454 hr = IMFAttributes_GetUnknown(attributes, &MF_MEDIA_ENGINE_CALLBACK, &IID_IMFMediaEngineNotify,
2455 (void **)&engine->callback);
2456 if (FAILED(hr))
2457 return hr;
2459 IMFAttributes_GetUnknown(attributes, &MF_MEDIA_ENGINE_DXGI_MANAGER, &IID_IMFDXGIDeviceManager,
2460 (void **)&engine->device_manager);
2462 if (FAILED(hr = MFCreateMediaSession(NULL, &engine->session)))
2463 return hr;
2465 if (FAILED(hr = IMFMediaSession_GetClock(engine->session, &clock)))
2466 return hr;
2468 hr = IMFClock_QueryInterface(clock, &IID_IMFPresentationClock, (void **)&engine->clock);
2469 IMFClock_Release(clock);
2470 if (FAILED(hr))
2471 return hr;
2473 if (FAILED(hr = IMFMediaSession_BeginGetEvent(engine->session, &engine->session_events, NULL)))
2474 return hr;
2476 if (FAILED(hr = MFCreateSourceResolver(&engine->resolver)))
2477 return hr;
2479 if (FAILED(hr = MFCreateAttributes(&engine->attributes, 0)))
2480 return hr;
2482 if (FAILED(hr = IMFAttributes_CopyAllItems(attributes, engine->attributes)))
2483 return hr;
2485 IMFAttributes_GetUINT64(attributes, &MF_MEDIA_ENGINE_PLAYBACK_HWND, &playback_hwnd);
2486 hr = IMFAttributes_GetUINT32(attributes, &MF_MEDIA_ENGINE_VIDEO_OUTPUT_FORMAT, &output_format);
2487 if (playback_hwnd) /* FIXME: handle MF_MEDIA_ENGINE_PLAYBACK_VISUAL */
2488 engine->mode = MEDIA_ENGINE_RENDERING_MODE;
2489 else
2491 if (SUCCEEDED(hr))
2492 engine->mode = MEDIA_ENGINE_FRAME_SERVER_MODE;
2493 else
2494 engine->mode = MEDIA_ENGINE_AUDIO_MODE;
2497 return S_OK;
2500 static HRESULT WINAPI media_engine_factory_CreateInstance(IMFMediaEngineClassFactory *iface, DWORD flags,
2501 IMFAttributes *attributes, IMFMediaEngine **engine)
2503 struct media_engine *object;
2504 HRESULT hr;
2506 TRACE("%p, %#x, %p, %p.\n", iface, flags, attributes, engine);
2508 if (!attributes || !engine)
2509 return E_POINTER;
2511 object = calloc(1, sizeof(*object));
2512 if (!object)
2513 return E_OUTOFMEMORY;
2515 hr = init_media_engine(flags, attributes, object);
2516 if (FAILED(hr))
2518 free_media_engine(object);
2519 return hr;
2522 *engine = &object->IMFMediaEngine_iface;
2524 return S_OK;
2527 static HRESULT WINAPI media_engine_factory_CreateTimeRange(IMFMediaEngineClassFactory *iface,
2528 IMFMediaTimeRange **range)
2530 TRACE("%p, %p.\n", iface, range);
2532 return create_time_range(range);
2535 static HRESULT WINAPI media_engine_factory_CreateError(IMFMediaEngineClassFactory *iface, IMFMediaError **error)
2537 TRACE("%p, %p.\n", iface, error);
2539 return create_media_error(error);
2542 static const IMFMediaEngineClassFactoryVtbl media_engine_factory_vtbl =
2544 media_engine_factory_QueryInterface,
2545 media_engine_factory_AddRef,
2546 media_engine_factory_Release,
2547 media_engine_factory_CreateInstance,
2548 media_engine_factory_CreateTimeRange,
2549 media_engine_factory_CreateError,
2552 static IMFMediaEngineClassFactory media_engine_factory = { &media_engine_factory_vtbl };
2554 static HRESULT WINAPI classfactory_QueryInterface(IClassFactory *iface, REFIID riid, void **obj)
2556 TRACE("%s, %p.\n", debugstr_guid(riid), obj);
2558 if (IsEqualGUID(riid, &IID_IClassFactory) ||
2559 IsEqualGUID(riid, &IID_IUnknown))
2561 IClassFactory_AddRef(iface);
2562 *obj = iface;
2563 return S_OK;
2566 WARN("interface %s not implemented.\n", debugstr_guid(riid));
2567 *obj = NULL;
2568 return E_NOINTERFACE;
2571 static ULONG WINAPI classfactory_AddRef(IClassFactory *iface)
2573 return 2;
2576 static ULONG WINAPI classfactory_Release(IClassFactory *iface)
2578 return 1;
2581 static HRESULT WINAPI classfactory_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID riid, void **obj)
2583 TRACE("%p, %s, %p.\n", outer, debugstr_guid(riid), obj);
2585 *obj = NULL;
2587 if (outer)
2588 return CLASS_E_NOAGGREGATION;
2590 return IMFMediaEngineClassFactory_QueryInterface(&media_engine_factory, riid, obj);
2593 static HRESULT WINAPI classfactory_LockServer(IClassFactory *iface, BOOL dolock)
2595 FIXME("(%d): stub.\n", dolock);
2596 return S_OK;
2599 static const IClassFactoryVtbl class_factory_vtbl =
2601 classfactory_QueryInterface,
2602 classfactory_AddRef,
2603 classfactory_Release,
2604 classfactory_CreateInstance,
2605 classfactory_LockServer,
2608 static IClassFactory classfactory = { &class_factory_vtbl };
2610 HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID riid, void **obj)
2612 TRACE("%s, %s, %p.\n", debugstr_guid(clsid), debugstr_guid(riid), obj);
2614 if (IsEqualGUID(clsid, &CLSID_MFMediaEngineClassFactory))
2615 return IClassFactory_QueryInterface(&classfactory, riid, obj);
2617 WARN("Unsupported class %s.\n", debugstr_guid(clsid));
2618 *obj = NULL;
2619 return CLASS_E_CLASSNOTAVAILABLE;