diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c index 05ee1070af6..83c8d34b92d 100644 --- a/dlls/mfplat/main.c +++ b/dlls/mfplat/main.c @@ -6713,3 +6713,260 @@ HRESULT WINAPI MFCreateSystemTimeSource(IMFPresentationTimeSource **time_source) return S_OK; } + +struct async_create_file +{ + IMFAsyncCallback IMFAsyncCallback_iface; + LONG refcount; + MF_FILE_ACCESSMODE access_mode; + MF_FILE_OPENMODE open_mode; + MF_FILE_FLAGS flags; + WCHAR *path; +}; + +struct async_create_file_result +{ + struct list entry; + IMFAsyncResult *result; + IMFByteStream *stream; +}; + +static struct list async_create_file_results = LIST_INIT(async_create_file_results); +static CRITICAL_SECTION async_create_file_cs = { NULL, -1, 0, 0, 0, 0 }; + +static struct async_create_file *impl_from_create_file_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct async_create_file, IMFAsyncCallback_iface); +} + +static HRESULT WINAPI async_create_file_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IMFAsyncCallback) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFAsyncCallback_AddRef(iface); + return S_OK; + } + + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI async_create_file_callback_AddRef(IMFAsyncCallback *iface) +{ + struct async_create_file *async = impl_from_create_file_IMFAsyncCallback(iface); + ULONG refcount = InterlockedIncrement(&async->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI async_create_file_callback_Release(IMFAsyncCallback *iface) +{ + struct async_create_file *async = impl_from_create_file_IMFAsyncCallback(iface); + ULONG refcount = InterlockedDecrement(&async->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + if (!refcount) + { + heap_free(async->path); + heap_free(async); + } + + return refcount; +} + +static HRESULT WINAPI async_create_file_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI async_create_file_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct async_create_file *async = impl_from_create_file_IMFAsyncCallback(iface); + IMFAsyncResult *caller; + IMFByteStream *stream; + HRESULT hr; + + caller = (IMFAsyncResult *)IMFAsyncResult_GetStateNoAddRef(result); + + hr = MFCreateFile(async->access_mode, async->open_mode, async->flags, async->path, &stream); + if (SUCCEEDED(hr)) + { + struct async_create_file_result *result_item; + + result_item = heap_alloc(sizeof(*result_item)); + if (result_item) + { + result_item->result = caller; + IMFAsyncResult_AddRef(caller); + result_item->stream = stream; + IMFByteStream_AddRef(stream); + + EnterCriticalSection(&async_create_file_cs); + list_add_tail(&async_create_file_results, &result_item->entry); + LeaveCriticalSection(&async_create_file_cs); + } + + IMFByteStream_Release(stream); + } + else + IMFAsyncResult_SetStatus(caller, hr); + + MFInvokeCallback(caller); + + return S_OK; +} + +static const IMFAsyncCallbackVtbl async_create_file_callback_vtbl = +{ + async_create_file_callback_QueryInterface, + async_create_file_callback_AddRef, + async_create_file_callback_Release, + async_create_file_callback_GetParameters, + async_create_file_callback_Invoke, +}; + +static WCHAR *heap_strdupW(const WCHAR *str) +{ + WCHAR *ret = NULL; + + if (str) + { + unsigned int size; + + size = (strlenW(str) + 1) * sizeof(WCHAR); + ret = heap_alloc(size); + if (ret) + memcpy(ret, str, size); + } + + return ret; +} + +/*********************************************************************** + * MFBeginCreateFile (mfplat.@) + */ +HRESULT WINAPI MFBeginCreateFile(MF_FILE_ACCESSMODE access_mode, MF_FILE_OPENMODE open_mode, MF_FILE_FLAGS flags, + const WCHAR *path, IMFAsyncCallback *callback, IUnknown *state, IUnknown **cancel_cookie) +{ + struct async_create_file *async = NULL; + IMFAsyncResult *caller, *item = NULL; + HRESULT hr; + + TRACE("%#x, %#x, %#x, %s, %p, %p, %p.\n", access_mode, open_mode, flags, debugstr_w(path), callback, state, + cancel_cookie); + + if (cancel_cookie) + *cancel_cookie = NULL; + + if (FAILED(hr = MFCreateAsyncResult(NULL, callback, state, &caller))) + return hr; + + async = heap_alloc(sizeof(*async)); + if (!async) + { + hr = E_OUTOFMEMORY; + goto failed; + } + + async->IMFAsyncCallback_iface.lpVtbl = &async_create_file_callback_vtbl; + async->refcount = 1; + async->access_mode = access_mode; + async->open_mode = open_mode; + async->flags = flags; + async->path = heap_strdupW(path); + if (!async->path) + { + hr = E_OUTOFMEMORY; + goto failed; + } + + hr = MFCreateAsyncResult(NULL, &async->IMFAsyncCallback_iface, (IUnknown *)caller, &item); + if (FAILED(hr)) + goto failed; + + if (cancel_cookie) + { + *cancel_cookie = (IUnknown *)caller; + IUnknown_AddRef(*cancel_cookie); + } + + hr = MFInvokeCallback(item); + +failed: + if (async) + IMFAsyncCallback_Release(&async->IMFAsyncCallback_iface); + if (item) + IMFAsyncResult_Release(item); + if (caller) + IMFAsyncResult_Release(caller); + + return hr; +} + +static HRESULT async_create_file_pull_result(IUnknown *unk, IMFByteStream **stream) +{ + struct async_create_file_result *item; + HRESULT hr = MF_E_UNEXPECTED; + IMFAsyncResult *result; + + *stream = NULL; + + if (FAILED(IUnknown_QueryInterface(unk, &IID_IMFAsyncResult, (void **)&result))) + return hr; + + EnterCriticalSection(&async_create_file_cs); + + LIST_FOR_EACH_ENTRY(item, &async_create_file_results, struct async_create_file_result, entry) + { + if (result == item->result) + { + *stream = item->stream; + IMFAsyncResult_Release(item->result); + list_remove(&item->entry); + heap_free(item); + break; + } + } + + LeaveCriticalSection(&async_create_file_cs); + + if (*stream) + hr = IMFAsyncResult_GetStatus(result); + + IMFAsyncResult_Release(result); + + return hr; +} + +/*********************************************************************** + * MFEndCreateFile (mfplat.@) + */ +HRESULT WINAPI MFEndCreateFile(IMFAsyncResult *result, IMFByteStream **stream) +{ + TRACE("%p, %p.\n", result, stream); + + return async_create_file_pull_result((IUnknown *)result, stream); +} + +/*********************************************************************** + * MFCancelCreateFile (mfplat.@) + */ +HRESULT WINAPI MFCancelCreateFile(IUnknown *cancel_cookie) +{ + IMFByteStream *stream = NULL; + HRESULT hr; + + TRACE("%p.\n", cancel_cookie); + + hr = async_create_file_pull_result(cancel_cookie, &stream); + + if (stream) + IMFByteStream_Release(stream); + + return hr; +} diff --git a/dlls/mfplat/mfplat.spec b/dlls/mfplat/mfplat.spec index 7823ac0d9e3..3ebbdbdd4b6 100644 --- a/dlls/mfplat/mfplat.spec +++ b/dlls/mfplat/mfplat.spec @@ -20,14 +20,14 @@ @ stdcall MFAllocateWorkQueueEx(long ptr) @ stub MFAppendCollection @ stub MFAverageTimePerFrameToFrameRate -@ stub MFBeginCreateFile +@ stdcall MFBeginCreateFile(long long long wstr ptr ptr ptr) @ stub MFBeginGetHostByName @ stub MFBeginRegisterWorkQueueWithMMCSS @ stub MFBeginUnregisterWorkQueueWithMMCSS @ stub MFBlockThread @ stub MFCalculateBitmapImageSize @ stdcall MFCalculateImageSize(ptr long long ptr) -@ stub MFCancelCreateFile +@ stdcall MFCancelCreateFile(ptr) @ stdcall MFCancelWorkItem(int64) @ stdcall MFCompareFullToPartialMediaType(ptr ptr) @ stub MFCompareSockaddrAddresses @@ -79,7 +79,7 @@ @ stub MFDeserializeEvent @ stub MFDeserializeMediaTypeFromStream @ stub MFDeserializePresentationDescriptor -@ stub MFEndCreateFile +@ stdcall MFEndCreateFile(ptr ptr) @ stub MFEndGetHostByName @ stub MFEndRegisterWorkQueueWithMMCSS @ stub MFEndUnregisterWorkQueueWithMMCSS diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index b349c51af39..d2ae8f2a857 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -2886,6 +2886,72 @@ static void test_MFCreateWaveFormatExFromMFMediaType(void) IMFMediaType_Release(mediatype); } +static HRESULT WINAPI test_create_file_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct test_callback *callback = impl_from_IMFAsyncCallback(iface); + IMFByteStream *stream; + IUnknown *object; + HRESULT hr; + + ok(!!result, "Unexpected result object.\n"); + + ok((IUnknown *)iface == IMFAsyncResult_GetStateNoAddRef(result), "Unexpected result state.\n"); + + hr = IMFAsyncResult_GetObject(result, &object); + ok(hr == E_POINTER, "Unexpected hr %#x.\n", hr); + + hr = MFEndCreateFile(result, &stream); + ok(hr == S_OK, "Failed to get file stream, hr %#x.\n", hr); + IMFByteStream_Release(stream); + + SetEvent(callback->event); + + return S_OK; +} + +static const IMFAsyncCallbackVtbl test_create_file_callback_vtbl = +{ + testcallback_QueryInterface, + testcallback_AddRef, + testcallback_Release, + testcallback_GetParameters, + test_create_file_callback_Invoke, +}; + +static void test_async_create_file(void) +{ + struct test_callback callback = { { &test_create_file_callback_vtbl } }; + WCHAR pathW[MAX_PATH], fileW[MAX_PATH]; + IUnknown *cancel_cookie; + HRESULT hr; + BOOL ret; + + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "Fail to start up, hr %#x.\n", hr); + + callback.event = CreateEventA(NULL, FALSE, FALSE, NULL); + + GetTempPathW(ARRAY_SIZE(pathW), pathW); + GetTempFileNameW(pathW, NULL, 0, fileW); + + hr = MFBeginCreateFile(MF_ACCESSMODE_READWRITE, MF_OPENMODE_DELETE_IF_EXIST, MF_FILEFLAGS_NONE, fileW, + &callback.IMFAsyncCallback_iface, (IUnknown *)&callback.IMFAsyncCallback_iface, &cancel_cookie); + ok(hr == S_OK, "Async create request failed, hr %#x.\n", hr); + ok(cancel_cookie != NULL, "Unexpected cancellation object.\n"); + + WaitForSingleObject(callback.event, INFINITE); + + IUnknown_Release(cancel_cookie); + + CloseHandle(callback.event); + + hr = MFShutdown(); + ok(hr == S_OK, "Failed to shut down, hr %#x.\n", hr); + + ret = DeleteFileW(fileW); + ok(ret, "Failed to delete test file.\n"); +} + START_TEST(mfplat) { CoInitialize(NULL); @@ -2920,6 +2986,7 @@ START_TEST(mfplat) test_attributes_serialization(); test_wrapped_media_type(); test_MFCreateWaveFormatExFromMFMediaType(); + test_async_create_file(); CoUninitialize(); } diff --git a/include/mfapi.h b/include/mfapi.h index cee9d403c49..d172db147a7 100644 --- a/include/mfapi.h +++ b/include/mfapi.h @@ -353,7 +353,10 @@ typedef enum _MFWaveFormatExConvertFlags HRESULT WINAPI MFAddPeriodicCallback(MFPERIODICCALLBACK callback, IUnknown *context, DWORD *key); HRESULT WINAPI MFAllocateWorkQueue(DWORD *queue); HRESULT WINAPI MFAllocateWorkQueueEx(MFASYNC_WORKQUEUE_TYPE queue_type, DWORD *queue); +HRESULT WINAPI MFBeginCreateFile(MF_FILE_ACCESSMODE access_mode, MF_FILE_OPENMODE open_mode, MF_FILE_FLAGS flags, + const WCHAR *path, IMFAsyncCallback *callback, IUnknown *state, IUnknown **cancel_cookie); HRESULT WINAPI MFCalculateImageSize(REFGUID subtype, UINT32 width, UINT32 height, UINT32 *size); +HRESULT WINAPI MFCancelCreateFile(IUnknown *cancel_cookie); HRESULT WINAPI MFCancelWorkItem(MFWORKITEM_KEY key); BOOL WINAPI MFCompareFullToPartialMediaType(IMFMediaType *full_type, IMFMediaType *partial_type); HRESULT WINAPI MFCopyImage(BYTE *dest, LONG deststride, const BYTE *src, LONG srcstride, DWORD width, DWORD lines); @@ -370,6 +373,7 @@ HRESULT WINAPI MFCreateMediaType(IMFMediaType **type); HRESULT WINAPI MFCreateSample(IMFSample **sample); HRESULT WINAPI MFCreateMemoryBuffer(DWORD max_length, IMFMediaBuffer **buffer); HRESULT WINAPI MFCreateWaveFormatExFromMFMediaType(IMFMediaType *type, WAVEFORMATEX **format, UINT32 *size, UINT32 flags); +HRESULT WINAPI MFEndCreateFile(IMFAsyncResult *result, IMFByteStream **stream); void * WINAPI MFHeapAlloc(SIZE_T size, ULONG flags, char *file, int line, EAllocationType type); void WINAPI MFHeapFree(void *ptr); HRESULT WINAPI MFGetAttributesAsBlob(IMFAttributes *attributes, UINT8 *buffer, UINT size);