This has been tested with VRChat in Proton, and with one change to `IMFMediaEngine` and some small changes to winegstreamer it is able to play YouTube videos in Unity video player based players. The AVPro based players have many more issues, because that API uses YouTube's HLS files instead of the direct MP4 links, and new media source would need many fixes to be able to play HLS correctly. Which it isn't supposed to do anyway since that's the `IMFMediaEngine`'s job.
There are some things that I did not implement: - `IMFByteStreamBuffering::SetBufferingParams`, the parameters don't have any obvious effect on the functionality of the byte stream or the HTTP requests it makes. It's probably not used by anything anyway. - How much data has to be pre-buffered before it considers buffering to be complete and sends `MEBufferingStopped`. I didn't put much time into this and just chose 64 KiB. - Persistent caching of downloaded data; Native seems to be able to omit redownloading data it has previously downloaded before by using the `If-Modified-Since` header. This seems like more work and unnecessary complexity than it's worth. - Mapping of all the WinHTTP errors to HRESULTs; HTTP status codes are handled, as well as the most common `ERROR_WINHTTP_*` values, but anything else just goes through `HRESULT_FROM_WIN32`. I don't even know how to trigger most of the WinHTTP errors; It's probably fine, the most important info is just that there has been an error anyway.
From: Torge Matthies tmatthies@codeweavers.com
--- include/mfidl.idl | 1 + 1 file changed, 1 insertion(+)
diff --git a/include/mfidl.idl b/include/mfidl.idl index 666af53c6e3..a8c5af05e68 100644 --- a/include/mfidl.idl +++ b/include/mfidl.idl @@ -1626,6 +1626,7 @@ cpp_quote("EXTERN_GUID(MR_STREAM_VOLUME_SERVICE, 0xf8b5fa2f, 0x32ef, 0x46f5, 0xb cpp_quote("EXTERN_GUID(MR_AUDIO_POLICY_SERVICE, 0x911fd737, 0x6775, 0x4ab0, 0xa6, 0x14, 0x29, 0x78, 0x62, 0xfd, 0xac, 0x88);") cpp_quote("EXTERN_GUID(MF_PROPERTY_HANDLER_SERVICE, 0xa3face02, 0x32b8, 0x41dd, 0x90, 0xe7, 0x5f, 0xef, 0x7c, 0x89, 0x91, 0xb5);") cpp_quote("EXTERN_GUID(MF_WORKQUEUE_SERVICES, 0x8e37d489, 0x41e0, 0x413a, 0x90, 0x68, 0x28, 0x7c, 0x88, 0x6d, 0x8d, 0xda);") +cpp_quote("EXTERN_GUID(MFNETSOURCE_STATISTICS_SERVICE, 0x3cb1f275, 0x0505, 0x4c5d, 0xae, 0x71, 0x0a, 0x55, 0x63, 0x44, 0xef, 0xa1);")
cpp_quote("EXTERN_GUID(MF_PROGRESSIVE_CODING_CONTENT, 0x8f020eea, 0x1508, 0x471f, 0x9d, 0xa6, 0x50, 0x7d, 0x7c, 0xfa, 0x40, 0xdb);") cpp_quote("EXTERN_GUID(MF_NALU_LENGTH_SET, 0xa7911d53, 0x12a4, 0x4965, 0xae, 0x70, 0x6e, 0xad, 0xd6, 0xff, 0x05, 0x51);")
From: Torge Matthies tmatthies@codeweavers.com
--- dlls/mf/tests/mf.c | 347 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 347 insertions(+)
diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index 09830191363..e8ae6701466 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -4722,6 +4722,7 @@ static void test_evr(void)
hr = IMFActivate_ActivateObject(activate, &IID_IMFMediaSink, (void **)&sink); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (!sink) return;
check_interface(sink, &IID_IMFMediaSinkPreroll, TRUE); check_interface(sink, &IID_IMFVideoRenderer, TRUE); @@ -6623,6 +6624,351 @@ static void test_media_session_Close(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); }
+static void test_network_bytestream(void) +{ + static const WCHAR *URL = L"http://test.winehq.org/tests/test.mp3"; + static const WCHAR *EFFECTIVE_URL = L"http://test.winehq.org:80/tests/test.mp3"; + static const WCHAR *CONTENT_TYPE = L"audio/mpeg"; + static const BYTE LAST_MODIFIED_TIME[] = { 0x00, 0x3b, 0x4b, 0xbf, 0x05, 0x80, 0xd8, 0x01 }; + + IMFSourceResolver *resolver; + IUnknown *object = NULL, *bs = NULL; + MF_OBJECT_TYPE obj_type; + HRESULT hr; + void *ptr; + + hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); + ok(hr == S_OK, "Startup failure, hr %#lx.\n", hr); + + hr = MFCreateSourceResolver(&resolver); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + if (object) IUnknown_Release(object); + + obj_type = (MF_OBJECT_TYPE)0xdeadbeef; + object = NULL; + hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"http://nonexistent.url/file.mp4", MF_RESOLUTION_BYTESTREAM, NULL, &obj_type, &object); + todo_wine + ok(hr == NS_E_SERVER_NOT_FOUND, "Got hr %#lx.\n", hr); + ok(obj_type == MF_OBJECT_INVALID, "Unexpected obj_type %#x.\n", obj_type); + if (object) IUnknown_Release(object); + + obj_type = (MF_OBJECT_TYPE)0xdeadbeef; + object = NULL; + hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"http://test.winehq.org/tests/invalid.mp3", MF_RESOLUTION_BYTESTREAM, NULL, &obj_type, &object); + todo_wine + ok(hr == NS_E_FILE_NOT_FOUND, "Got hr %#lx.\n", hr); + todo_wine + ok(obj_type == MF_OBJECT_INVALID, "Unexpected obj_type %#x.\n", obj_type); + if (object) IUnknown_Release(object); + + obj_type = (MF_OBJECT_TYPE)0xdeadbeef; + object = NULL; + hr = IMFSourceResolver_CreateObjectFromURL(resolver, URL, MF_RESOLUTION_BYTESTREAM, NULL, &obj_type, &object); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(obj_type == MF_OBJECT_BYTESTREAM, "Unexpected obj_type %#x.\n", obj_type); + + ptr = NULL; + hr = IUnknown_QueryInterface(object, &IID_IMFAttributes, &ptr); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(ptr != NULL, "Got NULL ptr.\n"); + if (SUCCEEDED(hr) && ptr) + { + IMFAttributes *attr = ptr; + UINT32 count = 0; + PROPVARIANT var; + GUID key = {0}; + + hr = IMFAttributes_GetCount(attr, &count); + ok(hr == S_OK, "Got hr %#lx\n", hr); + todo_wine + ok(count == 3, "count = %u\n", count); + + PropVariantInit(&var); + + hr = IMFAttributes_GetItemByIndex(attr, 0, &key, &var); + ok(hr == S_OK, "Got hr %#lx\n", hr); + ok(IsEqualGUID(&key, &MF_BYTESTREAM_EFFECTIVE_URL), "Got key %s\n", debugstr_guid(&key)); + ok(var.vt == VT_LPWSTR, "Got type %d\n", var.vt); + todo_wine + ok(!lstrcmpW(var.pwszVal, EFFECTIVE_URL), "Got value %s\n", var.pszVal); + memset(&key, 0, sizeof(key)); + PropVariantClear(&var); + + hr = IMFAttributes_GetItemByIndex(attr, 1, &key, &var); + ok(hr == S_OK, "Got hr %#lx\n", hr); + ok(IsEqualGUID(&key, &MF_BYTESTREAM_CONTENT_TYPE), "Got key %s\n", debugstr_guid(&key)); + ok(var.vt == VT_LPWSTR, "Got type %d\n", var.vt); + todo_wine + ok(!lstrcmpW(var.pwszVal, CONTENT_TYPE), "Got value %s\n", var.pszVal); + memset(&key, 0, sizeof(key)); + PropVariantClear(&var); + + hr = IMFAttributes_GetItemByIndex(attr, 2, &key, &var); + todo_wine + ok(hr == S_OK, "Got hr %#lx\n", hr); + todo_wine + ok(IsEqualGUID(&key, &MF_BYTESTREAM_LAST_MODIFIED_TIME), "Got key %s\n", debugstr_guid(&key)); + todo_wine + ok(var.vt == (VT_VECTOR | VT_I1 | VT_NULL), "Got type %d\n", var.vt); + todo_wine + ok(var.blob.cbSize == sizeof(LAST_MODIFIED_TIME), "Got size %lu\n", var.blob.cbSize); + todo_wine + ok(var.blob.pBlobData != NULL, "Got NULL value\n"); + if (var.blob.cbSize == sizeof(LAST_MODIFIED_TIME) && var.blob.pBlobData) + ok(!memcmp(var.blob.pBlobData, LAST_MODIFIED_TIME, sizeof(LAST_MODIFIED_TIME)), "Got wrong value\n"); + memset(&key, 0, sizeof(key)); + PropVariantClear(&var); + + hr = IMFAttributes_GetItemByIndex(attr, 3, &key, &var); + ok(hr == E_INVALIDARG, "Got hr %#lx\n", hr); + ok(IsEqualGUID(&key, &GUID_NULL), "Got key %s\n", debugstr_guid(&key)); + ok(var.vt == VT_EMPTY, "Got type %d\n", var.vt); + memset(&key, 0, sizeof(key)); + PropVariantClear(&var); + + IUnknown_Release((IUnknown *)ptr); + } + + ptr = NULL; + hr = IUnknown_QueryInterface(object, &IID_IMFByteStreamCacheControl, &ptr); + todo_wine + ok(hr == S_OK, "Got hr %#lx.\n", hr); + todo_wine + ok(ptr != NULL, "Got NULL ptr.\n"); + if (SUCCEEDED(hr) && ptr) + { + IMFByteStreamCacheControl *ctrl = ptr; + HRESULT hr; + + hr = IMFByteStreamCacheControl_StopBackgroundTransfer(ctrl); + ok(hr == S_OK, "Got hr %#lx\n", hr); + + IMFByteStreamCacheControl_Release(ctrl); + } + + ptr = NULL; + hr = IUnknown_QueryInterface(object, &IID_IMFByteStreamBuffering, &ptr); + todo_wine + ok(hr == S_OK, "Got hr %#lx.\n", hr); + todo_wine + ok(ptr != NULL, "Got NULL ptr.\n"); + if (SUCCEEDED(hr) && ptr) + { + MFBYTESTREAM_BUFFERING_PARAMS params = {0}; + IMFByteStreamBuffering *buffering = ptr; + MF_LEAKY_BUCKET_PAIR bucket = {0}; + HRESULT hr; + + hr = IMFByteStreamBuffering_StopBuffering(buffering); + ok(hr == S_FALSE, "Got hr %#lx\n", hr); + + hr = IMFByteStreamBuffering_EnableBuffering(buffering, FALSE); + ok(hr == S_OK, "Got hr %#lx\n", hr); + + hr = IMFByteStreamBuffering_EnableBuffering(buffering, TRUE); + ok(hr == S_OK, "Got hr %#lx\n", hr); + + hr = IMFByteStreamBuffering_StopBuffering(buffering); + ok(hr == S_OK || hr == S_FALSE, "Got hr %#lx\n", hr); + + hr = IMFByteStreamBuffering_SetBufferingParams(buffering, NULL); + ok(hr == E_INVALIDARG, "Got hr %#lx\n", hr); + + params.cbTotalFileSize = -1; + params.cbPlayableDataSize = -1; + params.prgBuckets = NULL; + params.cBuckets = 0; + params.qwNetBufferingTime = 0; + params.qwExtraBufferingTimeDuringSeek = 0; + params.qwPlayDuration = 0; + params.dRate = 1.0f; + hr = IMFByteStreamBuffering_SetBufferingParams(buffering, ¶ms); + todo_wine + ok(hr == S_OK, "Got hr %#lx\n", hr); + + params.cBuckets = 1; + hr = IMFByteStreamBuffering_SetBufferingParams(buffering, ¶ms); + ok(hr == E_INVALIDARG, "Got hr %#lx\n", hr); + + params.prgBuckets = &bucket; + bucket.dwBitrate = 0; + bucket.msBufferWindow = 0; + hr = IMFByteStreamBuffering_SetBufferingParams(buffering, ¶ms); + todo_wine + ok(hr == S_OK, "Got hr %#lx\n", hr); + + params.cbTotalFileSize = 0xdeadbeef; + params.cbPlayableDataSize = 0xdeadbeef; + bucket.dwBitrate = 0xdeadbeef; + bucket.msBufferWindow = 0xdeadbeef; + params.qwNetBufferingTime = 0xdeadbeef; + params.qwExtraBufferingTimeDuringSeek = 0xdeadbeef; + params.qwPlayDuration = 0xdeadbeef; + params.dRate = 12345.0f; + hr = IMFByteStreamBuffering_SetBufferingParams(buffering, ¶ms); + todo_wine + ok(hr == S_OK, "Got hr %#lx\n", hr); + + hr = IMFByteStreamBuffering_EnableBuffering(buffering, TRUE); + ok(hr == S_OK, "Got hr %#lx\n", hr); + + IMFByteStreamBuffering_Release(buffering); + } + + ptr = NULL; + hr = IUnknown_QueryInterface(object, &IID_IMFByteStreamTimeSeek, &ptr); + todo_wine + ok(hr == S_OK, "Got hr %#lx.\n", hr); + todo_wine + ok(ptr != NULL, "Got NULL ptr.\n"); + if (SUCCEEDED(hr) && ptr) + { + QWORD start_time = 0xdeadbeef, stop_time = 0xdeadbef0, duration = 0xdeadbef1; + IMFByteStreamTimeSeek *seek = ptr; + BOOL b = 0xdeadbeef; + HRESULT hr; + + hr = IMFByteStreamTimeSeek_GetTimeSeekResult(seek, NULL, NULL, NULL); + ok(hr == E_INVALIDARG, "Got hr %#lx\n", hr); + + hr = IMFByteStreamTimeSeek_GetTimeSeekResult(seek, &start_time, &stop_time, &duration); + ok(hr == MF_E_INVALIDREQUEST, "Got hr %#lx\n", hr); + ok(start_time == 0, "start_time = %I64u\n", start_time); + ok(stop_time == 0, "stop_time = %I64u\n", stop_time); + ok(duration == 0, "duration = %I64u\n", duration); + + hr = IMFByteStreamTimeSeek_IsTimeSeekSupported(seek, NULL); + ok(hr == S_FALSE, "Got hr %#lx\n", hr); + + hr = IMFByteStreamTimeSeek_IsTimeSeekSupported(seek, &b); + ok(hr == S_FALSE, "Got hr %#lx\n", hr); + ok(!b, "supported = %x\n", b); + + hr = IMFByteStreamTimeSeek_TimeSeek(seek, 0); + ok(hr == MF_E_INVALIDREQUEST, "Got hr %#lx\n", hr); + + hr = IMFByteStreamTimeSeek_GetTimeSeekResult(seek, &start_time, &stop_time, &duration); + ok(hr == MF_E_INVALIDREQUEST, "Got hr %#lx\n", hr); + ok(start_time == 0, "start_time = %I64u\n", start_time); + ok(stop_time == 0, "stop_time = %I64u\n", stop_time); + ok(duration == 0, "duration = %I64u\n", duration); + + IMFByteStreamTimeSeek_Release(seek); + } + + { + BYTE *tmp = malloc(8192); + ULONG read = 0, written = 0; + QWORD len = 0; + + hr = IMFByteStream_SetLength((IMFByteStream*)object, 1000); + ok(hr == E_NOTIMPL, "Got hr %#lx.\n", hr); + + hr = IMFByteStream_SetCurrentPosition((IMFByteStream*)object, 1000); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IMFByteStream_Read((IMFByteStream*)object, tmp, 8192, &read); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(read == 3365, "read = %lu\n", read); + + hr = IMFByteStream_SetCurrentPosition((IMFByteStream*)object, 1000); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + hr = IMFByteStream_Write((IMFByteStream*)object, tmp, 1000, &written); + ok(hr == E_NOTIMPL, "Got hr %#lx.\n", hr); + ok(written == 0, "written = %lu\n", written); + + free(tmp); + + hr = IMFByteStream_GetLength((IMFByteStream*)object, &len); + ok(hr == S_OK, "Got hr %#lx\n", hr); + ok(len != 0, "len = %I64u\n", len); + + hr = IMFByteStream_Flush((IMFByteStream*)object); + todo_wine + ok(hr == S_OK, "Got hr %#lx\n", hr); + } + + ptr = NULL; + hr = MFGetService(object, &MFNETSOURCE_STATISTICS_SERVICE, &IID_IPropertyStore, &ptr); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(ptr != NULL, "Got NULL ptr.\n"); + if (SUCCEEDED(hr) && ptr) + { + IPropertyStore *pstore = ptr; + DWORD count = 0; + + ptr = NULL; + hr = IUnknown_QueryInterface(object, &IID_IPropertyStore, &ptr); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(ptr == (void *)pstore, "Got different IPropertyStore: %p != %p.\n", ptr, pstore); + IPropertyStore_Release((IPropertyStore *)ptr); + + hr = IPropertyStore_GetCount(pstore, &count); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(count == 0, "Got count %lu.\n", count); + + IPropertyStore_Release(pstore); + } + + ptr = NULL; + hr = IUnknown_QueryInterface(object, &IID_IMFMediaEventGenerator, &ptr); + todo_wine + ok(hr == S_OK, "Got hr %#lx.\n", hr); + todo_wine + ok(ptr != NULL, "Got NULL ptr.\n"); + if (SUCCEEDED(hr) && ptr) + { + IMFMediaEvent *evt = (void *)(DWORD_PTR)0xdeadbeef; + BOOL seen_caps_changed = FALSE, buffering = FALSE; + IMFMediaEventGenerator *gen = ptr; + MediaEventType type; + HRESULT hr; + + while (SUCCEEDED(hr = IMFMediaEventGenerator_GetEvent(gen, MF_EVENT_FLAG_NO_WAIT, &evt))) + { + type = (MediaEventType)0xdeadbeef; + hr = IMFMediaEvent_GetType(evt, &type); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + if (type == MEByteStreamCharacteristicsChanged) + { + ok(!seen_caps_changed, "got multiple MEByteStreamCharacteristicsChanged events\n"); + seen_caps_changed = TRUE; + } + else if (type == MEBufferingStarted) + { + ok(!buffering, "got MEBufferingStopped without MEBufferingStarted\n"); + buffering = TRUE; + } + else if (type == MEBufferingStopped) + buffering = FALSE; + else + ok(0, "Unexpected event type %#lx\n", type); + + IMFMediaEvent_Release(evt); + } + ok(hr == MF_E_NO_EVENTS_AVAILABLE, "Got hr %#lx.\n", hr); + + IMFMediaEventGenerator_Release(gen); + } + + obj_type = (MF_OBJECT_TYPE)0xdeadbeef; + bs = NULL; + hr = IMFSourceResolver_CreateObjectFromByteStream(resolver, (void *)object, NULL, MF_RESOLUTION_MEDIASOURCE, NULL, &obj_type, &bs); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(obj_type == MF_OBJECT_MEDIASOURCE, "Unexpected obj_type %#x.\n", obj_type); + + if (bs) IUnknown_Release(bs); + if (object) IUnknown_Release(object); + + IMFSourceResolver_Release(resolver); + + hr = MFShutdown(); + ok(hr == S_OK, "Shutdown failure, hr %#lx.\n", hr); +} + START_TEST(mf) { init_functions(); @@ -6658,4 +7004,5 @@ START_TEST(mf) test_media_session_Start(); test_MFEnumDeviceSources(); test_media_session_Close(); + test_network_bytestream(); }
From: Torge Matthies tmatthies@codeweavers.com
--- dlls/mfplat/Makefile.in | 4 +- dlls/mfplat/http_error.c | 356 ++++++ dlls/mfplat/main.c | 60 +- dlls/mfplat/mfplat_private.h | 2 + dlls/mfplat/network.c | 2014 ++++++++++++++++++++++++++++++++++ 5 files changed, 2405 insertions(+), 31 deletions(-) create mode 100644 dlls/mfplat/http_error.c create mode 100644 dlls/mfplat/network.c
diff --git a/dlls/mfplat/Makefile.in b/dlls/mfplat/Makefile.in index 1e98aaa6f9d..fec5fa2dad5 100644 --- a/dlls/mfplat/Makefile.in +++ b/dlls/mfplat/Makefile.in @@ -1,13 +1,15 @@ MODULE = mfplat.dll IMPORTLIB = mfplat -IMPORTS = advapi32 ole32 dmoguids mfuuid propsys rtworkq kernelbase +IMPORTS = advapi32 ole32 dmoguids mfuuid propsys rtworkq kernelbase winhttp DELAYIMPORTS = bcrypt
EXTRADLLFLAGS = -Wb,--prefer-native
SOURCES = \ buffer.c \ + http_error.c \ main.c \ mediatype.c \ + network.c \ queue.c \ sample.c diff --git a/dlls/mfplat/http_error.c b/dlls/mfplat/http_error.c new file mode 100644 index 00000000000..30c157bdba7 --- /dev/null +++ b/dlls/mfplat/http_error.c @@ -0,0 +1,356 @@ +/* + * HTTP error to HRESULT mapping + * + * Copyright 2024 Torge Matthies for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <stdarg.h> + +#define COBJMACROS +#include "windef.h" +#include "winbase.h" +#include "winhttp.h" +#include "nserror.h" + +#include "mfplat_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +HRESULT map_http_error(DWORD http_error) +{ + TRACE("%lu.\n", http_error); + + switch (http_error) + { + case 401: + case 403: + case 407: + return E_ACCESSDENIED; + case 404: + return NS_E_FILE_NOT_FOUND; + case 410: + return NS_E_RESOURCE_GONE; + + case 500: + return NS_E_INTERNAL_SERVER_ERROR; + case 502: + return NS_E_ERROR_FROM_PROXY; + case 503: + return NS_E_SERVER_UNAVAILABLE; + case 504: + return NS_E_PROXY_TIMEOUT; + + case 100: + case 102: + case 103: + case 104: + case 105: + case 106: + case 107: + case 108: + case 109: + case 110: + case 111: + case 112: + case 113: + case 114: + case 115: + case 116: + case 117: + case 118: + case 119: + case 120: + case 121: + case 122: + case 123: + case 124: + case 125: + case 126: + case 127: + case 128: + case 129: + case 130: + case 131: + case 132: + case 133: + case 134: + case 135: + case 136: + case 137: + case 138: + case 139: + case 140: + case 141: + case 142: + case 143: + case 144: + case 145: + case 146: + case 147: + case 148: + case 149: + case 150: + case 151: + case 152: + case 153: + case 154: + case 155: + case 156: + case 157: + case 158: + case 159: + case 160: + case 161: + case 162: + case 163: + case 164: + case 165: + case 166: + case 167: + case 168: + case 169: + case 170: + case 171: + case 172: + case 173: + case 174: + case 175: + case 176: + case 177: + case 178: + case 179: + case 180: + case 181: + case 182: + case 183: + case 184: + case 185: + case 186: + case 187: + case 188: + case 189: + case 190: + case 191: + case 192: + case 193: + case 194: + case 195: + case 196: + case 197: + case 198: + case 199: + case 301: + case 302: + case 303: + case 307: + return NS_E_CONNECTION_FAILURE; + + case 400: + case 402: + case 405: + case 406: + case 408: + case 409: + case 411: + case 412: + case 413: + case 414: + case 415: + case 416: + case 417: + case 418: + case 419: + case 420: + case 421: + case 422: + case 423: + case 424: + case 425: + case 426: + case 427: + case 428: + case 429: + case 430: + case 431: + case 432: + case 433: + case 434: + case 435: + case 436: + case 437: + case 438: + case 439: + case 440: + case 441: + case 442: + case 443: + case 444: + case 445: + case 446: + case 447: + case 448: + case 449: + case 450: + case 451: + case 452: + case 453: + case 454: + case 455: + case 456: + case 457: + case 458: + case 459: + case 460: + case 461: + case 462: + case 463: + case 464: + case 465: + case 466: + case 467: + case 468: + case 469: + case 470: + case 471: + case 472: + case 473: + case 474: + case 475: + case 476: + case 477: + case 478: + case 479: + case 480: + case 481: + case 482: + case 483: + case 484: + case 485: + case 486: + case 487: + case 488: + case 489: + case 490: + case 491: + case 492: + case 493: + case 494: + case 495: + case 496: + case 497: + case 498: + case 499: + case 501: + case 506: + case 507: + case 508: + case 509: + case 510: + case 511: + case 512: + case 513: + case 514: + case 515: + case 516: + case 517: + case 518: + case 519: + case 520: + case 521: + case 522: + case 523: + case 524: + case 525: + case 526: + case 527: + case 528: + case 529: + case 530: + case 531: + case 532: + case 533: + case 534: + case 535: + case 536: + case 537: + case 538: + case 539: + case 540: + case 541: + case 542: + case 543: + case 544: + case 545: + case 546: + case 547: + case 548: + case 549: + case 550: + case 551: + case 552: + case 553: + case 554: + case 555: + case 556: + case 557: + case 558: + case 559: + case 560: + case 561: + case 562: + case 563: + case 564: + case 565: + case 566: + case 567: + case 568: + case 569: + case 570: + case 571: + case 572: + case 573: + case 574: + case 575: + case 576: + case 577: + case 578: + case 579: + case 580: + case 581: + case 582: + case 583: + case 584: + case 585: + case 586: + case 587: + case 588: + case 589: + case 590: + case 591: + case 592: + case 593: + case 594: + case 595: + case 596: + case 597: + case 598: + case 599: + return NS_E_BAD_REQUEST; + } + + return 0; +} diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c index 4ed607686f1..3cd8ceee8de 100644 --- a/dlls/mfplat/main.c +++ b/dlls/mfplat/main.c @@ -3007,7 +3007,7 @@ HRESULT attributes_CopyAllItems(struct attributes *attributes, IMFAttributes *de return hr; }
-static HRESULT WINAPI mfattributes_GetItem(IMFAttributes *iface, REFGUID key, PROPVARIANT *value) +HRESULT WINAPI mfattributes_GetItem(IMFAttributes *iface, REFGUID key, PROPVARIANT *value) { struct attributes *attributes = impl_from_IMFAttributes(iface);
@@ -3016,7 +3016,7 @@ static HRESULT WINAPI mfattributes_GetItem(IMFAttributes *iface, REFGUID key, PR return attributes_GetItem(attributes, key, value); }
-static HRESULT WINAPI mfattributes_GetItemType(IMFAttributes *iface, REFGUID key, MF_ATTRIBUTE_TYPE *type) +HRESULT WINAPI mfattributes_GetItemType(IMFAttributes *iface, REFGUID key, MF_ATTRIBUTE_TYPE *type) { struct attributes *attributes = impl_from_IMFAttributes(iface);
@@ -3025,7 +3025,7 @@ static HRESULT WINAPI mfattributes_GetItemType(IMFAttributes *iface, REFGUID key return attributes_GetItemType(attributes, key, type); }
-static HRESULT WINAPI mfattributes_CompareItem(IMFAttributes *iface, REFGUID key, REFPROPVARIANT value, BOOL *result) +HRESULT WINAPI mfattributes_CompareItem(IMFAttributes *iface, REFGUID key, REFPROPVARIANT value, BOOL *result) { struct attributes *attributes = impl_from_IMFAttributes(iface);
@@ -3034,7 +3034,7 @@ static HRESULT WINAPI mfattributes_CompareItem(IMFAttributes *iface, REFGUID key return attributes_CompareItem(attributes, key, value, result); }
-static HRESULT WINAPI mfattributes_Compare(IMFAttributes *iface, IMFAttributes *theirs, +HRESULT WINAPI mfattributes_Compare(IMFAttributes *iface, IMFAttributes *theirs, MF_ATTRIBUTES_MATCH_TYPE match_type, BOOL *ret) { struct attributes *attributes = impl_from_IMFAttributes(iface); @@ -3044,7 +3044,7 @@ static HRESULT WINAPI mfattributes_Compare(IMFAttributes *iface, IMFAttributes * return attributes_Compare(attributes, theirs, match_type, ret); }
-static HRESULT WINAPI mfattributes_GetUINT32(IMFAttributes *iface, REFGUID key, UINT32 *value) +HRESULT WINAPI mfattributes_GetUINT32(IMFAttributes *iface, REFGUID key, UINT32 *value) { struct attributes *attributes = impl_from_IMFAttributes(iface);
@@ -3053,7 +3053,7 @@ static HRESULT WINAPI mfattributes_GetUINT32(IMFAttributes *iface, REFGUID key, return attributes_GetUINT32(attributes, key, value); }
-static HRESULT WINAPI mfattributes_GetUINT64(IMFAttributes *iface, REFGUID key, UINT64 *value) +HRESULT WINAPI mfattributes_GetUINT64(IMFAttributes *iface, REFGUID key, UINT64 *value) { struct attributes *attributes = impl_from_IMFAttributes(iface);
@@ -3062,7 +3062,7 @@ static HRESULT WINAPI mfattributes_GetUINT64(IMFAttributes *iface, REFGUID key, return attributes_GetUINT64(attributes, key, value); }
-static HRESULT WINAPI mfattributes_GetDouble(IMFAttributes *iface, REFGUID key, double *value) +HRESULT WINAPI mfattributes_GetDouble(IMFAttributes *iface, REFGUID key, double *value) { struct attributes *attributes = impl_from_IMFAttributes(iface);
@@ -3071,7 +3071,7 @@ static HRESULT WINAPI mfattributes_GetDouble(IMFAttributes *iface, REFGUID key, return attributes_GetDouble(attributes, key, value); }
-static HRESULT WINAPI mfattributes_GetGUID(IMFAttributes *iface, REFGUID key, GUID *value) +HRESULT WINAPI mfattributes_GetGUID(IMFAttributes *iface, REFGUID key, GUID *value) { struct attributes *attributes = impl_from_IMFAttributes(iface);
@@ -3080,7 +3080,7 @@ static HRESULT WINAPI mfattributes_GetGUID(IMFAttributes *iface, REFGUID key, GU return attributes_GetGUID(attributes, key, value); }
-static HRESULT WINAPI mfattributes_GetStringLength(IMFAttributes *iface, REFGUID key, UINT32 *length) +HRESULT WINAPI mfattributes_GetStringLength(IMFAttributes *iface, REFGUID key, UINT32 *length) { struct attributes *attributes = impl_from_IMFAttributes(iface);
@@ -3089,7 +3089,7 @@ static HRESULT WINAPI mfattributes_GetStringLength(IMFAttributes *iface, REFGUID return attributes_GetStringLength(attributes, key, length); }
-static HRESULT WINAPI mfattributes_GetString(IMFAttributes *iface, REFGUID key, WCHAR *value, +HRESULT WINAPI mfattributes_GetString(IMFAttributes *iface, REFGUID key, WCHAR *value, UINT32 size, UINT32 *length) { struct attributes *attributes = impl_from_IMFAttributes(iface); @@ -3099,7 +3099,7 @@ static HRESULT WINAPI mfattributes_GetString(IMFAttributes *iface, REFGUID key, return attributes_GetString(attributes, key, value, size, length); }
-static HRESULT WINAPI mfattributes_GetAllocatedString(IMFAttributes *iface, REFGUID key, WCHAR **value, UINT32 *length) +HRESULT WINAPI mfattributes_GetAllocatedString(IMFAttributes *iface, REFGUID key, WCHAR **value, UINT32 *length) { struct attributes *attributes = impl_from_IMFAttributes(iface);
@@ -3108,7 +3108,7 @@ static HRESULT WINAPI mfattributes_GetAllocatedString(IMFAttributes *iface, REFG return attributes_GetAllocatedString(attributes, key, value, length); }
-static HRESULT WINAPI mfattributes_GetBlobSize(IMFAttributes *iface, REFGUID key, UINT32 *size) +HRESULT WINAPI mfattributes_GetBlobSize(IMFAttributes *iface, REFGUID key, UINT32 *size) { struct attributes *attributes = impl_from_IMFAttributes(iface);
@@ -3117,7 +3117,7 @@ static HRESULT WINAPI mfattributes_GetBlobSize(IMFAttributes *iface, REFGUID key return attributes_GetBlobSize(attributes, key, size); }
-static HRESULT WINAPI mfattributes_GetBlob(IMFAttributes *iface, REFGUID key, UINT8 *buf, +HRESULT WINAPI mfattributes_GetBlob(IMFAttributes *iface, REFGUID key, UINT8 *buf, UINT32 bufsize, UINT32 *blobsize) { struct attributes *attributes = impl_from_IMFAttributes(iface); @@ -3127,7 +3127,7 @@ static HRESULT WINAPI mfattributes_GetBlob(IMFAttributes *iface, REFGUID key, UI return attributes_GetBlob(attributes, key, buf, bufsize, blobsize); }
-static HRESULT WINAPI mfattributes_GetAllocatedBlob(IMFAttributes *iface, REFGUID key, UINT8 **buf, UINT32 *size) +HRESULT WINAPI mfattributes_GetAllocatedBlob(IMFAttributes *iface, REFGUID key, UINT8 **buf, UINT32 *size) { struct attributes *attributes = impl_from_IMFAttributes(iface);
@@ -3136,7 +3136,7 @@ static HRESULT WINAPI mfattributes_GetAllocatedBlob(IMFAttributes *iface, REFGUI return attributes_GetAllocatedBlob(attributes, key, buf, size); }
-static HRESULT WINAPI mfattributes_GetUnknown(IMFAttributes *iface, REFGUID key, REFIID riid, void **out) +HRESULT WINAPI mfattributes_GetUnknown(IMFAttributes *iface, REFGUID key, REFIID riid, void **out) { struct attributes *attributes = impl_from_IMFAttributes(iface);
@@ -3145,7 +3145,7 @@ static HRESULT WINAPI mfattributes_GetUnknown(IMFAttributes *iface, REFGUID key, return attributes_GetUnknown(attributes, key, riid, out); }
-static HRESULT WINAPI mfattributes_SetItem(IMFAttributes *iface, REFGUID key, REFPROPVARIANT value) +HRESULT WINAPI mfattributes_SetItem(IMFAttributes *iface, REFGUID key, REFPROPVARIANT value) { struct attributes *attributes = impl_from_IMFAttributes(iface);
@@ -3154,7 +3154,7 @@ static HRESULT WINAPI mfattributes_SetItem(IMFAttributes *iface, REFGUID key, RE return attributes_SetItem(attributes, key, value); }
-static HRESULT WINAPI mfattributes_DeleteItem(IMFAttributes *iface, REFGUID key) +HRESULT WINAPI mfattributes_DeleteItem(IMFAttributes *iface, REFGUID key) { struct attributes *attributes = impl_from_IMFAttributes(iface);
@@ -3163,7 +3163,7 @@ static HRESULT WINAPI mfattributes_DeleteItem(IMFAttributes *iface, REFGUID key) return attributes_DeleteItem(attributes, key); }
-static HRESULT WINAPI mfattributes_DeleteAllItems(IMFAttributes *iface) +HRESULT WINAPI mfattributes_DeleteAllItems(IMFAttributes *iface) { struct attributes *attributes = impl_from_IMFAttributes(iface);
@@ -3172,7 +3172,7 @@ static HRESULT WINAPI mfattributes_DeleteAllItems(IMFAttributes *iface) return attributes_DeleteAllItems(attributes); }
-static HRESULT WINAPI mfattributes_SetUINT32(IMFAttributes *iface, REFGUID key, UINT32 value) +HRESULT WINAPI mfattributes_SetUINT32(IMFAttributes *iface, REFGUID key, UINT32 value) { struct attributes *attributes = impl_from_IMFAttributes(iface);
@@ -3181,7 +3181,7 @@ static HRESULT WINAPI mfattributes_SetUINT32(IMFAttributes *iface, REFGUID key, return attributes_SetUINT32(attributes, key, value); }
-static HRESULT WINAPI mfattributes_SetUINT64(IMFAttributes *iface, REFGUID key, UINT64 value) +HRESULT WINAPI mfattributes_SetUINT64(IMFAttributes *iface, REFGUID key, UINT64 value) { struct attributes *attributes = impl_from_IMFAttributes(iface);
@@ -3190,7 +3190,7 @@ static HRESULT WINAPI mfattributes_SetUINT64(IMFAttributes *iface, REFGUID key, return attributes_SetUINT64(attributes, key, value); }
-static HRESULT WINAPI mfattributes_SetDouble(IMFAttributes *iface, REFGUID key, double value) +HRESULT WINAPI mfattributes_SetDouble(IMFAttributes *iface, REFGUID key, double value) { struct attributes *attributes = impl_from_IMFAttributes(iface);
@@ -3199,7 +3199,7 @@ static HRESULT WINAPI mfattributes_SetDouble(IMFAttributes *iface, REFGUID key, return attributes_SetDouble(attributes, key, value); }
-static HRESULT WINAPI mfattributes_SetGUID(IMFAttributes *iface, REFGUID key, REFGUID value) +HRESULT WINAPI mfattributes_SetGUID(IMFAttributes *iface, REFGUID key, REFGUID value) { struct attributes *attributes = impl_from_IMFAttributes(iface);
@@ -3208,7 +3208,7 @@ static HRESULT WINAPI mfattributes_SetGUID(IMFAttributes *iface, REFGUID key, RE return attributes_SetGUID(attributes, key, value); }
-static HRESULT WINAPI mfattributes_SetString(IMFAttributes *iface, REFGUID key, const WCHAR *value) +HRESULT WINAPI mfattributes_SetString(IMFAttributes *iface, REFGUID key, const WCHAR *value) { struct attributes *attributes = impl_from_IMFAttributes(iface);
@@ -3217,7 +3217,7 @@ static HRESULT WINAPI mfattributes_SetString(IMFAttributes *iface, REFGUID key, return attributes_SetString(attributes, key, value); }
-static HRESULT WINAPI mfattributes_SetBlob(IMFAttributes *iface, REFGUID key, const UINT8 *buf, UINT32 size) +HRESULT WINAPI mfattributes_SetBlob(IMFAttributes *iface, REFGUID key, const UINT8 *buf, UINT32 size) { struct attributes *attributes = impl_from_IMFAttributes(iface);
@@ -3226,7 +3226,7 @@ static HRESULT WINAPI mfattributes_SetBlob(IMFAttributes *iface, REFGUID key, co return attributes_SetBlob(attributes, key, buf, size); }
-static HRESULT WINAPI mfattributes_SetUnknown(IMFAttributes *iface, REFGUID key, IUnknown *unknown) +HRESULT WINAPI mfattributes_SetUnknown(IMFAttributes *iface, REFGUID key, IUnknown *unknown) { struct attributes *attributes = impl_from_IMFAttributes(iface);
@@ -3235,7 +3235,7 @@ static HRESULT WINAPI mfattributes_SetUnknown(IMFAttributes *iface, REFGUID key, return attributes_SetUnknown(attributes, key, unknown); }
-static HRESULT WINAPI mfattributes_LockStore(IMFAttributes *iface) +HRESULT WINAPI mfattributes_LockStore(IMFAttributes *iface) { struct attributes *attributes = impl_from_IMFAttributes(iface);
@@ -3244,7 +3244,7 @@ static HRESULT WINAPI mfattributes_LockStore(IMFAttributes *iface) return attributes_LockStore(attributes); }
-static HRESULT WINAPI mfattributes_UnlockStore(IMFAttributes *iface) +HRESULT WINAPI mfattributes_UnlockStore(IMFAttributes *iface) { struct attributes *attributes = impl_from_IMFAttributes(iface);
@@ -3253,7 +3253,7 @@ static HRESULT WINAPI mfattributes_UnlockStore(IMFAttributes *iface) return attributes_UnlockStore(attributes); }
-static HRESULT WINAPI mfattributes_GetCount(IMFAttributes *iface, UINT32 *count) +HRESULT WINAPI mfattributes_GetCount(IMFAttributes *iface, UINT32 *count) { struct attributes *attributes = impl_from_IMFAttributes(iface);
@@ -3262,7 +3262,7 @@ static HRESULT WINAPI mfattributes_GetCount(IMFAttributes *iface, UINT32 *count) return attributes_GetCount(attributes, count); }
-static HRESULT WINAPI mfattributes_GetItemByIndex(IMFAttributes *iface, UINT32 index, GUID *key, PROPVARIANT *value) +HRESULT WINAPI mfattributes_GetItemByIndex(IMFAttributes *iface, UINT32 index, GUID *key, PROPVARIANT *value) { struct attributes *attributes = impl_from_IMFAttributes(iface);
@@ -3271,7 +3271,7 @@ static HRESULT WINAPI mfattributes_GetItemByIndex(IMFAttributes *iface, UINT32 i return attributes_GetItemByIndex(attributes, index, key, value); }
-static HRESULT WINAPI mfattributes_CopyAllItems(IMFAttributes *iface, IMFAttributes *dest) +HRESULT WINAPI mfattributes_CopyAllItems(IMFAttributes *iface, IMFAttributes *dest) { struct attributes *attributes = impl_from_IMFAttributes(iface);
diff --git a/dlls/mfplat/mfplat_private.h b/dlls/mfplat/mfplat_private.h index c9af96d88f9..2729eae9dce 100644 --- a/dlls/mfplat/mfplat_private.h +++ b/dlls/mfplat/mfplat_private.h @@ -85,6 +85,8 @@ extern HRESULT attributes_GetItemByIndex(struct attributes *object, UINT32 index PROPVARIANT *value); extern HRESULT attributes_CopyAllItems(struct attributes *object, IMFAttributes *dest);
+extern HRESULT map_http_error(DWORD http_error); + static inline BOOL mf_array_reserve(void **elements, size_t *capacity, size_t count, size_t size) { size_t new_capacity, max_capacity; diff --git a/dlls/mfplat/network.c b/dlls/mfplat/network.c new file mode 100644 index 00000000000..d9a2981f5a7 --- /dev/null +++ b/dlls/mfplat/network.c @@ -0,0 +1,2014 @@ +/* + * HTTP network byte stream implementation + * + * Copyright 2024 Torge Matthies for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <stdarg.h> + +#define COBJMACROS +#include "windef.h" +#include "winbase.h" +#include "winhttp.h" +#include "nserror.h" +#include "mfidl.h" +#include "rpcproxy.h" +#include "rtworkq.h" +#include "bcrypt.h" +#include "pathcch.h" + +#include "mfplat_private.h" + +#include "wine/debug.h" +#include "wine/list.h" +#include "wine/mfinternal.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +struct bytestream_http; + +static HRESULT send_characteristics_changed_event(struct bytestream_http *object); + +static HRESULT send_buffering_event(struct bytestream_http *object, BOOL buffering); +static HRESULT update_buffering_status(struct bytestream_http *object); + +static HRESULT do_read(struct bytestream_http *object, BYTE *buffer, ULONG size, ULONG *read_len); + +static HRESULT create_temp_file(HANDLE *h); + +static HRESULT open_connection(struct bytestream_http *object, ULONGLONG range_start, ULONGLONG range_end); +static void close_connection(struct bytestream_http *object, BOOL wait); + +static BOOL is_range_available(struct bytestream_http *object, ULONGLONG start, ULONGLONG count); +static ULONGLONG find_range_end(struct bytestream_http *object, ULONGLONG start); +static ULONGLONG find_hole_end(struct bytestream_http *object, ULONGLONG start); +static void mark_range_valid(struct bytestream_http *object, ULONGLONG start, ULONGLONG count); + +struct downloaded_range +{ + struct list entry; + ULONGLONG start; + ULONGLONG end; +}; + +struct bytestream_http +{ + struct attributes attributes; + IMFByteStream IMFByteStream_iface; + IMFByteStreamCacheControl IMFByteStreamCacheControl_iface; + IMFByteStreamBuffering IMFByteStreamBuffering_iface; + IMFMediaEventGenerator IMFMediaEventGenerator_iface; + IMFByteStreamTimeSeek IMFByteStreamTimeSeek_iface; + IPropertyStore IPropertyStore_iface; + IMFGetService IMFGetService_iface; + + CRITICAL_SECTION cs; + CONDITION_VARIABLE cv; + + HRESULT error; + + IRtwqAsyncCallback read_callback; + struct list pending; + ULONGLONG position; + BOOL has_pending_read; + + WCHAR *url; + + IPropertyStore *propstore; + IMFMediaEventQueue *event_queue; + + BOOL closing_connnection; + HINTERNET session; + HINTERNET connection; + HINTERNET request; + + BOOL parsed_headers; + const WCHAR *content_type; + BOOL has_length; + ULONGLONG content_length; + ULONGLONG last_modified; + ULONGLONG content_range_start; + ULONGLONG content_range_end; + + BOOL buffering_enabled; + BOOL buffering; + + struct list downloaded_ranges; + HANDLE cache_file; + + ULONGLONG current_write_pos; + BOOL read_active; + DWORD bytes_read; + BYTE buffer[1024*1024]; +}; + +static struct bytestream_http *impl_from_IMFAttributes(IMFAttributes *iface) +{ + return CONTAINING_RECORD(iface, struct bytestream_http, attributes.IMFAttributes_iface); +} + +static struct bytestream_http *impl_from_IMFByteStream(IMFByteStream *iface) +{ + return CONTAINING_RECORD(iface, struct bytestream_http, IMFByteStream_iface); +} + +static struct bytestream_http *impl_from_IMFByteStreamCacheControl(IMFByteStreamCacheControl *iface) +{ + return CONTAINING_RECORD(iface, struct bytestream_http, IMFByteStreamCacheControl_iface); +} + +static struct bytestream_http *impl_from_IMFByteStreamBuffering(IMFByteStreamBuffering *iface) +{ + return CONTAINING_RECORD(iface, struct bytestream_http, IMFByteStreamBuffering_iface); +} + +static struct bytestream_http *impl_from_IMFMediaEventGenerator(IMFMediaEventGenerator *iface) +{ + return CONTAINING_RECORD(iface, struct bytestream_http, IMFMediaEventGenerator_iface); +} + +static struct bytestream_http *impl_from_IMFByteStreamTimeSeek(IMFByteStreamTimeSeek *iface) +{ + return CONTAINING_RECORD(iface, struct bytestream_http, IMFByteStreamTimeSeek_iface); +} + +static struct bytestream_http *impl_from_IPropertyStore(IPropertyStore *iface) +{ + return CONTAINING_RECORD(iface, struct bytestream_http, IPropertyStore_iface); +} + +static struct bytestream_http *impl_from_IMFGetService(IMFGetService *iface) +{ + return CONTAINING_RECORD(iface, struct bytestream_http, IMFGetService_iface); +} + +enum async_stream_op_type +{ + ASYNC_STREAM_OP_READ, +}; + +struct async_stream_op +{ + IUnknown IUnknown_iface; + LONG refcount; + union + { + const BYTE *src; + BYTE *dest; + } u; + QWORD position; + ULONG requested_length; + ULONG actual_length; + IMFAsyncResult *caller; + struct list entry; + enum async_stream_op_type type; +}; + +static struct async_stream_op *impl_async_stream_op_from_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct async_stream_op, IUnknown_iface); +} + +static HRESULT WINAPI async_stream_op_QueryInterface(IUnknown *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IUnknown_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI async_stream_op_AddRef(IUnknown *iface) +{ + struct async_stream_op *op = impl_async_stream_op_from_IUnknown(iface); + ULONG refcount = InterlockedIncrement(&op->refcount); + + TRACE("%p, refcount %ld.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI async_stream_op_Release(IUnknown *iface) +{ + struct async_stream_op *op = impl_async_stream_op_from_IUnknown(iface); + ULONG refcount = InterlockedDecrement(&op->refcount); + + TRACE("%p, refcount %ld.\n", iface, refcount); + + if (!refcount) + { + if (op->caller) + IMFAsyncResult_Release(op->caller); + free(op); + } + + return refcount; +} + +static const IUnknownVtbl async_stream_op_vtbl = +{ + async_stream_op_QueryInterface, + async_stream_op_AddRef, + async_stream_op_Release, +}; + +static HRESULT WINAPI bytestream_http_QueryInterface(IMFByteStream *iface, REFIID riid, void **out) +{ + struct bytestream_http *object = impl_from_IMFByteStream(iface); + + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), out); + + if (IsEqualIID(riid, &IID_IMFByteStream) || + IsEqualIID(riid, &IID_IUnknown)) + { + *out = &object->IMFByteStream_iface; + } + else if (IsEqualIID(riid, &IID_IMFAttributes)) + { + *out = &object->attributes.IMFAttributes_iface; + } + else if (IsEqualIID(riid, &IID_IMFByteStream)) + { + *out = &object->IMFByteStream_iface; + } + else if (IsEqualIID(riid, &IID_IMFByteStreamCacheControl)) + { + *out = &object->IMFByteStreamCacheControl_iface; + } + else if (IsEqualIID(riid, &IID_IMFByteStreamBuffering)) + { + *out = &object->IMFByteStreamBuffering_iface; + } + else if (IsEqualIID(riid, &IID_IMFMediaEventGenerator)) + { + *out = &object->IMFMediaEventGenerator_iface; + } + else if (IsEqualIID(riid, &IID_IMFByteStreamTimeSeek)) + { + *out = &object->IMFByteStreamTimeSeek_iface; + } + else if (IsEqualIID(riid, &IID_IPropertyStore)) + { + *out = &object->IPropertyStore_iface; + } + else if (IsEqualIID(riid, &IID_IMFGetService)) + { + *out = &object->IMFGetService_iface; + } + else + { + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *out = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown *)*out); + return S_OK; +} + +static ULONG WINAPI bytestream_http_AddRef(IMFByteStream *iface) +{ + struct bytestream_http *object = impl_from_IMFByteStream(iface); + ULONG refcount = InterlockedIncrement(&object->attributes.ref); + + TRACE("%p, refcount %ld.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI bytestream_http_Release(IMFByteStream *iface) +{ + struct bytestream_http *object = impl_from_IMFByteStream(iface); + ULONG refcount = InterlockedDecrement(&object->attributes.ref); + struct async_stream_op *cur, *cur2; + + TRACE("%p, refcount %ld.\n", iface, refcount); + + if (!refcount) + { + close_connection(object, FALSE); + clear_attributes_object(&object->attributes); + LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &object->pending, struct async_stream_op, entry) + { + list_remove(&cur->entry); + IUnknown_Release(&cur->IUnknown_iface); + } + if (object->event_queue) + IMFMediaEventQueue_Release(object->event_queue); + if (object->propstore) + IPropertyStore_Release(object->propstore); + if (object->cache_file && object->cache_file != INVALID_HANDLE_VALUE) + CloseHandle(object->cache_file); + DeleteCriticalSection(&object->cs); + free((void *)object->content_type); + free((void *)object->url); + free(object); + } + + return refcount; +} + +static struct bytestream_http *impl_from_read_callback_IRtwqAsyncCallback(IRtwqAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct bytestream_http, read_callback); +} + +static HRESULT bytestream_http_create_io_request(struct bytestream_http *object, enum async_stream_op_type type, + const BYTE *data, ULONG size, IMFAsyncCallback *callback, IUnknown *state) +{ + struct async_stream_op *op; + IRtwqAsyncResult *request; + HRESULT hr; + + if (type != ASYNC_STREAM_OP_READ) + return E_NOTIMPL; + + op = malloc(sizeof(*op)); + if (!op) + return E_OUTOFMEMORY; + + op->IUnknown_iface.lpVtbl = &async_stream_op_vtbl; + op->refcount = 1; + op->u.src = data; + op->position = object->position; + op->requested_length = size; + op->type = type; + if (FAILED(hr = RtwqCreateAsyncResult((IUnknown *)&object->IMFByteStream_iface, (IRtwqAsyncCallback *)callback, + state, (IRtwqAsyncResult **)&op->caller))) + { + goto failed; + } + + if (FAILED(hr = RtwqCreateAsyncResult(&op->IUnknown_iface, &object->read_callback, NULL, &request))) + goto failed; + + RtwqPutWorkItem(MFASYNC_CALLBACK_QUEUE_IO, 0, request); + IRtwqAsyncResult_Release(request); + +failed: + IUnknown_Release(&op->IUnknown_iface); + return hr; +} + +static HRESULT bytestream_http_complete_io_request(struct bytestream_http *object, enum async_stream_op_type type, + IMFAsyncResult *result, ULONG *actual_length) +{ + struct async_stream_op *op = NULL, *cur; + HRESULT hr; + + EnterCriticalSection(&object->cs); + LIST_FOR_EACH_ENTRY(cur, &object->pending, struct async_stream_op, entry) + { + if (cur->caller == result && cur->type == type) + { + op = cur; + list_remove(&cur->entry); + break; + } + } + LeaveCriticalSection(&object->cs); + + if (!op) + return E_INVALIDARG; + + if (SUCCEEDED(hr = IMFAsyncResult_GetStatus(result))) + *actual_length = op->actual_length; + + IUnknown_Release(&op->IUnknown_iface); + + return hr; +} + +static HRESULT WINAPI bytestream_http_callback_QueryInterface(IRtwqAsyncCallback *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IRtwqAsyncCallback) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IRtwqAsyncCallback_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI bytestream_http_read_callback_AddRef(IRtwqAsyncCallback *iface) +{ + struct bytestream_http *object = impl_from_read_callback_IRtwqAsyncCallback(iface); + return IMFByteStream_AddRef(&object->IMFByteStream_iface); +} + +static ULONG WINAPI bytestream_http_read_callback_Release(IRtwqAsyncCallback *iface) +{ + struct bytestream_http *object = impl_from_read_callback_IRtwqAsyncCallback(iface); + return IMFByteStream_Release(&object->IMFByteStream_iface); +} + +static HRESULT WINAPI bytestream_http_callback_GetParameters(IRtwqAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI bytestream_http_read_callback_Invoke(IRtwqAsyncCallback *iface, IRtwqAsyncResult *result) +{ + struct bytestream_http *object = impl_from_read_callback_IRtwqAsyncCallback(iface); + struct async_stream_op *op; + IUnknown *resobj; + HRESULT hr; + + if (FAILED(hr = IRtwqAsyncResult_GetObject(result, &resobj))) + return hr; + + op = impl_async_stream_op_from_IUnknown(resobj); + + EnterCriticalSection(&object->cs); + hr = do_read(object, op->u.dest, op->requested_length, &op->actual_length); + if(FAILED(hr)) TRACE("Read failed: %#lx\n", hr); + IMFAsyncResult_SetStatus(op->caller, hr); + list_add_tail(&object->pending, &op->entry); + LeaveCriticalSection(&object->cs); + + MFInvokeCallback(op->caller); + + return S_OK; +} + +static const IRtwqAsyncCallbackVtbl bytestream_http_read_callback_vtbl = +{ + bytestream_http_callback_QueryInterface, + bytestream_http_read_callback_AddRef, + bytestream_http_read_callback_Release, + bytestream_http_callback_GetParameters, + bytestream_http_read_callback_Invoke, +}; + +static HRESULT WINAPI bytestream_http_GetCapabilities(IMFByteStream *iface, DWORD *capabilities) +{ + struct bytestream_http *object = impl_from_IMFByteStream(iface); + + TRACE("%p, %p.\n", iface, capabilities); + + EnterCriticalSection(&object->cs); + *capabilities = MFBYTESTREAM_IS_READABLE | MFBYTESTREAM_IS_SEEKABLE; + if (!object->has_length || !is_range_available(object, 0, object->content_length)) + *capabilities |= MFBYTESTREAM_IS_PARTIALLY_DOWNLOADED; + LeaveCriticalSection(&object->cs); + return S_OK; +} + +static HRESULT WINAPI bytestream_http_SetLength(IMFByteStream *iface, QWORD length) +{ + TRACE("%p, %s\n", iface, wine_dbgstr_longlong(length)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI bytestream_http_GetCurrentPosition(IMFByteStream *iface, QWORD *position) +{ + struct bytestream_http *object = impl_from_IMFByteStream(iface); + + TRACE("%p, %p.\n", iface, position); + + if (!position) + return E_INVALIDARG; + + EnterCriticalSection(&object->cs); + *position = object->position; + LeaveCriticalSection(&object->cs); + return S_OK; +} + +static HRESULT WINAPI bytestream_http_GetLength(IMFByteStream *iface, QWORD *length) +{ + struct bytestream_http *object = impl_from_IMFByteStream(iface); + + TRACE("%p, %p.\n", iface, length); + + EnterCriticalSection(&object->cs); + + if (!object->has_length) + { + LeaveCriticalSection(&object->cs); + return MF_E_BYTESTREAM_UNKNOWN_LENGTH; + } + + *length = object->content_length; + + LeaveCriticalSection(&object->cs); + return S_OK; +} + +static HRESULT WINAPI bytestream_http_IsEndOfStream(IMFByteStream *iface, BOOL *ret) +{ + struct bytestream_http *object = impl_from_IMFByteStream(iface); + + TRACE("%p, %p.\n", iface, ret); + + EnterCriticalSection(&object->cs); + *ret = object->position >= object->content_length; + LeaveCriticalSection(&object->cs); + return S_OK; +} + +static HRESULT send_characteristics_changed_event(struct bytestream_http *object) +{ + PROPVARIANT data = {.vt = VT_EMPTY}; + GUID ext_type = GUID_NULL; + HRESULT hr; + + TRACE("%p.\n", object); + + if (FAILED(hr = IMFMediaEventQueue_QueueEventParamVar(object->event_queue, MEByteStreamCharacteristicsChanged, + &ext_type, S_OK, &data))) + ERR("Failed to enqueue event %x, hr %#lx.\n", MEByteStreamCharacteristicsChanged, hr); + + return hr; +} + +static HRESULT do_read(struct bytestream_http *object, BYTE *buffer, ULONG size, ULONG *read_len) +{ + DWORD prev_caps = 0, new_caps = 0; + LARGE_INTEGER position; + HRESULT hr = S_OK; + ULONGLONG end; + BOOL ret; + + position.QuadPart = object->position; + if (!object->error && !is_range_available(object, position.QuadPart, size)) + { + while (!object->error && object->has_pending_read) + SleepConditionVariableCS(&object->cv, &object->cs, INFINITE); + + object->has_pending_read = TRUE; + + if (!object->request || object->closing_connnection || object->current_write_pos > position.QuadPart || + object->current_write_pos + 12288 <= position.QuadPart) + { + end = find_hole_end(object, position.QuadPart); + if (FAILED(hr = open_connection(object, position.QuadPart, end))) + { + object->has_pending_read = FALSE; + return hr; + } + } + + while (!object->error && !is_range_available(object, position.QuadPart, size)) + SleepConditionVariableCS(&object->cv, &object->cs, INFINITE); + + object->has_pending_read = FALSE; + } + + if (object->error) + { + hr = object->error; + return hr; + } + + if (object->has_length && position.QuadPart + size > object->content_length) + size = object->content_length - position.QuadPart; + if ((ret = SetFilePointerEx(object->cache_file, position, NULL, FILE_BEGIN))) + { + if ((ret = ReadFile(object->cache_file, buffer, size, read_len, NULL))) + { + bytestream_http_GetCapabilities(&object->IMFByteStream_iface, &prev_caps); + + object->position += *read_len; + + bytestream_http_GetCapabilities(&object->IMFByteStream_iface, &new_caps); + if (prev_caps != new_caps) + send_characteristics_changed_event(object); + } + } + + if (!ret) + hr = HRESULT_FROM_WIN32(GetLastError()); + + return hr; +} + +static HRESULT WINAPI bytestream_http_Read(IMFByteStream *iface, BYTE *buffer, ULONG size, ULONG *read_len) +{ + struct bytestream_http *object = impl_from_IMFByteStream(iface); + HRESULT hr; + + TRACE("%p, %p, %lu, %p.\n", iface, buffer, size, read_len); + + EnterCriticalSection(&object->cs); + hr = do_read(object, buffer, size, read_len); + LeaveCriticalSection(&object->cs); + return hr; +} + +static HRESULT WINAPI bytestream_http_BeginRead(IMFByteStream *iface, BYTE *data, ULONG size, IMFAsyncCallback *callback, + IUnknown *state) +{ + struct bytestream_http *object = impl_from_IMFByteStream(iface); + + TRACE("%p, %p, %lu, %p, %p.\n", iface, data, size, callback, state); + + return bytestream_http_create_io_request(object, ASYNC_STREAM_OP_READ, data, size, callback, state); +} + +static HRESULT WINAPI bytestream_http_EndRead(IMFByteStream *iface, IMFAsyncResult *result, ULONG *byte_read) +{ + struct bytestream_http *object = impl_from_IMFByteStream(iface); + + TRACE("%p, %p, %p.\n", iface, result, byte_read); + + return bytestream_http_complete_io_request(object, ASYNC_STREAM_OP_READ, result, byte_read); +} + +static HRESULT WINAPI bytestream_http_Write(IMFByteStream *iface, const BYTE *data, ULONG size, ULONG *written) +{ + TRACE("%p, %p, %lu, %p\n", iface, data, size, written); + + return E_NOTIMPL; +} + +static HRESULT WINAPI bytestream_http_BeginWrite(IMFByteStream *iface, const BYTE *data, ULONG size, + IMFAsyncCallback *callback, IUnknown *state) +{ + TRACE("%p, %p, %lu, %p, %p.\n", iface, data, size, callback, state); + + return E_NOTIMPL; +} + +static HRESULT WINAPI bytestream_http_EndWrite(IMFByteStream *iface, IMFAsyncResult *result, ULONG *written) +{ + TRACE("%p, %p, %p.\n", iface, result, written); + + return E_NOTIMPL; +} + +static HRESULT WINAPI bytestream_http_Seek(IMFByteStream *iface, MFBYTESTREAM_SEEK_ORIGIN origin, LONGLONG offset, + DWORD flags, QWORD *current) +{ + struct bytestream_http *object = impl_from_IMFByteStream(iface); + HRESULT hr = S_OK; + + TRACE("%p, %u, %s, %#lx, %p.\n", iface, origin, wine_dbgstr_longlong(offset), flags, current); + + EnterCriticalSection(&object->cs); + + switch (origin) + { + case msoBegin: + object->position = offset; + break; + case msoCurrent: + object->position += offset; + break; + default: + WARN("Unknown origin mode %d.\n", origin); + hr = E_INVALIDARG; + } + + *current = object->position; + + LeaveCriticalSection(&object->cs); + + return hr; +} + +static HRESULT WINAPI bytestream_http_Flush(IMFByteStream *iface) +{ + TRACE("%p\n", iface); + + return S_OK; +} + +static HRESULT WINAPI bytestream_http_Close(IMFByteStream *iface) +{ + struct bytestream_http *object = impl_from_IMFByteStream(iface); + struct async_stream_op *cur, *cur2; + + TRACE("%p\n", iface); + + EnterCriticalSection(&object->cs); + LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &object->pending, struct async_stream_op, entry) + { + list_remove(&cur->entry); + IUnknown_Release(&cur->IUnknown_iface); + } + object->error = MF_E_SHUTDOWN; + close_connection(object, TRUE); + IMFMediaEventQueue_Shutdown(object->event_queue); + LeaveCriticalSection(&object->cs); + return S_OK; +} + +static HRESULT WINAPI bytestream_http_SetCurrentPosition(IMFByteStream *iface, QWORD position) +{ + QWORD new_pos; + + TRACE("%p, %s.\n", iface, wine_dbgstr_longlong(position)); + + return IMFByteStream_Seek(iface, msoBegin, position, 0, &new_pos); +} + +static const IMFByteStreamVtbl bytestream_http_vtbl = +{ + bytestream_http_QueryInterface, + bytestream_http_AddRef, + bytestream_http_Release, + bytestream_http_GetCapabilities, + bytestream_http_GetLength, + bytestream_http_SetLength, + bytestream_http_GetCurrentPosition, + bytestream_http_SetCurrentPosition, + bytestream_http_IsEndOfStream, + bytestream_http_Read, + bytestream_http_BeginRead, + bytestream_http_EndRead, + bytestream_http_Write, + bytestream_http_BeginWrite, + bytestream_http_EndWrite, + bytestream_http_Seek, + bytestream_http_Flush, + bytestream_http_Close, +}; + +static HRESULT WINAPI bytestream_http_attributes_QueryInterface(IMFAttributes *iface, REFIID riid, void **out) +{ + struct bytestream_http *object = impl_from_IMFAttributes(iface); + return IMFByteStream_QueryInterface(&object->IMFByteStream_iface, riid, out); +} + +static ULONG WINAPI bytestream_http_attributes_AddRef(IMFAttributes *iface) +{ + struct bytestream_http *object = impl_from_IMFAttributes(iface); + return IMFByteStream_AddRef(&object->IMFByteStream_iface); +} + +static ULONG WINAPI bytestream_http_attributes_Release(IMFAttributes *iface) +{ + struct bytestream_http *object = impl_from_IMFAttributes(iface); + return IMFByteStream_Release(&object->IMFByteStream_iface); +} + +HRESULT WINAPI mfattributes_GetItem(IMFAttributes *iface, REFGUID key, PROPVARIANT *value); +HRESULT WINAPI mfattributes_GetItemType(IMFAttributes *iface, REFGUID key, MF_ATTRIBUTE_TYPE *type); +HRESULT WINAPI mfattributes_CompareItem(IMFAttributes *iface, REFGUID key, REFPROPVARIANT value, BOOL *result); +HRESULT WINAPI mfattributes_Compare(IMFAttributes *iface, IMFAttributes *theirs, + MF_ATTRIBUTES_MATCH_TYPE match_type, BOOL *ret); +HRESULT WINAPI mfattributes_GetUINT32(IMFAttributes *iface, REFGUID key, UINT32 *value); +HRESULT WINAPI mfattributes_GetUINT64(IMFAttributes *iface, REFGUID key, UINT64 *value); +HRESULT WINAPI mfattributes_GetDouble(IMFAttributes *iface, REFGUID key, double *value); +HRESULT WINAPI mfattributes_GetGUID(IMFAttributes *iface, REFGUID key, GUID *value); +HRESULT WINAPI mfattributes_GetStringLength(IMFAttributes *iface, REFGUID key, UINT32 *length); +HRESULT WINAPI mfattributes_GetString(IMFAttributes *iface, REFGUID key, WCHAR *value, + UINT32 size, UINT32 *length); +HRESULT WINAPI mfattributes_GetAllocatedString(IMFAttributes *iface, REFGUID key, WCHAR **value, UINT32 *length); +HRESULT WINAPI mfattributes_GetBlobSize(IMFAttributes *iface, REFGUID key, UINT32 *size); +HRESULT WINAPI mfattributes_GetBlob(IMFAttributes *iface, REFGUID key, UINT8 *buf, + UINT32 bufsize, UINT32 *blobsize); +HRESULT WINAPI mfattributes_GetAllocatedBlob(IMFAttributes *iface, REFGUID key, UINT8 **buf, UINT32 *size); +HRESULT WINAPI mfattributes_GetUnknown(IMFAttributes *iface, REFGUID key, REFIID riid, void **out); +HRESULT WINAPI mfattributes_SetItem(IMFAttributes *iface, REFGUID key, REFPROPVARIANT value); +HRESULT WINAPI mfattributes_DeleteItem(IMFAttributes *iface, REFGUID key); +HRESULT WINAPI mfattributes_DeleteAllItems(IMFAttributes *iface); +HRESULT WINAPI mfattributes_SetUINT32(IMFAttributes *iface, REFGUID key, UINT32 value); +HRESULT WINAPI mfattributes_SetUINT64(IMFAttributes *iface, REFGUID key, UINT64 value); +HRESULT WINAPI mfattributes_SetDouble(IMFAttributes *iface, REFGUID key, double value); +HRESULT WINAPI mfattributes_SetGUID(IMFAttributes *iface, REFGUID key, REFGUID value); +HRESULT WINAPI mfattributes_SetString(IMFAttributes *iface, REFGUID key, const WCHAR *value); +HRESULT WINAPI mfattributes_SetBlob(IMFAttributes *iface, REFGUID key, const UINT8 *buf, UINT32 size); +HRESULT WINAPI mfattributes_SetUnknown(IMFAttributes *iface, REFGUID key, IUnknown *unknown); +HRESULT WINAPI mfattributes_LockStore(IMFAttributes *iface); +HRESULT WINAPI mfattributes_UnlockStore(IMFAttributes *iface); +HRESULT WINAPI mfattributes_GetCount(IMFAttributes *iface, UINT32 *count); +HRESULT WINAPI mfattributes_GetItemByIndex(IMFAttributes *iface, UINT32 index, GUID *key, PROPVARIANT *value); +HRESULT WINAPI mfattributes_CopyAllItems(IMFAttributes *iface, IMFAttributes *dest); + +static const IMFAttributesVtbl bytestream_http_attributes_vtbl = +{ + bytestream_http_attributes_QueryInterface, + bytestream_http_attributes_AddRef, + bytestream_http_attributes_Release, + mfattributes_GetItem, + mfattributes_GetItemType, + mfattributes_CompareItem, + mfattributes_Compare, + mfattributes_GetUINT32, + mfattributes_GetUINT64, + mfattributes_GetDouble, + mfattributes_GetGUID, + mfattributes_GetStringLength, + mfattributes_GetString, + mfattributes_GetAllocatedString, + mfattributes_GetBlobSize, + mfattributes_GetBlob, + mfattributes_GetAllocatedBlob, + mfattributes_GetUnknown, + mfattributes_SetItem, + mfattributes_DeleteItem, + mfattributes_DeleteAllItems, + mfattributes_SetUINT32, + mfattributes_SetUINT64, + mfattributes_SetDouble, + mfattributes_SetGUID, + mfattributes_SetString, + mfattributes_SetBlob, + mfattributes_SetUnknown, + mfattributes_LockStore, + mfattributes_UnlockStore, + mfattributes_GetCount, + mfattributes_GetItemByIndex, + mfattributes_CopyAllItems +}; + +static HRESULT WINAPI bytestream_http_IMFByteStreamCacheControl_QueryInterface(IMFByteStreamCacheControl *iface, + REFIID riid, void **obj) +{ + struct bytestream_http *object = impl_from_IMFByteStreamCacheControl(iface); + return IMFByteStream_QueryInterface(&object->IMFByteStream_iface, riid, obj); +} + +static ULONG WINAPI bytestream_http_IMFByteStreamCacheControl_AddRef(IMFByteStreamCacheControl *iface) +{ + struct bytestream_http *object = impl_from_IMFByteStreamCacheControl(iface); + return IMFByteStream_AddRef(&object->IMFByteStream_iface); +} + +static ULONG WINAPI bytestream_http_IMFByteStreamCacheControl_Release(IMFByteStreamCacheControl *iface) +{ + struct bytestream_http *object = impl_from_IMFByteStreamCacheControl(iface); + return IMFByteStream_Release(&object->IMFByteStream_iface); +} + +static HRESULT WINAPI bytestream_http_IMFByteStreamCacheControl_StopBackgroundTransfer(IMFByteStreamCacheControl *iface) +{ + struct bytestream_http *object = impl_from_IMFByteStreamCacheControl(iface); + + TRACE("%p.\n", iface); + + IMFByteStreamBuffering_StopBuffering(&object->IMFByteStreamBuffering_iface); + return S_OK; +} + +static const IMFByteStreamCacheControlVtbl bytestream_http_IMFByteStreamCacheControl_vtbl = +{ + bytestream_http_IMFByteStreamCacheControl_QueryInterface, + bytestream_http_IMFByteStreamCacheControl_AddRef, + bytestream_http_IMFByteStreamCacheControl_Release, + bytestream_http_IMFByteStreamCacheControl_StopBackgroundTransfer, +}; + +static HRESULT WINAPI bytestream_http_IMFByteStreamBuffering_QueryInterface(IMFByteStreamBuffering *iface, + REFIID riid, void **obj) +{ + struct bytestream_http *object = impl_from_IMFByteStreamBuffering(iface); + return IMFByteStream_QueryInterface(&object->IMFByteStream_iface, riid, obj); +} + +static ULONG WINAPI bytestream_http_IMFByteStreamBuffering_AddRef(IMFByteStreamBuffering *iface) +{ + struct bytestream_http *object = impl_from_IMFByteStreamBuffering(iface); + return IMFByteStream_AddRef(&object->IMFByteStream_iface); +} + +static ULONG WINAPI bytestream_http_IMFByteStreamBuffering_Release(IMFByteStreamBuffering *iface) +{ + struct bytestream_http *object = impl_from_IMFByteStreamBuffering(iface); + return IMFByteStream_Release(&object->IMFByteStream_iface); +} + +static HRESULT WINAPI bytestream_http_IMFByteStreamBuffering_SetBufferingParams(IMFByteStreamBuffering *iface, + MFBYTESTREAM_BUFFERING_PARAMS *params) +{ + FIXME("%p, %p: semi-stub.\n", iface, params); + + if (!params || (params->cBuckets > 0 && !params->prgBuckets)) + return E_INVALIDARG; + + /* It's not clear to me how these parameters are used, it's probably fine to not implement them */ + return E_NOTIMPL; +} + +static HRESULT send_buffering_event(struct bytestream_http *object, BOOL buffering) +{ + MediaEventType event_type = buffering ? MEBufferingStarted : MEBufferingStopped; + PROPVARIANT data = {.vt = VT_EMPTY}; + GUID ext_type = GUID_NULL; + HRESULT hr; + + TRACE("%p, %d.\n", object, buffering); + + if (!object->buffering_enabled) + return S_FALSE; + + if (FAILED(hr = IMFMediaEventQueue_QueueEventParamVar(object->event_queue, event_type, &ext_type, S_OK, &data))) + ERR("Failed to enqueue event %lx, hr %#lx.\n", event_type, hr); + + return hr; +} + +static HRESULT update_buffering_status(struct bytestream_http *object) +{ + BOOL prev = object->buffering; + + TRACE("%p.\n", object); + + object->buffering = is_range_available(object, object->position, 65536); + if (object->buffering == prev) + return S_OK; + return send_buffering_event(object, object->buffering); +} + +static HRESULT WINAPI bytestream_http_IMFByteStreamBuffering_EnableBuffering(IMFByteStreamBuffering *iface, + BOOL enable) +{ + struct bytestream_http *object = impl_from_IMFByteStreamBuffering(iface); + + TRACE("%p, %d.\n", iface, enable); + + EnterCriticalSection(&object->cs); + if (!enable && object->buffering) + send_buffering_event(object, FALSE); + object->buffering_enabled = enable; + if (enable && object->buffering) + send_buffering_event(object, TRUE); + LeaveCriticalSection(&object->cs); + return S_OK; +} + +static HRESULT WINAPI bytestream_http_IMFByteStreamBuffering_StopBuffering(IMFByteStreamBuffering *iface) +{ + struct bytestream_http *object = impl_from_IMFByteStreamBuffering(iface); + + TRACE("%p.\n", iface); + + EnterCriticalSection(&object->cs); + + if (!object->buffering_enabled || !object->request) + { + LeaveCriticalSection(&object->cs); + return S_FALSE; + } + + if (!object->has_pending_read) + { + close_connection(object, TRUE); + if (object->buffering) + { + object->buffering = FALSE; + send_buffering_event(object, FALSE); + } + } + + LeaveCriticalSection(&object->cs); + return S_OK; +} + +static const IMFByteStreamBufferingVtbl bytestream_http_IMFByteStreamBuffering_vtbl = +{ + bytestream_http_IMFByteStreamBuffering_QueryInterface, + bytestream_http_IMFByteStreamBuffering_AddRef, + bytestream_http_IMFByteStreamBuffering_Release, + bytestream_http_IMFByteStreamBuffering_SetBufferingParams, + bytestream_http_IMFByteStreamBuffering_EnableBuffering, + bytestream_http_IMFByteStreamBuffering_StopBuffering, +}; + +static HRESULT WINAPI bytestream_http_IMFByteStreamTimeSeek_QueryInterface(IMFByteStreamTimeSeek *iface, + REFIID riid, void **obj) +{ + struct bytestream_http *object = impl_from_IMFByteStreamTimeSeek(iface); + return IMFByteStream_QueryInterface(&object->IMFByteStream_iface, riid, obj); +} + +static ULONG WINAPI bytestream_http_IMFByteStreamTimeSeek_AddRef(IMFByteStreamTimeSeek *iface) +{ + struct bytestream_http *object = impl_from_IMFByteStreamTimeSeek(iface); + return IMFByteStream_AddRef(&object->IMFByteStream_iface); +} + +static ULONG WINAPI bytestream_http_IMFByteStreamTimeSeek_Release(IMFByteStreamTimeSeek *iface) +{ + struct bytestream_http *object = impl_from_IMFByteStreamTimeSeek(iface); + return IMFByteStream_Release(&object->IMFByteStream_iface); +} + +static HRESULT WINAPI bytestream_http_IMFByteStreamTimeSeek_IsTimeSeekSupported(IMFByteStreamTimeSeek *iface, + BOOL *result) +{ + TRACE("%p, %p.\n", iface, result); + + if (result) + *result = FALSE; + return S_FALSE; +} + +static HRESULT WINAPI bytestream_http_IMFByteStreamTimeSeek_TimeSeek(IMFByteStreamTimeSeek *iface, QWORD position) +{ + TRACE("%p, %s.\n", iface, wine_dbgstr_longlong(position)); + + return MF_E_INVALIDREQUEST; +} + +static HRESULT WINAPI bytestream_http_IMFByteStreamTimeSeek_GetTimeSeekResult(IMFByteStreamTimeSeek *iface, + QWORD *start_time, QWORD *stop_time, QWORD *duration) +{ + TRACE("%p, %p, %p, %p.\n", iface, start_time, stop_time, duration); + + if (!start_time || !stop_time || !duration) + return E_INVALIDARG; + + *start_time = 0; + *stop_time = 0; + *duration = 0; + return MF_E_INVALIDREQUEST; +} + +static const IMFByteStreamTimeSeekVtbl bytestream_http_IMFByteStreamTimeSeek_vtbl = +{ + bytestream_http_IMFByteStreamTimeSeek_QueryInterface, + bytestream_http_IMFByteStreamTimeSeek_AddRef, + bytestream_http_IMFByteStreamTimeSeek_Release, + bytestream_http_IMFByteStreamTimeSeek_IsTimeSeekSupported, + bytestream_http_IMFByteStreamTimeSeek_TimeSeek, + bytestream_http_IMFByteStreamTimeSeek_GetTimeSeekResult, +}; + +static HRESULT WINAPI bytestream_http_IMFMediaEventGenerator_QueryInterface(IMFMediaEventGenerator *iface, REFIID riid, + void **obj) +{ + struct bytestream_http *object = impl_from_IMFMediaEventGenerator(iface); + return IMFByteStream_QueryInterface(&object->IMFByteStream_iface, riid, obj); +} + +static ULONG WINAPI bytestream_http_IMFMediaEventGenerator_AddRef(IMFMediaEventGenerator *iface) +{ + struct bytestream_http *object = impl_from_IMFMediaEventGenerator(iface); + return IMFByteStream_AddRef(&object->IMFByteStream_iface); +} + +static ULONG WINAPI bytestream_http_IMFMediaEventGenerator_Release(IMFMediaEventGenerator *iface) +{ + struct bytestream_http *object = impl_from_IMFMediaEventGenerator(iface); + return IMFByteStream_Release(&object->IMFByteStream_iface); +} + +static HRESULT WINAPI bytestream_http_IMFMediaEventGenerator_GetEvent(IMFMediaEventGenerator *iface, DWORD flags, + IMFMediaEvent **event) +{ + struct bytestream_http *object = impl_from_IMFMediaEventGenerator(iface); + + TRACE("%p, %#lx, %p.\n", iface, flags, event); + + return IMFMediaEventQueue_GetEvent(object->event_queue, flags, event); +} + +static HRESULT WINAPI bytestream_http_IMFMediaEventGenerator_BeginGetEvent(IMFMediaEventGenerator *iface, + IMFAsyncCallback *callback, IUnknown *state) +{ + struct bytestream_http *object = impl_from_IMFMediaEventGenerator(iface); + + TRACE("%p, %p, %p.\n", iface, callback, state); + + return IMFMediaEventQueue_BeginGetEvent(object->event_queue, callback, state); +} + +static HRESULT WINAPI bytestream_http_IMFMediaEventGenerator_EndGetEvent(IMFMediaEventGenerator *iface, + IMFAsyncResult *result, IMFMediaEvent **event) +{ + struct bytestream_http *object = impl_from_IMFMediaEventGenerator(iface); + + TRACE("%p, %p, %p.\n", iface, result, event); + + return IMFMediaEventQueue_EndGetEvent(object->event_queue, result, event); +} + +static HRESULT WINAPI bytestream_http_IMFMediaEventGenerator_QueueEvent(IMFMediaEventGenerator *iface, + MediaEventType type, REFGUID ext_type, HRESULT hr, const PROPVARIANT *value) +{ + struct bytestream_http *object = impl_from_IMFMediaEventGenerator(iface); + + TRACE("%p, %ld, %s, %#lx, %s.\n", iface, type, debugstr_guid(ext_type), hr, debugstr_propvar(value)); + + return IMFMediaEventQueue_QueueEventParamVar(object->event_queue, type, ext_type, hr, value); +} + +static const IMFMediaEventGeneratorVtbl bytestream_http_IMFMediaEventGenerator_vtbl = +{ + bytestream_http_IMFMediaEventGenerator_QueryInterface, + bytestream_http_IMFMediaEventGenerator_AddRef, + bytestream_http_IMFMediaEventGenerator_Release, + bytestream_http_IMFMediaEventGenerator_GetEvent, + bytestream_http_IMFMediaEventGenerator_BeginGetEvent, + bytestream_http_IMFMediaEventGenerator_EndGetEvent, + bytestream_http_IMFMediaEventGenerator_QueueEvent, +}; + +static HRESULT WINAPI bytestream_http_IPropertyStore_QueryInterface(IPropertyStore *iface, + REFIID riid, void **obj) +{ + struct bytestream_http *object = impl_from_IPropertyStore(iface); + return IMFByteStream_QueryInterface(&object->IMFByteStream_iface, riid, obj); +} + +static ULONG WINAPI bytestream_http_IPropertyStore_AddRef(IPropertyStore *iface) +{ + struct bytestream_http *object = impl_from_IPropertyStore(iface); + return IMFByteStream_AddRef(&object->IMFByteStream_iface); +} + +static ULONG WINAPI bytestream_http_IPropertyStore_Release(IPropertyStore *iface) +{ + struct bytestream_http *object = impl_from_IPropertyStore(iface); + return IMFByteStream_Release(&object->IMFByteStream_iface); +} + +static HRESULT WINAPI bytestream_http_IPropertyStore_GetCount(IPropertyStore *iface, DWORD *count) +{ + struct bytestream_http *object = impl_from_IPropertyStore(iface); + + TRACE("%p, %p.\n", iface, count); + + return IPropertyStore_GetCount(object->propstore, count); +} + +static HRESULT WINAPI bytestream_http_IPropertyStore_GetAt(IPropertyStore *iface, DWORD prop, PROPERTYKEY *key) +{ + struct bytestream_http *object = impl_from_IPropertyStore(iface); + + TRACE("%p, %lu, %p.\n", iface, prop, key); + + return IPropertyStore_GetAt(object->propstore, prop, key); +} + +static HRESULT WINAPI bytestream_http_IPropertyStore_GetValue(IPropertyStore *iface, REFPROPERTYKEY key, PROPVARIANT *value) +{ + struct bytestream_http *object = impl_from_IPropertyStore(iface); + + TRACE("%p, %p, %p.\n", iface, key, value); + + return IPropertyStore_GetValue(object->propstore, key, value); +} + +static HRESULT WINAPI bytestream_http_IPropertyStore_SetValue(IPropertyStore *iface, REFPROPERTYKEY key, REFPROPVARIANT value) +{ + struct bytestream_http *object = impl_from_IPropertyStore(iface); + + TRACE("%p, %p, %p.\n", iface, key, value); + + return IPropertyStore_SetValue(object->propstore, key, value); +} + +static HRESULT WINAPI bytestream_http_IPropertyStore_Commit(IPropertyStore *iface) +{ + struct bytestream_http *object = impl_from_IPropertyStore(iface); + + TRACE("%p.\n", iface); + + return IPropertyStore_Commit(object->propstore); +} + +static const IPropertyStoreVtbl bytestream_http_IPropertyStore_vtbl = +{ + bytestream_http_IPropertyStore_QueryInterface, + bytestream_http_IPropertyStore_AddRef, + bytestream_http_IPropertyStore_Release, + bytestream_http_IPropertyStore_GetCount, + bytestream_http_IPropertyStore_GetAt, + bytestream_http_IPropertyStore_GetValue, + bytestream_http_IPropertyStore_SetValue, + bytestream_http_IPropertyStore_Commit, +}; + +static HRESULT WINAPI bytestream_http_IMFGetService_QueryInterface(IMFGetService *iface, REFIID riid, void **obj) +{ + struct bytestream_http *object = impl_from_IMFGetService(iface); + return IMFByteStream_QueryInterface(&object->IMFByteStream_iface, riid, obj); +} + +static ULONG WINAPI bytestream_http_IMFGetService_AddRef(IMFGetService *iface) +{ + struct bytestream_http *object = impl_from_IMFGetService(iface); + return IMFByteStream_AddRef(&object->IMFByteStream_iface); +} + +static ULONG WINAPI bytestream_http_IMFGetService_Release(IMFGetService *iface) +{ + struct bytestream_http *object = impl_from_IMFGetService(iface); + return IMFByteStream_Release(&object->IMFByteStream_iface); +} + +static HRESULT WINAPI bytestream_http_IMFGetService_GetService(IMFGetService *iface, REFGUID service, + REFIID riid, void **obj) +{ + struct bytestream_http *object = impl_from_IMFGetService(iface); + + TRACE("%p, %s, %s, %p.\n", iface, debugstr_guid(service), debugstr_guid(riid), obj); + + if (IsEqualGUID(service, &MFNETSOURCE_STATISTICS_SERVICE) && IsEqualIID(riid, &IID_IPropertyStore)) + return IMFByteStream_QueryInterface(&object->IMFByteStream_iface, riid, obj); + + return MF_E_UNSUPPORTED_SERVICE; +} + +static const IMFGetServiceVtbl bytestream_http_IMFGetService_vtbl = +{ + bytestream_http_IMFGetService_QueryInterface, + bytestream_http_IMFGetService_AddRef, + bytestream_http_IMFGetService_Release, + bytestream_http_IMFGetService_GetService, +}; + +static BOOL is_range_available(struct bytestream_http *object, ULONGLONG start, ULONGLONG count) +{ + struct downloaded_range *entry; + ULONGLONG end = start + count; + + if (object->has_length && end > object->content_length) + end = object->content_length; + + LIST_FOR_EACH_ENTRY(entry, &object->downloaded_ranges, struct downloaded_range, entry) + { + if (entry->start <= start && entry->end >= end) + return TRUE; + if (entry->start >= end) + break; + } + return FALSE; +} + +static ULONGLONG find_range_end(struct bytestream_http *object, ULONGLONG start) +{ + struct downloaded_range *entry; + + LIST_FOR_EACH_ENTRY(entry, &object->downloaded_ranges, struct downloaded_range, entry) + { + if (entry->start <= start && entry->end >= start) + return entry->end; + } + return -1ULL; +} + +static ULONGLONG find_hole_end(struct bytestream_http *object, ULONGLONG start) +{ + struct downloaded_range *entry; + + LIST_FOR_EACH_ENTRY(entry, &object->downloaded_ranges, struct downloaded_range, entry) + { + if (entry->start >= start) + return entry->start; + } + return -1ULL; +} + +static void mark_range_valid(struct bytestream_http *object, ULONGLONG start, ULONGLONG count) +{ + struct downloaded_range *entry, *new_entry; + ULONGLONG end = start + count; + + TRACE("%p %I64u %I64u\n", object, start, end); + + LIST_FOR_EACH_ENTRY(entry, &object->downloaded_ranges, struct downloaded_range, entry) + { + struct downloaded_range *next = LIST_ENTRY(entry->entry.next, struct downloaded_range, entry); + + if (start >= entry->start && start <= entry->end) + { + if (end > entry->end) + entry->end = end - entry->start; + + if (&next->entry != &object->downloaded_ranges && next->start <= entry->end) + { + entry->end = next->end; + list_remove(&next->entry); + free(next); + } + + return; + } + else if (&next->entry != &object->downloaded_ranges && start < next->start) + break; + } + + new_entry = calloc(1, sizeof(*new_entry)); + list_init(&new_entry->entry); + new_entry->start = start; + new_entry->end = end; + list_add_before(&entry->entry, &new_entry->entry); +} + +static BOOL parse_content_range(struct bytestream_http *object, WCHAR *value) +{ + static const WCHAR bytesW[] = L"bytes "; + + ULONGLONG start, end, len; + WCHAR *ptr; + + if (wcsncmp(value, bytesW, ARRAY_SIZE(bytesW) - 1) || value[6] == '-') + return FALSE; + ptr = value + 6; + if (!wcsncmp(ptr, L"*/", 2)) + { + start = 0; + end = -1ULL; + ptr++; + } + else + { + start = wcstoull(ptr, &ptr, 10); + if (*ptr != '-') + return FALSE; + if (ptr[1] == '/') + end = -1ULL; + else + end = wcstoull(ptr + 1, &ptr, 10); + } + if (*ptr != '/' || ptr[1] == 0) + return FALSE; + if (ptr[1] != '*') + { + len = wcstoull(ptr + 1, &ptr, 10); + if (*ptr != 0) + return FALSE; + object->has_length = TRUE; + object->content_length = len; + } + object->content_range_start = start; + object->content_range_end = end; + return TRUE; +} + +static inline void handle_winhttp_error(struct bytestream_http *object, DWORD err) +{ + HRESULT hr = HRESULT_FROM_WIN32(err); + + TRACE("error %#lx\n", err); + + if (err == ERROR_INVALID_HANDLE && object->closing_connnection) + return; + + if (err == ERROR_WINHTTP_NAME_NOT_RESOLVED || err == ERROR_WINHTTP_CANNOT_CONNECT) + hr = NS_E_SERVER_NOT_FOUND; + else if (err == ERROR_WINHTTP_CONNECTION_ERROR || err == ERROR_WINHTTP_TIMEOUT) + hr = NS_E_CONNECTION_FAILURE; + + if (!object->error) + { + object->error = hr; + WakeAllConditionVariable(&object->cv); + } +} + +static inline void handle_http_error(struct bytestream_http *object, DWORD status) +{ + HRESULT hr = map_http_error(status); + + TRACE("status %ld error %#lx\n", status, hr); + + if (!object->error) + { + object->error = hr; + WakeAllConditionVariable(&object->cv); + } +} + +static BOOL parse_headers(struct bytestream_http *object, HINTERNET handle) +{ + WCHAR buffer[1024]; + DWORD len; + + if (!object->content_type) + { + len = sizeof(buffer); + if (WinHttpQueryHeaders(handle, WINHTTP_QUERY_CONTENT_TYPE, WINHTTP_HEADER_NAME_BY_INDEX, + buffer, &len, WINHTTP_NO_HEADER_INDEX)) + { + object->content_type = wcsdup(buffer); + } + else if (GetLastError() == ERROR_WINHTTP_HEADER_NOT_FOUND) + { + object->content_type = NULL; + } + else + { + handle_winhttp_error(object, GetLastError()); + return FALSE; + } + } + + if (object->content_length == -1ULL) + { + len = sizeof(buffer); + if (WinHttpQueryHeaders(handle, WINHTTP_QUERY_CONTENT_LENGTH, WINHTTP_HEADER_NAME_BY_INDEX, + buffer, &len, WINHTTP_NO_HEADER_INDEX)) + { + WCHAR *end = buffer; + object->content_length = wcstoull(buffer, &end, 10); + if (end == buffer) + { + handle_winhttp_error(object, ERROR_WINHTTP_INVALID_HEADER); + WakeAllConditionVariable(&object->cv); + return FALSE; + } + else + object->has_length = TRUE; + } + else if (GetLastError() == ERROR_WINHTTP_HEADER_NOT_FOUND) + { + object->content_length = -1ULL; + } + else + { + handle_winhttp_error(object, GetLastError()); + return FALSE; + } + } + + if (object->last_modified == -1ULL) + { + len = sizeof(buffer); + if (WinHttpQueryHeaders(handle, WINHTTP_QUERY_LAST_MODIFIED, WINHTTP_HEADER_NAME_BY_INDEX, + buffer, &len, WINHTTP_NO_HEADER_INDEX)) + { + ULARGE_INTEGER ul = {0}; + SYSTEMTIME st = {0}; + FILETIME ft = {0}; + + if (!WinHttpTimeToSystemTime(buffer, &st) || !SystemTimeToFileTime(&st, &ft)) + { + handle_winhttp_error(object, ERROR_WINHTTP_INVALID_HEADER); + return FALSE; + } + ul.u.LowPart = ft.dwLowDateTime; + ul.u.HighPart = ft.dwHighDateTime; + object->last_modified = ul.QuadPart; + } + else if (GetLastError() == ERROR_WINHTTP_HEADER_NOT_FOUND) + { + object->last_modified = -1ULL; + } + else + { + handle_winhttp_error(object, GetLastError()); + return FALSE; + } + } + + len = sizeof(buffer); + if (WinHttpQueryHeaders(handle, WINHTTP_QUERY_CONTENT_RANGE, WINHTTP_HEADER_NAME_BY_INDEX, + buffer, &len, WINHTTP_NO_HEADER_INDEX)) + { + parse_content_range(object, buffer); + object->current_write_pos = object->content_range_start; + } + else if (GetLastError() == ERROR_WINHTTP_HEADER_NOT_FOUND) + { + object->content_range_start = 0; + object->content_range_end = -1ULL; + object->current_write_pos = 0; + } + else + { + handle_winhttp_error(object, GetLastError()); + return FALSE; + } + return TRUE; +} + +static void CALLBACK progress_callback_http(HINTERNET handle, DWORD_PTR context, DWORD status, LPVOID buf, DWORD buflen) +{ + struct bytestream_http *object = (void *)context; + + TRACE("%p, %lx, %p, %lu\n", handle, status, buf, buflen); + + switch (status) + { + case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE: + { + EnterCriticalSection(&object->cs); + + if (object->error || object->closing_connnection) + { + LeaveCriticalSection(&object->cs); + return; + } + + if (!WinHttpReceiveResponse(handle, NULL)) + handle_winhttp_error(object, GetLastError()); + + LeaveCriticalSection(&object->cs); + break; + } + + case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE: + { + DWORD status = 0, len = sizeof(status); + + EnterCriticalSection(&object->cs); + + if (object->error || object->closing_connnection) + { + LeaveCriticalSection(&object->cs); + return; + } + + if (!WinHttpQueryHeaders(handle, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, + WINHTTP_HEADER_NAME_BY_INDEX, &status, &len, WINHTTP_NO_HEADER_INDEX)) + handle_winhttp_error(object, GetLastError()); + else if (status < 200 || status >= 300) + handle_http_error(object, status); + else if (parse_headers(object, handle) && !WinHttpQueryDataAvailable(handle, NULL)) + handle_winhttp_error(object, GetLastError()); + + object->parsed_headers = TRUE; + LeaveCriticalSection(&object->cs); + WakeAllConditionVariable(&object->cv); + break; + } + + case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE: + { + DWORD *len = buf; + DWORD to_read = *len < sizeof(object->buffer) ? *len : sizeof(object->buffer); + ULONGLONG end; + BOOL ret; + + EnterCriticalSection(&object->cs); + + if (object->error || object->closing_connnection) + { + LeaveCriticalSection(&object->cs); + return; + } + + if (*len > 0 && !object->read_active) + { + object->read_active = TRUE; + LeaveCriticalSection(&object->cs); + ret = WinHttpReadData(handle, object->buffer, to_read, &object->bytes_read); + EnterCriticalSection(&object->cs); + if (!ret) + { + object->read_active = FALSE; + handle_winhttp_error(object, GetLastError()); + } + } + if (*len == 0) + { + end = find_range_end(object, object->current_write_pos); + if (end == -1ULL && (object->content_range_end == -1ULL || + object->current_write_pos == object->content_range_end)) + { + if (!object->has_length || object->current_write_pos > object->content_length) + object->content_length = object->current_write_pos; + if (!object->has_length) + { + object->has_length = TRUE; + send_characteristics_changed_event(object); + } + WakeAllConditionVariable(&object->cv); + } + } + + LeaveCriticalSection(&object->cs); + break; + } + + case WINHTTP_CALLBACK_STATUS_READ_COMPLETE: + { + OVERLAPPED ovl = {0}; + DWORD written = 0, prev_caps = 0, new_caps = 0; + BOOL ret; + + EnterCriticalSection(&object->cs); + + object->read_active = FALSE; + + if (object->error || object->closing_connnection) + { + LeaveCriticalSection(&object->cs); + return; + } + + ovl.Internal = -1; + ovl.InternalHigh = -1; + ovl.Offset = (DWORD)object->current_write_pos; + ovl.OffsetHigh = (DWORD)(object->current_write_pos >> 32); + if (!WriteFile(object->cache_file, buf, buflen, &written, &ovl) || written < buflen) + { + if (!object->error) + object->error = HRESULT_FROM_WIN32(GetLastError()); + } + else + { + bytestream_http_GetCapabilities(&object->IMFByteStream_iface, &prev_caps); + + mark_range_valid(object, object->current_write_pos, written); + object->current_write_pos += written; + + update_buffering_status(object); + + bytestream_http_GetCapabilities(&object->IMFByteStream_iface, &new_caps); + if (prev_caps != new_caps) + send_characteristics_changed_event(object); + + LeaveCriticalSection(&object->cs); + ret = WinHttpQueryDataAvailable(handle, NULL); + EnterCriticalSection(&object->cs); + if (!ret) + handle_winhttp_error(object, GetLastError()); + } + + LeaveCriticalSection(&object->cs); + WakeAllConditionVariable(&object->cv); + break; + } + + case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR: + { + WINHTTP_ASYNC_RESULT *result = buf; + + EnterCriticalSection(&object->cs); + + if (result->dwResult == API_READ_DATA) + object->read_active = FALSE; + + if (object->error || object->closing_connnection) + { + LeaveCriticalSection(&object->cs); + return; + } + + handle_winhttp_error(object, result->dwError); + + LeaveCriticalSection(&object->cs); + WakeAllConditionVariable(&object->cv); + break; + } + + case WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING: + { + BOOL release = TRUE; + + EnterCriticalSection(&object->cs); + + if (handle == object->request) + { + object->request = NULL; + object->read_active = FALSE; + } + else if (handle == object->connection) + object->connection = NULL; + else if (handle == object->session) + object->session = NULL; + else + release = FALSE; + + LeaveCriticalSection(&object->cs); + WakeAllConditionVariable(&object->cv); + + if (release) + IMFByteStream_Release(&object->IMFByteStream_iface); + break; + } + } +} + +/* Taken from MFCreateTempFile */ +static HRESULT create_temp_file(HANDLE *h) +{ + WCHAR name[24], tmppath[MAX_PATH], *path; + HRESULT hr = S_OK; + ULONG64 rnd; + size_t len; + + BCryptGenRandom(NULL, (UCHAR *)&rnd, sizeof(rnd), BCRYPT_USE_SYSTEM_PREFERRED_RNG); + swprintf(name, ARRAY_SIZE(name), L"MFP%llX.TMP", rnd); + GetTempPathW(ARRAY_SIZE(tmppath), tmppath); + + len = wcslen(tmppath) + wcslen(name) + 2; + if (!(path = malloc(len * sizeof(*path)))) + return E_OUTOFMEMORY; + + wcscpy(path, tmppath); + PathCchAppend(path, len, name); + + *h = CreateFileW(path, GENERIC_READ | GENERIC_WRITE | FILE_FLAG_DELETE_ON_CLOSE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (*h == INVALID_HANDLE_VALUE) + hr = HRESULT_FROM_WIN32(GetLastError()); + + free(path); + + return hr; +} + +static void close_connection(struct bytestream_http *object, BOOL wait) +{ + if (wait) + { + while (object->closing_connnection) + SleepConditionVariableCS(&object->cv, &object->cs, INFINITE); + object->closing_connnection = TRUE; + } + if (object->request && !WinHttpCloseHandle(object->request)) + object->request = NULL; + if (object->connection && !WinHttpCloseHandle(object->connection)) + object->connection = NULL; + if (object->session && !WinHttpCloseHandle(object->session)) + object->session = NULL; + if (wait) + { + while (object->request || object->connection || object->session) + SleepConditionVariableCS(&object->cv, &object->cs, INFINITE); + object->closing_connnection = FALSE; + } + object->content_range_start = -1ULL; + object->content_range_end = -1ULL; +} + +static HRESULT open_connection(struct bytestream_http *object, ULONGLONG range_start, ULONGLONG range_end) +{ + static const WCHAR *accept_all[] = { L"*/*", NULL }; + + WCHAR host[MAX_PATH], range[256]; + URL_COMPONENTS uc; + DWORD flags; + + close_connection(object, TRUE); + + range_start -= range_start % 1024; + if (range_end != -1ULL) range_end -= 1; + + memset(&uc, 0, sizeof(uc)); + uc.dwStructSize = sizeof(uc); + uc.lpszHostName = host; + uc.dwHostNameLength = ARRAY_SIZE(host); + uc.dwUrlPathLength = ~0U; + uc.dwSchemeLength = ~0U; + if (!WinHttpCrackUrl(object->url, 0, 0, &uc)) + { + uc.lpszHostName = malloc(uc.dwHostNameLength * sizeof(WCHAR)); + if (!uc.lpszHostName) + return E_OUTOFMEMORY; + if (!WinHttpCrackUrl(object->url, 0, 0, &uc)) + goto error; + } + + object->session = WinHttpOpen(L"NSPlayer/12.00.19041.4894 WMFSDK/12.00.19041.4894", + WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, + WINHTTP_FLAG_ASYNC); + if (!object->session) + goto error; + IMFByteStream_AddRef(&object->IMFByteStream_iface); + WinHttpSetStatusCallback(object->session, progress_callback_http, + WINHTTP_CALLBACK_FLAG_ALL_COMPLETIONS | WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING, 0); + if (!WinHttpSetOption(object->session, WINHTTP_OPTION_CONTEXT_VALUE, &object, sizeof(object))) + goto error; + + object->connection = WinHttpConnect(object->session, uc.lpszHostName, uc.nPort, 0); + if (!object->connection) + goto error; + IMFByteStream_AddRef(&object->IMFByteStream_iface); + + flags = WINHTTP_FLAG_REFRESH; + if (uc.nScheme == INTERNET_SCHEME_HTTPS) + flags |= WINHTTP_FLAG_SECURE; + + object->request = WinHttpOpenRequest(object->connection, NULL, uc.lpszUrlPath, NULL, WINHTTP_NO_REFERER, accept_all, flags); + if (!object->request) + goto error; + IMFByteStream_AddRef(&object->IMFByteStream_iface); + + WinHttpAddRequestHeaders(object->request, L"GetContentFeatures.DLNA.ORG: 1", (DWORD)-1L, WINHTTP_ADDREQ_FLAG_ADD); + + /* TODO: Implement persistent caching of downloaded data */ + /*if (object->last_modified != -1ULL) + { + WCHAR date[WINHTTP_TIME_FORMAT_BUFSIZE], if_modified_since[WINHTTP_TIME_FORMAT_BUFSIZE + 19]; + ULARGE_INTEGER ul = {0}; + SYSTEMTIME st = {0}; + FILETIME ft = {0}; + + ul.QuadPart = object->last_modified; + ft.dwLowDateTime = ul.u.LowPart; + ft.dwHighDateTime = ul.u.HighPart; + if (FileTimeToSystemTime(&ft, &st) && WinHttpTimeFromSystemTime(&st, date)) + { + swprintf(if_modified_since, ARRAY_SIZE(if_modified_since), L"If-Modified-Since: %s", date); + WinHttpAddRequestHeaders(object->request, if_modified_since, (DWORD)-1L, WINHTTP_ADDREQ_FLAG_ADD); + } + }*/ + + if (range_end == -1ULL) + swprintf(range, ARRAY_SIZE(range), L"Range: bytes=%I64u-", range_start); + else + swprintf(range, ARRAY_SIZE(range), L"Range: bytes=%I64u-%I64u", range_start, range_end); + if (!WinHttpAddRequestHeaders(object->request, range, (DWORD)-1L, WINHTTP_ADDREQ_FLAG_ADD)) + goto error; + + if (!WinHttpSendRequest(object->request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, + (DWORD_PTR)object)) + goto error; + + if (uc.lpszHostName != host) + free(uc.lpszHostName); + return S_OK; + +error: + handle_winhttp_error(object, GetLastError()); + if (uc.lpszHostName != host) + free(uc.lpszHostName); + close_connection(object, TRUE); + WakeAllConditionVariable(&object->cv); + return object->error; +} + +static WCHAR *normalize_uri_scheme(const WCHAR *url) +{ + struct + { + const WCHAR *scheme; + const WCHAR *replacement; + } scheme_replacements[] = + { + { L"httpd:", L"http:" }, + { L"httpsd:", L"https:" }, + { L"mms:", L"http:" } + }; + int i; + + for (i = 0; i < ARRAY_SIZE(scheme_replacements); i++) + { + size_t scheme_len = wcslen(scheme_replacements[i].scheme); + if (!wcsncmp(url, scheme_replacements[i].scheme, scheme_len)) + { + size_t replacement_len = wcslen(scheme_replacements[i].replacement); + size_t url_len = wcslen(url); + WCHAR *new_url = malloc((url_len - scheme_len + replacement_len + 1) * sizeof(*new_url)); + if (!new_url) + return NULL; + wcscpy(new_url, scheme_replacements[i].replacement); + wcscat(new_url, url + scheme_len); + return new_url; + } + } + return wcsdup(url); +} + +static WCHAR *recreate_url(const WCHAR *url) +{ + WCHAR host[MAX_PATH], port_str[6]; + URL_COMPONENTS uc; + WCHAR *out; + + url = normalize_uri_scheme(url); + if (!url) + return NULL; + + memset(&uc, 0, sizeof(uc)); + uc.dwStructSize = sizeof(uc); + uc.lpszHostName = host; + uc.dwHostNameLength = ARRAY_SIZE(host); + uc.dwUrlPathLength = ~0U; + uc.dwSchemeLength = ~0U; + if (!WinHttpCrackUrl(url, 0, 0, &uc)) + { + uc.lpszHostName = malloc(uc.dwHostNameLength * sizeof(WCHAR)); + if (!uc.lpszHostName) + goto error; + if (!WinHttpCrackUrl(url, 0, 0, &uc)) + goto error; + } + + out = malloc((8 + wcslen(uc.lpszHostName) + 1 + 5 + wcslen(uc.lpszUrlPath) + 1) * sizeof(WCHAR)); + if (!out) + goto error; + + out[0] = 0; + if (uc.nScheme == INTERNET_SCHEME_HTTPS) + wcscat(out, L"https://"); + else + wcscat(out, L"http://"); + wcscat(out, uc.lpszHostName); + wcscat(out, L":"); + swprintf(port_str, ARRAY_SIZE(port_str), L"%u", uc.nPort); + wcscat(out, port_str); + wcscat(out, uc.lpszUrlPath); + free((void *)url); + return out; + +error: + if (uc.lpszHostName != host) + free(uc.lpszHostName); + free((void *)url); + return NULL; +} + +HRESULT create_http_bytestream(const WCHAR *url, void **out) +{ + struct bytestream_http *object; + ULARGE_INTEGER ul = {0}; + FILETIME ft = {0}; + HRESULT hr; + + TRACE("%s, %p.\n", debugstr_w(url), out); + + if (!(object = calloc(1, sizeof(*object)))) + return E_OUTOFMEMORY; + + if (FAILED(hr = init_attributes_object(&object->attributes, 0))) + { + free(object); + return hr; + } + object->IMFByteStream_iface.lpVtbl = &bytestream_http_vtbl; + object->attributes.IMFAttributes_iface.lpVtbl = &bytestream_http_attributes_vtbl; + object->IMFByteStreamCacheControl_iface.lpVtbl = &bytestream_http_IMFByteStreamCacheControl_vtbl; + object->IMFByteStreamBuffering_iface.lpVtbl = &bytestream_http_IMFByteStreamBuffering_vtbl; + object->IMFMediaEventGenerator_iface.lpVtbl = &bytestream_http_IMFMediaEventGenerator_vtbl; + object->IMFByteStreamTimeSeek_iface.lpVtbl = &bytestream_http_IMFByteStreamTimeSeek_vtbl; + object->IPropertyStore_iface.lpVtbl = &bytestream_http_IPropertyStore_vtbl; + object->IMFGetService_iface.lpVtbl = &bytestream_http_IMFGetService_vtbl; + InitializeCriticalSection(&object->cs); + InitializeConditionVariable(&object->cv); + object->read_callback.lpVtbl = &bytestream_http_read_callback_vtbl; + list_init(&object->pending); + object->url = recreate_url(url); + object->content_length = -1ULL; + object->last_modified = -1ULL; + object->content_range_end = -1ULL; + object->buffering = TRUE; + list_init(&object->downloaded_ranges); + + if (!object->url) + goto error; + + if (FAILED(hr = CreatePropertyStore(&object->propstore)) || + FAILED(hr = MFCreateEventQueue(&object->event_queue))) + goto error; + + if (FAILED(hr = create_temp_file(&object->cache_file))) + { + ERR("Couldn't create temp file: %lx\n", hr); + goto error; + } + + EnterCriticalSection(&object->cs); + + if (FAILED(hr = open_connection(object, 0ULL, -1ULL))) + goto error; + + while (!object->parsed_headers && !object->error) + SleepConditionVariableCS(&object->cv, &object->cs, INFINITE); + + if (object->error) + { + hr = object->error; + goto error; + } + + IMFAttributes_SetString(&object->attributes.IMFAttributes_iface, &MF_BYTESTREAM_EFFECTIVE_URL, object->url); + IMFAttributes_SetString(&object->attributes.IMFAttributes_iface, &MF_BYTESTREAM_CONTENT_TYPE, + object->content_type ? object->content_type : L"application/octet-stream"); + if (object->last_modified != -1ULL) + { + ul.QuadPart = object->last_modified; + ft.dwLowDateTime = ul.u.LowPart; + ft.dwHighDateTime = ul.u.HighPart; + IMFAttributes_SetBlob(&object->attributes.IMFAttributes_iface, &MF_BYTESTREAM_LAST_MODIFIED_TIME, (void *)&ft, + sizeof(ft)); + } + + LeaveCriticalSection(&object->cs); + + *out = &object->IMFByteStream_iface; + return S_OK; + +error: + LeaveCriticalSection(&object->cs); + IMFByteStream_Release(&object->IMFByteStream_iface); + + if (hr == NS_E_SERVER_NOT_FOUND && wcsncmp(url, L"http:", 5)) + hr = WININET_E_NAME_NOT_RESOLVED; + else if (!wcsncmp(url, L"mms:", 4)) + hr = MF_E_UNSUPPORTED_BYTESTREAM_TYPE; + return hr; +}
From: Torge Matthies tmatthies@codeweavers.com
--- dlls/mf/main.c | 2 ++ dlls/mf/mf.idl | 6 ++++++ dlls/mf/mf.rgs | 16 ++++++++++++++-- dlls/mf/mf_private.h | 1 + dlls/mf/scheme_handler.c | 33 +++++++++++++++++++++++++++++++++ dlls/mf/tests/mf.c | 38 ++++---------------------------------- dlls/mfplat/mfplat.spec | 3 +++ dlls/mfplat/network.c | 5 +++++ 8 files changed, 68 insertions(+), 36 deletions(-)
diff --git a/dlls/mf/main.c b/dlls/mf/main.c index e74a4ae8e4c..120343a1be2 100644 --- a/dlls/mf/main.c +++ b/dlls/mf/main.c @@ -549,6 +549,7 @@ static const IClassFactoryVtbl class_factory_vtbl = };
static struct class_factory file_scheme_handler_factory = { { &class_factory_vtbl }, file_scheme_handler_construct }; +static struct class_factory http_scheme_handler_factory = { { &class_factory_vtbl }, http_scheme_handler_construct }; static struct class_factory urlmon_scheme_handler_factory = { { &class_factory_vtbl }, urlmon_scheme_handler_construct };
static const struct class_object @@ -559,6 +560,7 @@ static const struct class_object class_objects[] = { { &CLSID_FileSchemePlugin, &file_scheme_handler_factory.IClassFactory_iface }, + { &CLSID_HttpSchemePlugin, &http_scheme_handler_factory.IClassFactory_iface }, { &CLSID_UrlmonSchemePlugin, &urlmon_scheme_handler_factory.IClassFactory_iface }, };
diff --git a/dlls/mf/mf.idl b/dlls/mf/mf.idl index 4f5ef36c965..ca05a991ca9 100644 --- a/dlls/mf/mf.idl +++ b/dlls/mf/mf.idl @@ -25,6 +25,12 @@ ] coclass FileSchemePlugin { }
+[ + threading(both), + uuid(44cb442b-9da9-49df-b3fd-023777b16e50) +] +coclass HttpSchemePlugin {} + [ threading(both), uuid(9ec4b4f9-3029-45ad-947b-344de2a249e2) diff --git a/dlls/mf/mf.rgs b/dlls/mf/mf.rgs index f06576baccb..778b07e65ed 100644 --- a/dlls/mf/mf.rgs +++ b/dlls/mf/mf.rgs @@ -14,11 +14,23 @@ HKLM } 'http:' { - val '{9ec4b4f9-3029-45ad-947b-344de2a249e2}' = s 'Urlmon Scheme Handler' + val '{44cb442b-9da9-49df-b3fd-023777b16e50}' = s 'Http Scheme Handler' } 'https:' { - val '{9ec4b4f9-3029-45ad-947b-344de2a249e2}' = s 'Urlmon Scheme Handler' + val '{44cb442b-9da9-49df-b3fd-023777b16e50}' = s 'Http Scheme Handler' + } + 'httpd:' + { + val '{44cb442b-9da9-49df-b3fd-023777b16e50}' = s 'Http Scheme Handler' + } + 'httpsd:' + { + val '{44cb442b-9da9-49df-b3fd-023777b16e50}' = s 'Http Scheme Handler' + } + 'mms:' + { + val '{44cb442b-9da9-49df-b3fd-023777b16e50}' = s 'Http Scheme Handler' } } } diff --git a/dlls/mf/mf_private.h b/dlls/mf/mf_private.h index 1f2ef17a8c9..adb35e14194 100644 --- a/dlls/mf/mf_private.h +++ b/dlls/mf/mf_private.h @@ -114,6 +114,7 @@ static inline const char *debugstr_propvar(const PROPVARIANT *v) }
extern HRESULT file_scheme_handler_construct(REFIID riid, void **obj); +extern HRESULT http_scheme_handler_construct(REFIID riid, void **obj); extern HRESULT urlmon_scheme_handler_construct(REFIID riid, void **obj);
extern BOOL mf_is_sample_copier_transform(IMFTransform *transform); diff --git a/dlls/mf/scheme_handler.c b/dlls/mf/scheme_handler.c index 7e92748aeb9..3cd16319b2a 100644 --- a/dlls/mf/scheme_handler.c +++ b/dlls/mf/scheme_handler.c @@ -483,6 +483,39 @@ HRESULT file_scheme_handler_construct(REFIID riid, void **obj) return hr; }
+WINAPI HRESULT __wine_create_http_bytestream(const WCHAR *url, void **out); + +static HRESULT http_stream_create(const WCHAR *url, DWORD flags, IMFByteStream **out) +{ + if (flags & MF_RESOLUTION_WRITE) + return E_INVALIDARG; + + return __wine_create_http_bytestream(url, (void **)out); +} + +HRESULT http_scheme_handler_construct(REFIID riid, void **obj) +{ + struct scheme_handler *handler; + HRESULT hr; + + TRACE("%s, %p.\n", debugstr_guid(riid), obj); + + if (!(handler = calloc(1, sizeof(*handler)))) + return E_OUTOFMEMORY; + + handler->IMFSchemeHandler_iface.lpVtbl = &scheme_handler_vtbl; + handler->IMFAsyncCallback_iface.lpVtbl = &scheme_handler_callback_vtbl; + handler->refcount = 1; + list_init(&handler->results); + InitializeCriticalSection(&handler->cs); + handler->create_stream = http_stream_create; + + hr = IMFSchemeHandler_QueryInterface(&handler->IMFSchemeHandler_iface, riid, obj); + IMFSchemeHandler_Release(&handler->IMFSchemeHandler_iface); + + return hr; +} + static HRESULT urlmon_stream_create(const WCHAR *url, DWORD flags, IMFByteStream **out) { IMFAttributes *attributes; diff --git a/dlls/mf/tests/mf.c b/dlls/mf/tests/mf.c index e8ae6701466..f0339d249fc 100644 --- a/dlls/mf/tests/mf.c +++ b/dlls/mf/tests/mf.c @@ -5380,7 +5380,6 @@ static void test_scheme_resolvers(void) for (i = 0; i < ARRAY_SIZE(urls); i++) { hr = IMFSourceResolver_CreateObjectFromURL(resolver, urls[i], MF_RESOLUTION_BYTESTREAM, NULL, &type, &object); - todo_wine_if(i >= 2) ok(hr == S_OK, "got hr %#lx\n", hr); if (hr != S_OK) continue; @@ -5404,7 +5403,6 @@ static void test_scheme_resolvers(void) hr = IMFAttributes_GetItem(attributes, &MF_BYTESTREAM_CONTENT_TYPE, NULL); ok(hr == S_OK, "got hr %#lx\n", hr); hr = IMFAttributes_GetItem(attributes, &MF_BYTESTREAM_LAST_MODIFIED_TIME, NULL); - todo_wine ok(hr == S_OK, "got hr %#lx\n", hr); IMFAttributes_Release(attributes);
@@ -5412,8 +5410,7 @@ static void test_scheme_resolvers(void) ok(hr == S_OK, "got hr %#lx\n", hr); hr = IMFByteStream_GetCapabilities(byte_stream, &caps); ok(hr == S_OK, "got hr %#lx\n", hr); - todo_wine - ok(caps == (expect_caps | MFBYTESTREAM_IS_PARTIALLY_DOWNLOADED) + ok(caps == expect_caps || caps == (expect_caps | MFBYTESTREAM_IS_PARTIALLY_DOWNLOADED) || caps == (expect_caps | MFBYTESTREAM_DOES_NOT_USE_NETWORK), "got caps %#lx\n", caps); hr = IMFByteStream_GetLength(byte_stream, &length); @@ -5432,35 +5429,25 @@ static void test_scheme_resolvers(void) ok(hr == MF_E_UNSUPPORTED_BYTESTREAM_TYPE, "got hr %#lx\n", hr);
hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"http://test.winehq.bla/tests/test.mp3", MF_RESOLUTION_BYTESTREAM, NULL, &type, &object); - todo_wine ok(hr == NS_E_SERVER_NOT_FOUND, "got hr %#lx\n", hr); hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"https://test.winehq.bla/tests/test.mp3", MF_RESOLUTION_BYTESTREAM, NULL, &type, &object); - todo_wine ok(hr == WININET_E_NAME_NOT_RESOLVED, "got hr %#lx\n", hr); hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"httpd://test.winehq.bla/tests/test.mp3", MF_RESOLUTION_BYTESTREAM, NULL, &type, &object); - todo_wine ok(hr == WININET_E_NAME_NOT_RESOLVED, "got hr %#lx\n", hr); hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"httpsd://test.winehq.bla/tests/test.mp3", MF_RESOLUTION_BYTESTREAM, NULL, &type, &object); - todo_wine ok(hr == WININET_E_NAME_NOT_RESOLVED, "got hr %#lx\n", hr); hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"mms://test.winehq.bla/tests/test.mp3", MF_RESOLUTION_BYTESTREAM, NULL, &type, &object); - todo_wine ok(hr == WININET_E_NAME_NOT_RESOLVED, "got hr %#lx\n", hr);
hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"http://test.winehq.org/tests/invalid.mp3", MF_RESOLUTION_BYTESTREAM, NULL, &type, &object); - todo_wine ok(hr == NS_E_FILE_NOT_FOUND, "got hr %#lx\n", hr); hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"https://test.winehq.org/tests/invalid.mp3", MF_RESOLUTION_BYTESTREAM, NULL, &type, &object); - todo_wine ok(hr == NS_E_FILE_NOT_FOUND, "got hr %#lx\n", hr); hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"httpd://test.winehq.org/tests/invalid.mp3", MF_RESOLUTION_BYTESTREAM, NULL, &type, &object); - todo_wine ok(hr == NS_E_FILE_NOT_FOUND, "got hr %#lx\n", hr); hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"httpsd://test.winehq.org/tests/invalid.mp3", MF_RESOLUTION_BYTESTREAM, NULL, &type, &object); - todo_wine ok(hr == NS_E_FILE_NOT_FOUND, "got hr %#lx\n", hr); hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"mms://test.winehq.org/tests/invalid.mp3", MF_RESOLUTION_BYTESTREAM, NULL, &type, &object); - todo_wine ok(hr == MF_E_UNSUPPORTED_BYTESTREAM_TYPE, "got hr %#lx\n", hr);
IMFSourceResolver_Release(resolver); @@ -6648,7 +6635,6 @@ static void test_network_bytestream(void) obj_type = (MF_OBJECT_TYPE)0xdeadbeef; object = NULL; hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"http://nonexistent.url/file.mp4", MF_RESOLUTION_BYTESTREAM, NULL, &obj_type, &object); - todo_wine ok(hr == NS_E_SERVER_NOT_FOUND, "Got hr %#lx.\n", hr); ok(obj_type == MF_OBJECT_INVALID, "Unexpected obj_type %#x.\n", obj_type); if (object) IUnknown_Release(object); @@ -6656,9 +6642,7 @@ static void test_network_bytestream(void) obj_type = (MF_OBJECT_TYPE)0xdeadbeef; object = NULL; hr = IMFSourceResolver_CreateObjectFromURL(resolver, L"http://test.winehq.org/tests/invalid.mp3", MF_RESOLUTION_BYTESTREAM, NULL, &obj_type, &object); - todo_wine ok(hr == NS_E_FILE_NOT_FOUND, "Got hr %#lx.\n", hr); - todo_wine ok(obj_type == MF_OBJECT_INVALID, "Unexpected obj_type %#x.\n", obj_type); if (object) IUnknown_Release(object);
@@ -6681,7 +6665,6 @@ static void test_network_bytestream(void)
hr = IMFAttributes_GetCount(attr, &count); ok(hr == S_OK, "Got hr %#lx\n", hr); - todo_wine ok(count == 3, "count = %u\n", count);
PropVariantInit(&var); @@ -6690,7 +6673,6 @@ static void test_network_bytestream(void) ok(hr == S_OK, "Got hr %#lx\n", hr); ok(IsEqualGUID(&key, &MF_BYTESTREAM_EFFECTIVE_URL), "Got key %s\n", debugstr_guid(&key)); ok(var.vt == VT_LPWSTR, "Got type %d\n", var.vt); - todo_wine ok(!lstrcmpW(var.pwszVal, EFFECTIVE_URL), "Got value %s\n", var.pszVal); memset(&key, 0, sizeof(key)); PropVariantClear(&var); @@ -6699,21 +6681,15 @@ static void test_network_bytestream(void) ok(hr == S_OK, "Got hr %#lx\n", hr); ok(IsEqualGUID(&key, &MF_BYTESTREAM_CONTENT_TYPE), "Got key %s\n", debugstr_guid(&key)); ok(var.vt == VT_LPWSTR, "Got type %d\n", var.vt); - todo_wine ok(!lstrcmpW(var.pwszVal, CONTENT_TYPE), "Got value %s\n", var.pszVal); memset(&key, 0, sizeof(key)); PropVariantClear(&var);
hr = IMFAttributes_GetItemByIndex(attr, 2, &key, &var); - todo_wine ok(hr == S_OK, "Got hr %#lx\n", hr); - todo_wine ok(IsEqualGUID(&key, &MF_BYTESTREAM_LAST_MODIFIED_TIME), "Got key %s\n", debugstr_guid(&key)); - todo_wine ok(var.vt == (VT_VECTOR | VT_I1 | VT_NULL), "Got type %d\n", var.vt); - todo_wine ok(var.blob.cbSize == sizeof(LAST_MODIFIED_TIME), "Got size %lu\n", var.blob.cbSize); - todo_wine ok(var.blob.pBlobData != NULL, "Got NULL value\n"); if (var.blob.cbSize == sizeof(LAST_MODIFIED_TIME) && var.blob.pBlobData) ok(!memcmp(var.blob.pBlobData, LAST_MODIFIED_TIME, sizeof(LAST_MODIFIED_TIME)), "Got wrong value\n"); @@ -6732,9 +6708,7 @@ static void test_network_bytestream(void)
ptr = NULL; hr = IUnknown_QueryInterface(object, &IID_IMFByteStreamCacheControl, &ptr); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - todo_wine ok(ptr != NULL, "Got NULL ptr.\n"); if (SUCCEEDED(hr) && ptr) { @@ -6749,9 +6723,7 @@ static void test_network_bytestream(void)
ptr = NULL; hr = IUnknown_QueryInterface(object, &IID_IMFByteStreamBuffering, &ptr); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - todo_wine ok(ptr != NULL, "Got NULL ptr.\n"); if (SUCCEEDED(hr) && ptr) { @@ -6818,9 +6790,7 @@ static void test_network_bytestream(void)
ptr = NULL; hr = IUnknown_QueryInterface(object, &IID_IMFByteStreamTimeSeek, &ptr); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - todo_wine ok(ptr != NULL, "Got NULL ptr.\n"); if (SUCCEEDED(hr) && ptr) { @@ -6879,6 +6849,9 @@ static void test_network_bytestream(void) ok(hr == E_NOTIMPL, "Got hr %#lx.\n", hr); ok(written == 0, "written = %lu\n", written);
+ hr = IMFByteStream_BeginWrite((IMFByteStream*)object, tmp, 1000, (void *)(DWORD_PTR)0xdeadbeef, NULL); + ok(hr == E_NOTIMPL, "Got hr %#lx.\n", hr); + free(tmp);
hr = IMFByteStream_GetLength((IMFByteStream*)object, &len); @@ -6886,7 +6859,6 @@ static void test_network_bytestream(void) ok(len != 0, "len = %I64u\n", len);
hr = IMFByteStream_Flush((IMFByteStream*)object); - todo_wine ok(hr == S_OK, "Got hr %#lx\n", hr); }
@@ -6914,9 +6886,7 @@ static void test_network_bytestream(void)
ptr = NULL; hr = IUnknown_QueryInterface(object, &IID_IMFMediaEventGenerator, &ptr); - todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr); - todo_wine ok(ptr != NULL, "Got NULL ptr.\n"); if (SUCCEEDED(hr) && ptr) { diff --git a/dlls/mfplat/mfplat.spec b/dlls/mfplat/mfplat.spec index 7b356f9f457..f55f49b7e8c 100644 --- a/dlls/mfplat/mfplat.spec +++ b/dlls/mfplat/mfplat.spec @@ -181,3 +181,6 @@ @ stdcall -ret64 MFllMulDiv(int64 int64 int64 int64) @ stub PropVariantFromStream @ stub PropVariantToStream + +# Wine extension +@ stdcall __wine_create_http_bytestream(wstr ptr) diff --git a/dlls/mfplat/network.c b/dlls/mfplat/network.c index d9a2981f5a7..2369dbf31fd 100644 --- a/dlls/mfplat/network.c +++ b/dlls/mfplat/network.c @@ -2012,3 +2012,8 @@ error: hr = MF_E_UNSUPPORTED_BYTESTREAM_TYPE; return hr; } + +WINAPI HRESULT __wine_create_http_bytestream(const WCHAR *url, void **out) +{ + return create_http_bytestream(url, out); +}
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=149310
Your paranoid android.
=== w7u_2qxl (32 bit report) ===
mf: mf.c:5383: Test failed: got hr 0x80072f8f mf.c:5383: Test failed: got hr 0x80072f8f mf.c:5445: Test failed: got hr 0x80072f8f mf.c:5449: Test failed: got hr 0x80072f8f mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf.c:6327: Test failed: WaitForSingleObject returned 258 mf.c:6327: Test failed: Unexpected hr 0xd36d8. mf: Timeout
=== w8 (32 bit report) ===
mf: mf.c:6676: Test failed: Got value h
=== w8adm (32 bit report) ===
mf: mf.c:6676: Test failed: Got value h
=== w864 (32 bit report) ===
mf: mf.c:6676: Test failed: Got value h
=== w864 (64 bit report) ===
mf: mf.c:6676: Test failed: Got value h
Test failures in test-linux-32 are in ddraw7 and kernel32, neither of which I touched
Nikolay Sivov (@nsivov) commented about dlls/mf/tests/mf.c:
hr = IMFActivate_ActivateObject(activate, &IID_IMFMediaSink, (void **)&sink); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
if (!sink) return;
check_interface(sink, &IID_IMFMediaSinkPreroll, TRUE); check_interface(sink, &IID_IMFVideoRenderer, TRUE);
This one is unrelated.
Nikolay Sivov (@nsivov) commented about dlls/mf/tests/mf.c:
ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
}
+static void test_network_bytestream(void) +{
- static const WCHAR *URL = L"http://test.winehq.org/tests/test.mp3";
- static const WCHAR *EFFECTIVE_URL = L"http://test.winehq.org:80/tests/test.mp3";
- static const WCHAR *CONTENT_TYPE = L"audio/mpeg";
- static const BYTE LAST_MODIFIED_TIME[] = { 0x00, 0x3b, 0x4b, 0xbf, 0x05, 0x80, 0xd8, 0x01 };
I don't think we need to bother with checking exact value.
Nikolay Sivov (@nsivov) commented about dlls/mf/tests/mf.c:
- static const WCHAR *CONTENT_TYPE = L"audio/mpeg";
- static const BYTE LAST_MODIFIED_TIME[] = { 0x00, 0x3b, 0x4b, 0xbf, 0x05, 0x80, 0xd8, 0x01 };
- IMFSourceResolver *resolver;
- IUnknown *object = NULL, *bs = NULL;
- MF_OBJECT_TYPE obj_type;
- HRESULT hr;
- void *ptr;
- hr = MFStartup(MF_VERSION, MFSTARTUP_FULL);
- ok(hr == S_OK, "Startup failure, hr %#lx.\n", hr);
- hr = MFCreateSourceResolver(&resolver);
- ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
- if (object) IUnknown_Release(object);
This does not do anything.
Nikolay Sivov (@nsivov) commented about dlls/mf/tests/mf.c:
- obj_type = (MF_OBJECT_TYPE)0xdeadbeef;
- object = NULL;
- hr = IMFSourceResolver_CreateObjectFromURL(resolver, URL, MF_RESOLUTION_BYTESTREAM, NULL, &obj_type, &object);
- ok(hr == S_OK, "Got hr %#lx.\n", hr);
- ok(obj_type == MF_OBJECT_BYTESTREAM, "Unexpected obj_type %#x.\n", obj_type);
- ptr = NULL;
- hr = IUnknown_QueryInterface(object, &IID_IMFAttributes, &ptr);
- ok(hr == S_OK, "Got hr %#lx.\n", hr);
- ok(ptr != NULL, "Got NULL ptr.\n");
- if (SUCCEEDED(hr) && ptr)
- {
IMFAttributes *attr = ptr;
UINT32 count = 0;
PROPVARIANT var;
GUID key = {0};
Let's have those variable at top level, remove indentation, and use actual variable you need instead of "ptr". It's also useful to use a bunch of check_interface() one after another so you know what needs to be supported.
Nikolay Sivov (@nsivov) commented about dlls/mf/tests/mf.c:
ok(hr == S_OK, "Got hr %#lx\n", hr);
ok(IsEqualGUID(&key, &MF_BYTESTREAM_EFFECTIVE_URL), "Got key %s\n", debugstr_guid(&key));
ok(var.vt == VT_LPWSTR, "Got type %d\n", var.vt);
todo_wine
ok(!lstrcmpW(var.pwszVal, EFFECTIVE_URL), "Got value %s\n", var.pszVal);
memset(&key, 0, sizeof(key));
PropVariantClear(&var);
hr = IMFAttributes_GetItemByIndex(attr, 1, &key, &var);
ok(hr == S_OK, "Got hr %#lx\n", hr);
ok(IsEqualGUID(&key, &MF_BYTESTREAM_CONTENT_TYPE), "Got key %s\n", debugstr_guid(&key));
ok(var.vt == VT_LPWSTR, "Got type %d\n", var.vt);
todo_wine
ok(!lstrcmpW(var.pwszVal, CONTENT_TYPE), "Got value %s\n", var.pszVal);
memset(&key, 0, sizeof(key));
PropVariantClear(&var);
Unless you find a use case where indexing matters, I would remove this. Use GetItem() instead.
Nikolay Sivov (@nsivov) commented about dlls/mf/tests/mf.c:
ok(stop_time == 0, "stop_time = %I64u\n", stop_time);
ok(duration == 0, "duration = %I64u\n", duration);
IMFByteStreamTimeSeek_Release(seek);
- }
- {
BYTE *tmp = malloc(8192);
ULONG read = 0, written = 0;
QWORD len = 0;
hr = IMFByteStream_SetLength((IMFByteStream*)object, 1000);
ok(hr == E_NOTIMPL, "Got hr %#lx.\n", hr);
hr = IMFByteStream_SetCurrentPosition((IMFByteStream*)object, 1000);
ok(hr == S_OK, "Got hr %#lx.\n", hr);
Please use correct type instead of the casts.
Nikolay Sivov (@nsivov) commented about dlls/mfplat/http_error.c:
- case 189:
- case 190:
- case 191:
- case 192:
- case 193:
- case 194:
- case 195:
- case 196:
- case 197:
- case 198:
- case 199:
- case 301:
- case 302:
- case 303:
- case 307:
return NS_E_CONNECTION_FAILURE;
I think this needs get more compact.
Nikolay Sivov (@nsivov) commented about dlls/mfplat/mfplat.spec:
@ stdcall -ret64 MFllMulDiv(int64 int64 int64 int64) @ stub PropVariantFromStream @ stub PropVariantToStream
+# Wine extension +@ stdcall __wine_create_http_bytestream(wstr ptr)
Why would we want that?
Nikolay Sivov (@nsivov) commented about dlls/mfplat/main.c:
return hr;
}
-static HRESULT WINAPI mfattributes_GetItem(IMFAttributes *iface, REFGUID key, PROPVARIANT *value) +HRESULT WINAPI mfattributes_GetItem(IMFAttributes *iface, REFGUID key, PROPVARIANT *value) {
Instead just use attributes_*() helpers instead. Does that not work for some reason?
Nikolay Sivov (@nsivov) commented about dlls/mfplat/network.c:
- ULONGLONG content_length;
- ULONGLONG last_modified;
- ULONGLONG content_range_start;
- ULONGLONG content_range_end;
- BOOL buffering_enabled;
- BOOL buffering;
- struct list downloaded_ranges;
- HANDLE cache_file;
- ULONGLONG current_write_pos;
- BOOL read_active;
- DWORD bytes_read;
- BYTE buffer[1024*1024];
+};
Could you try splitting this up? The file adds 2K lines at once.
Nikolay Sivov (@nsivov) commented about dlls/mfplat/Makefile.in:
MODULE = mfplat.dll IMPORTLIB = mfplat -IMPORTS = advapi32 ole32 dmoguids mfuuid propsys rtworkq kernelbase +IMPORTS = advapi32 ole32 dmoguids mfuuid propsys rtworkq kernelbase winhttp
Is there an evidence we should use winhttp? Is it going to need any other protocols? If yes, how is it implemented on Windows? If it turns out we need something else in addition to winhttp later, this will need more work to structure it properly.
Nikolay Sivov (@nsivov) commented about dlls/mfplat/network.c:
- }
- LeaveCriticalSection(&object->cs);
- *out = &object->IMFByteStream_iface;
- return S_OK;
+error:
- LeaveCriticalSection(&object->cs);
- IMFByteStream_Release(&object->IMFByteStream_iface);
- if (hr == NS_E_SERVER_NOT_FOUND && wcsncmp(url, L"http:", 5))
hr = WININET_E_NAME_NOT_RESOLVED;
- else if (!wcsncmp(url, L"mms:", 4))
hr = MF_E_UNSUPPORTED_BYTESTREAM_TYPE;
- return hr;
This suggest that wininet is involved then? Also, why only check for mms: or for any scheme at all? I think it should be registered in scheme handler section, so if we don't need one for mms, maybe it should not call http one?
Nikolay Sivov (@nsivov) commented about dlls/mfplat/network.c:
goto error;
- }
- IMFAttributes_SetString(&object->attributes.IMFAttributes_iface, &MF_BYTESTREAM_EFFECTIVE_URL, object->url);
- IMFAttributes_SetString(&object->attributes.IMFAttributes_iface, &MF_BYTESTREAM_CONTENT_TYPE,
object->content_type ? object->content_type : L"application/octet-stream");
- if (object->last_modified != -1ULL)
- {
ul.QuadPart = object->last_modified;
ft.dwLowDateTime = ul.u.LowPart;
ft.dwHighDateTime = ul.u.HighPart;
IMFAttributes_SetBlob(&object->attributes.IMFAttributes_iface, &MF_BYTESTREAM_LAST_MODIFIED_TIME, (void *)&ft,
sizeof(ft));
- }
- LeaveCriticalSection(&object->cs);
Why are you locking during object creation?
Nikolay Sivov (@nsivov) commented about dlls/mfplat/network.c:
if (!wcsncmp(url, scheme_replacements[i].scheme, scheme_len))
{
size_t replacement_len = wcslen(scheme_replacements[i].replacement);
size_t url_len = wcslen(url);
WCHAR *new_url = malloc((url_len - scheme_len + replacement_len + 1) * sizeof(*new_url));
if (!new_url)
return NULL;
wcscpy(new_url, scheme_replacements[i].replacement);
wcscat(new_url, url + scheme_len);
return new_url;
}
- }
- return wcsdup(url);
+}
+static WCHAR *recreate_url(const WCHAR *url)
Why do you need this?
On Fri Oct 25 13:31:44 2024 +0000, Nikolay Sivov wrote:
This one is unrelated.
For some reason, this test fails on my Windows 10 VM, that's why I had it in here. I forgot to remove it, will do.
On Fri Oct 25 13:31:44 2024 +0000, Nikolay Sivov wrote:
I don't think we need to bother with checking exact value.
At least the effective url is different from the input url. I can remove the checks for content type and modification time, but I'd say at least the effective url attribute should be checked, what do you think?
On Fri Oct 25 13:31:44 2024 +0000, Nikolay Sivov wrote:
Let's have those variable at top level, remove indentation, and use actual variable you need instead of "ptr". It's also useful to use a bunch of check_interface() one after another so you know what needs to be supported.
Okay, I thought this would be cleaner and not clutter up the top of the function but I'll change this.
On Fri Oct 25 14:43:47 2024 +0000, Torge Matthies wrote:
At least the effective url is different from the input url. I can remove the checks for content type and modification time, but I'd say at least the effective url attribute should be checked, what do you think?
My comment was only for modification time value.
On Fri Oct 25 13:31:45 2024 +0000, Nikolay Sivov wrote:
Instead just use attributes_*() helpers instead. Does that not work for some reason?
That means that I'd have to copy&paste the mfattributes_*() functions from here to network.c, without changes. I figured I'd avoid more code duplication.
On Fri Oct 25 13:31:45 2024 +0000, Nikolay Sivov wrote:
Could you try splitting this up? The file adds 2K lines at once.
Would implementing one interface at a time be a good strategy for this?
On Fri Oct 25 13:31:45 2024 +0000, Nikolay Sivov wrote:
Is there an evidence we should use winhttp? Is it going to need any other protocols? If yes, how is it implemented on Windows? If it turns out we need something else in addition to winhttp later, this will need more work to structure it properly.
The requests I got from mfplat on my test HTTP server looked exactly like winhttp requests, with 3 additional headers. I even wrote some code that uses winhttp to exactly replicate the requests I got on the server side, so I was pretty confident this was using winhttp.
We also need a media source for RTSP support, but not a bytestream. Native mfplat will not give you a bytestream for an rtsp:// url, only a media source. Afaik there is no public Windows dll for rtsp.
On Fri Oct 25 13:31:45 2024 +0000, Nikolay Sivov wrote:
Why would we want that?
I guess I can move the HTTP scheme handler from dlls/mf to dlls/mfplat, will do!
On Fri Oct 25 13:31:46 2024 +0000, Nikolay Sivov wrote:
This suggest that wininet is involved then? Also, why only check for mms: or for any scheme at all? I think it should be registered in scheme handler section, so if we don't need one for mms, maybe it should not call http one?
mms:// is only "not supported" when there's an error connecting to the server, otherwise it functions like HTTP. Well, it's supposed to try a different protocol first, but I found no existing implementation of said protocol in Wine, and I was focusing on HTTP/HTTPS here. I suppose I should just mark this protocol as entirely unsupported for now instead of relying on the HTTP fallback.
On Fri Oct 25 13:31:46 2024 +0000, Nikolay Sivov wrote:
Why are you locking during object creation?
Waiting for the connection to be established and headers to be parsed, as well as connection errors to be detected, requires us to wait on a `CONDITION_VARIABLE`, which in turn requires a locked `CRITICAL_SECTION`.
On Fri Oct 25 13:31:46 2024 +0000, Nikolay Sivov wrote:
Why do you need this?
`normalize_uri_scheme` to turn httpd:// into http:// and httpsd:// into https://, `recreate_url` to add the port to the url.
I figured using `WinHttpCrackUrl` and re-combining the parts together with the port would be cleaner than trying to find where the hostname ends manually and splicing the port in at that point.
On Fri Oct 25 15:22:15 2024 +0000, Torge Matthies wrote:
mms:// is only "not supported" when there's an error connecting to the server, otherwise it functions like HTTP. Well, it's supposed to try a different protocol first, but I found no existing implementation of said protocol in Wine, and I was focusing on HTTP/HTTPS here. I suppose I should just mark this protocol as entirely unsupported for now instead of relying on the HTTP fallback.
Should we have a separate handler for mms:// or not?
On Fri Oct 25 15:30:30 2024 +0000, Torge Matthies wrote:
`normalize_uri_scheme` to turn httpd:// into http:// and httpsd:// into https://, `recreate_url` to add the port to the url. I figured using `WinHttpCrackUrl` and re-combining the parts together with the port would be cleaner than trying to find where the hostname ends manually and splicing the port in at that point.
Can you query it back from winhttp after it was resolved, or something similar? It's hard to believe this needs manual handling outside of network layer.
On Fri Oct 25 17:22:50 2024 +0000, Nikolay Sivov wrote:
Should we have a separate handler for mms:// or not?
Maybe, but it would need to handle http too, since mms falls back to that if it can't use the primary protocol
On Fri Oct 25 17:24:51 2024 +0000, Nikolay Sivov wrote:
Can you query it back from winhttp after it was resolved, or something similar? It's hard to believe this needs manual handling outside of network layer.
I didn't actually test if native WinHTTP handles those two schemes, it definitely doesn't on Wine but I'll have to test that on Windows.
I don't even know what those two protocols are, searching for "httpd" just brings up results for http daemon. It's possible that those schemes are handled by some other lower-level component, searching in Wine doesn't bring up anything outside of mfplat though.
On Fri Oct 25 14:46:22 2024 +0000, Torge Matthies wrote:
Okay, I thought this would be cleaner and not clutter up the top of the function but I'll change this.
Actually, how do I deal with unimplemented interfaces if I do this? If I remove the `if`, I can't send the tests before the implementation, because the tests relying on interfaces that aren't implemented in UrlmonSchemeHandler would crash.
On Fri Oct 25 14:59:04 2024 +0000, Torge Matthies wrote:
The requests I got from mfplat on my test HTTP server looked exactly like winhttp requests, with 3 additional headers. I even wrote some code that uses winhttp to exactly replicate the requests I got on the server side, so I was pretty confident this was using winhttp. We also need a media source for RTSP support, but not a bytestream. Native mfplat will not give you a bytestream for an rtsp:// url, only a media source. Afaik there is no public Windows dll for rtsp.
Update: It looks like the native network media source uses wininet, not winhttp.
On Fri Oct 25 14:52:32 2024 +0000, Torge Matthies wrote:
Would implementing one interface at a time be a good strategy for this?
When it makes sense, if something need more than one interface to be useful, no need to split too much. But I suspect with 2K lines you could at least add stubs first, per interface.
On Fri Oct 25 15:04:31 2024 +0000, Torge Matthies wrote:
I guess I can move the HTTP scheme handler from dlls/mf to dlls/mfplat, will do!
If it's not too difficult I would prefer to follow dependencies structure the way it is on Windows. Where network stream and scheme handler should be located?