2 * Copyright 2022 Nikolay Sivov 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
28 #include "mf_private.h"
30 #include "wine/debug.h"
31 #include "wine/list.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(mfplat
);
35 typedef HRESULT (*create_stream_cb
)(const WCHAR
*url
, DWORD flags
, IMFByteStream
**out
);
37 struct scheme_handler_result
40 IMFAsyncResult
*result
;
41 MF_OBJECT_TYPE obj_type
;
47 IMFSchemeHandler IMFSchemeHandler_iface
;
48 IMFAsyncCallback IMFAsyncCallback_iface
;
50 IMFSourceResolver
*resolver
;
53 create_stream_cb create_stream
;
56 static struct scheme_handler
*impl_from_IMFSchemeHandler(IMFSchemeHandler
*iface
)
58 return CONTAINING_RECORD(iface
, struct scheme_handler
, IMFSchemeHandler_iface
);
61 static struct scheme_handler
*impl_from_IMFAsyncCallback(IMFAsyncCallback
*iface
)
63 return CONTAINING_RECORD(iface
, struct scheme_handler
, IMFAsyncCallback_iface
);
66 static HRESULT WINAPI
scheme_handler_QueryInterface(IMFSchemeHandler
*iface
, REFIID riid
, void **obj
)
68 TRACE("%p, %s, %p.\n", iface
, debugstr_guid(riid
), obj
);
70 if (IsEqualIID(riid
, &IID_IMFSchemeHandler
) ||
71 IsEqualIID(riid
, &IID_IUnknown
))
74 IMFSchemeHandler_AddRef(iface
);
78 WARN("Unsupported %s.\n", debugstr_guid(riid
));
83 static ULONG WINAPI
scheme_handler_AddRef(IMFSchemeHandler
*iface
)
85 struct scheme_handler
*handler
= impl_from_IMFSchemeHandler(iface
);
86 ULONG refcount
= InterlockedIncrement(&handler
->refcount
);
88 TRACE("%p, refcount %lu.\n", handler
, refcount
);
93 static ULONG WINAPI
scheme_handler_Release(IMFSchemeHandler
*iface
)
95 struct scheme_handler
*handler
= impl_from_IMFSchemeHandler(iface
);
96 ULONG refcount
= InterlockedDecrement(&handler
->refcount
);
97 struct scheme_handler_result
*result
, *next
;
99 TRACE("%p, refcount %lu.\n", iface
, refcount
);
103 LIST_FOR_EACH_ENTRY_SAFE(result
, next
, &handler
->results
, struct scheme_handler_result
, entry
)
105 list_remove(&result
->entry
);
106 IMFAsyncResult_Release(result
->result
);
108 IUnknown_Release(result
->object
);
111 DeleteCriticalSection(&handler
->cs
);
112 if (handler
->resolver
)
113 IMFSourceResolver_Release(handler
->resolver
);
120 struct create_object_context
122 IUnknown IUnknown_iface
;
125 IPropertyStore
*props
;
130 static struct create_object_context
*impl_from_IUnknown(IUnknown
*iface
)
132 return CONTAINING_RECORD(iface
, struct create_object_context
, IUnknown_iface
);
135 static HRESULT WINAPI
create_object_context_QueryInterface(IUnknown
*iface
, REFIID riid
, void **obj
)
137 TRACE("%p, %s, %p.\n", iface
, debugstr_guid(riid
), obj
);
139 if (IsEqualIID(riid
, &IID_IUnknown
))
142 IUnknown_AddRef(iface
);
146 WARN("Unsupported %s.\n", debugstr_guid(riid
));
148 return E_NOINTERFACE
;
151 static ULONG WINAPI
create_object_context_AddRef(IUnknown
*iface
)
153 struct create_object_context
*context
= impl_from_IUnknown(iface
);
154 ULONG refcount
= InterlockedIncrement(&context
->refcount
);
156 TRACE("%p, refcount %lu.\n", iface
, refcount
);
161 static ULONG WINAPI
create_object_context_Release(IUnknown
*iface
)
163 struct create_object_context
*context
= impl_from_IUnknown(iface
);
164 ULONG refcount
= InterlockedDecrement(&context
->refcount
);
166 TRACE("%p, refcount %lu.\n", iface
, refcount
);
171 IPropertyStore_Release(context
->props
);
179 static const IUnknownVtbl create_object_context_vtbl
=
181 create_object_context_QueryInterface
,
182 create_object_context_AddRef
,
183 create_object_context_Release
,
186 static HRESULT WINAPI
scheme_handler_BeginCreateObject(IMFSchemeHandler
*iface
, const WCHAR
*url
, DWORD flags
,
187 IPropertyStore
*props
, IUnknown
**cancel_cookie
, IMFAsyncCallback
*callback
, IUnknown
*state
)
189 struct scheme_handler
*handler
= impl_from_IMFSchemeHandler(iface
);
190 struct create_object_context
*context
;
191 IMFAsyncResult
*caller
, *item
;
194 TRACE("%p, %s, %#lx, %p, %p, %p, %p.\n", iface
, debugstr_w(url
), flags
, props
, cancel_cookie
, callback
, state
);
197 *cancel_cookie
= NULL
;
199 if (FAILED(hr
= MFCreateAsyncResult(NULL
, callback
, state
, &caller
)))
202 if (!(context
= malloc(sizeof(*context
))))
204 IMFAsyncResult_Release(caller
);
205 return E_OUTOFMEMORY
;
208 context
->IUnknown_iface
.lpVtbl
= &create_object_context_vtbl
;
209 context
->refcount
= 1;
210 context
->props
= props
;
212 IPropertyStore_AddRef(context
->props
);
213 context
->flags
= flags
;
214 context
->url
= wcsdup(url
);
217 IMFAsyncResult_Release(caller
);
218 IUnknown_Release(&context
->IUnknown_iface
);
219 return E_OUTOFMEMORY
;
222 hr
= MFCreateAsyncResult(&context
->IUnknown_iface
, &handler
->IMFAsyncCallback_iface
, (IUnknown
*)caller
, &item
);
223 IUnknown_Release(&context
->IUnknown_iface
);
226 if (SUCCEEDED(hr
= MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_IO
, item
)))
230 *cancel_cookie
= (IUnknown
*)caller
;
231 IUnknown_AddRef(*cancel_cookie
);
235 IMFAsyncResult_Release(item
);
237 IMFAsyncResult_Release(caller
);
242 static HRESULT WINAPI
scheme_handler_EndCreateObject(IMFSchemeHandler
*iface
, IMFAsyncResult
*result
,
243 MF_OBJECT_TYPE
*obj_type
, IUnknown
**object
)
245 struct scheme_handler
*handler
= impl_from_IMFSchemeHandler(iface
);
246 struct scheme_handler_result
*found
= NULL
, *cur
;
249 TRACE("%p, %p, %p, %p.\n", iface
, result
, obj_type
, object
);
251 EnterCriticalSection(&handler
->cs
);
253 LIST_FOR_EACH_ENTRY(cur
, &handler
->results
, struct scheme_handler_result
, entry
)
255 if (result
== cur
->result
)
257 list_remove(&cur
->entry
);
263 LeaveCriticalSection(&handler
->cs
);
267 *obj_type
= found
->obj_type
;
268 *object
= found
->object
;
269 hr
= IMFAsyncResult_GetStatus(found
->result
);
270 IMFAsyncResult_Release(found
->result
);
275 *obj_type
= MF_OBJECT_INVALID
;
277 hr
= MF_E_UNEXPECTED
;
283 static HRESULT WINAPI
scheme_handler_CancelObjectCreation(IMFSchemeHandler
*iface
, IUnknown
*cancel_cookie
)
285 struct scheme_handler
*handler
= impl_from_IMFSchemeHandler(iface
);
286 struct scheme_handler_result
*found
= NULL
, *cur
;
288 TRACE("%p, %p.\n", iface
, cancel_cookie
);
290 EnterCriticalSection(&handler
->cs
);
292 LIST_FOR_EACH_ENTRY(cur
, &handler
->results
, struct scheme_handler_result
, entry
)
294 if (cancel_cookie
== (IUnknown
*)cur
->result
)
296 list_remove(&cur
->entry
);
302 LeaveCriticalSection(&handler
->cs
);
306 IMFAsyncResult_Release(found
->result
);
308 IUnknown_Release(found
->object
);
312 return found
? S_OK
: MF_E_UNEXPECTED
;
315 static const IMFSchemeHandlerVtbl scheme_handler_vtbl
=
317 scheme_handler_QueryInterface
,
318 scheme_handler_AddRef
,
319 scheme_handler_Release
,
320 scheme_handler_BeginCreateObject
,
321 scheme_handler_EndCreateObject
,
322 scheme_handler_CancelObjectCreation
,
325 static HRESULT WINAPI
scheme_handler_callback_QueryInterface(IMFAsyncCallback
*iface
, REFIID riid
, void **obj
)
327 if (IsEqualIID(riid
, &IID_IMFAsyncCallback
) ||
328 IsEqualIID(riid
, &IID_IUnknown
))
331 IMFAsyncCallback_AddRef(iface
);
335 WARN("Unsupported %s.\n", debugstr_guid(riid
));
337 return E_NOINTERFACE
;
340 static ULONG WINAPI
scheme_handler_callback_AddRef(IMFAsyncCallback
*iface
)
342 struct scheme_handler
*handler
= impl_from_IMFAsyncCallback(iface
);
343 return IMFSchemeHandler_AddRef(&handler
->IMFSchemeHandler_iface
);
346 static ULONG WINAPI
scheme_handler_callback_Release(IMFAsyncCallback
*iface
)
348 struct scheme_handler
*handler
= impl_from_IMFAsyncCallback(iface
);
349 return IMFSchemeHandler_Release(&handler
->IMFSchemeHandler_iface
);
352 static HRESULT WINAPI
scheme_handler_callback_GetParameters(IMFAsyncCallback
*iface
, DWORD
*flags
, DWORD
*queue
)
357 static HRESULT
scheme_handler_get_resolver(struct scheme_handler
*handler
, IMFSourceResolver
**resolver
)
361 if (!handler
->resolver
)
363 IMFSourceResolver
*resolver
;
365 if (FAILED(hr
= MFCreateSourceResolver(&resolver
)))
368 if (InterlockedCompareExchangePointer((void **)&handler
->resolver
, resolver
, NULL
))
369 IMFSourceResolver_Release(resolver
);
372 *resolver
= handler
->resolver
;
373 IMFSourceResolver_AddRef(*resolver
);
378 static HRESULT WINAPI
scheme_handler_callback_Invoke(IMFAsyncCallback
*iface
, IMFAsyncResult
*result
)
380 struct scheme_handler
*handler
= impl_from_IMFAsyncCallback(iface
);
381 struct scheme_handler_result
*handler_result
;
382 MF_OBJECT_TYPE obj_type
= MF_OBJECT_INVALID
;
383 IUnknown
*object
= NULL
, *context_object
;
384 struct create_object_context
*context
;
385 IMFSourceResolver
*resolver
;
386 IMFAsyncResult
*caller
;
387 IMFByteStream
*stream
;
390 caller
= (IMFAsyncResult
*)IMFAsyncResult_GetStateNoAddRef(result
);
392 if (FAILED(hr
= IMFAsyncResult_GetObject(result
, &context_object
)))
394 WARN("Expected context set for callee result.\n");
398 context
= impl_from_IUnknown(context_object
);
400 if (SUCCEEDED(hr
= handler
->create_stream(context
->url
, context
->flags
, &stream
)))
402 if (context
->flags
& MF_RESOLUTION_MEDIASOURCE
)
404 if (SUCCEEDED(hr
= scheme_handler_get_resolver(handler
, &resolver
)))
406 hr
= IMFSourceResolver_CreateObjectFromByteStream(resolver
, stream
, context
->url
, context
->flags
,
407 context
->props
, &obj_type
, &object
);
408 IMFSourceResolver_Release(resolver
);
409 IMFByteStream_Release(stream
);
414 object
= (IUnknown
*)stream
;
415 obj_type
= MF_OBJECT_BYTESTREAM
;
419 handler_result
= malloc(sizeof(*handler_result
));
422 handler_result
->result
= caller
;
423 IMFAsyncResult_AddRef(handler_result
->result
);
424 handler_result
->obj_type
= obj_type
;
425 handler_result
->object
= object
;
427 EnterCriticalSection(&handler
->cs
);
428 list_add_tail(&handler
->results
, &handler_result
->entry
);
429 LeaveCriticalSection(&handler
->cs
);
434 IUnknown_Release(object
);
438 IUnknown_Release(&context
->IUnknown_iface
);
440 IMFAsyncResult_SetStatus(caller
, hr
);
441 MFInvokeCallback(caller
);
446 static const IMFAsyncCallbackVtbl scheme_handler_callback_vtbl
=
448 scheme_handler_callback_QueryInterface
,
449 scheme_handler_callback_AddRef
,
450 scheme_handler_callback_Release
,
451 scheme_handler_callback_GetParameters
,
452 scheme_handler_callback_Invoke
,
455 static HRESULT
file_stream_create(const WCHAR
*url
, DWORD flags
, IMFByteStream
**out
)
457 if (!wcsnicmp(url
, L
"file://", 7))
459 return MFCreateFile(flags
& MF_RESOLUTION_WRITE
? MF_ACCESSMODE_READWRITE
: MF_ACCESSMODE_READ
,
460 MF_OPENMODE_FAIL_IF_NOT_EXIST
, MF_FILEFLAGS_NONE
, url
, out
);
463 HRESULT
file_scheme_handler_construct(REFIID riid
, void **obj
)
465 struct scheme_handler
*handler
;
468 TRACE("%s, %p.\n", debugstr_guid(riid
), obj
);
470 if (!(handler
= calloc(1, sizeof(*handler
))))
471 return E_OUTOFMEMORY
;
473 handler
->IMFSchemeHandler_iface
.lpVtbl
= &scheme_handler_vtbl
;
474 handler
->IMFAsyncCallback_iface
.lpVtbl
= &scheme_handler_callback_vtbl
;
475 handler
->refcount
= 1;
476 list_init(&handler
->results
);
477 InitializeCriticalSection(&handler
->cs
);
478 handler
->create_stream
= file_stream_create
;
480 hr
= IMFSchemeHandler_QueryInterface(&handler
->IMFSchemeHandler_iface
, riid
, obj
);
481 IMFSchemeHandler_Release(&handler
->IMFSchemeHandler_iface
);
486 static HRESULT
urlmon_stream_create(const WCHAR
*url
, DWORD flags
, IMFByteStream
**out
)
488 IMFAttributes
*attributes
;
492 if (flags
& MF_RESOLUTION_WRITE
)
495 if (FAILED(hr
= URLOpenBlockingStreamW(NULL
, url
, &stream
, 0, NULL
)))
497 WARN("Failed to open url %s, hr %#lx\n", debugstr_w(url
), hr
);
501 hr
= MFCreateMFByteStreamOnStream(stream
, out
);
502 IStream_Release(stream
);
506 IMFByteStream_QueryInterface(*out
, &IID_IMFAttributes
, (void **)&attributes
);
507 IMFAttributes_DeleteItem(attributes
, &MF_BYTESTREAM_ORIGIN_NAME
);
508 IMFAttributes_SetString(attributes
, &MF_BYTESTREAM_EFFECTIVE_URL
, url
);
509 IMFAttributes_SetString(attributes
, &MF_BYTESTREAM_CONTENT_TYPE
, L
"application/octet-stream");
510 IMFAttributes_Release(attributes
);
514 HRESULT
urlmon_scheme_handler_construct(REFIID riid
, void **obj
)
516 struct scheme_handler
*handler
;
519 TRACE("%s, %p.\n", debugstr_guid(riid
), obj
);
521 if (!(handler
= calloc(1, sizeof(*handler
))))
522 return E_OUTOFMEMORY
;
524 handler
->IMFSchemeHandler_iface
.lpVtbl
= &scheme_handler_vtbl
;
525 handler
->IMFAsyncCallback_iface
.lpVtbl
= &scheme_handler_callback_vtbl
;
526 handler
->refcount
= 1;
527 list_init(&handler
->results
);
528 InitializeCriticalSection(&handler
->cs
);
529 handler
->create_stream
= urlmon_stream_create
;
531 hr
= IMFSchemeHandler_QueryInterface(&handler
->IMFSchemeHandler_iface
, riid
, obj
);
532 IMFSchemeHandler_Release(&handler
->IMFSchemeHandler_iface
);