From e8c4c1db0c40e6b8bca3ab2295ea94bfe3d71dab Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Fri, 22 Mar 2019 09:15:16 +0300 Subject: [PATCH] mfplat: Implement IStream-based bytestream object. Signed-off-by: Nikolay Sivov Signed-off-by: Alexandre Julliard --- dlls/mfplat/main.c | 651 +++++++++++++++++++++++++++++++++---- dlls/mfplat/tests/mfplat.c | 100 +++++- include/mfidl.idl | 42 +++ include/mfobjects.idl | 1 + 4 files changed, 727 insertions(+), 67 deletions(-) diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c index dd89f4c91ad..2adf3819c1d 100644 --- a/dlls/mfplat/main.c +++ b/dlls/mfplat/main.c @@ -1791,10 +1791,17 @@ HRESULT WINAPI MFInitAttributesFromBlob(IMFAttributes *dest, const UINT8 *buffer return hr; } -typedef struct _mfbytestream +typedef struct bytestream { struct attributes attributes; IMFByteStream IMFByteStream_iface; + IMFAsyncCallback read_callback; + IMFAsyncCallback write_callback; + IStream *stream; + QWORD position; + DWORD capabilities; + struct list pending; + CRITICAL_SECTION cs; } mfbytestream; static inline mfbytestream *impl_from_IMFByteStream(IMFByteStream *iface) @@ -1802,20 +1809,209 @@ static inline mfbytestream *impl_from_IMFByteStream(IMFByteStream *iface) return CONTAINING_RECORD(iface, mfbytestream, IMFByteStream_iface); } -static HRESULT WINAPI mfbytestream_QueryInterface(IMFByteStream *iface, REFIID riid, void **out) +static struct bytestream *impl_from_read_callback_IMFAsyncCallback(IMFAsyncCallback *iface) { - mfbytestream *This = impl_from_IMFByteStream(iface); + return CONTAINING_RECORD(iface, struct bytestream, read_callback); +} - TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), out); +static struct bytestream *impl_from_write_callback_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct bytestream, write_callback); +} - if(IsEqualGUID(riid, &IID_IUnknown) || - IsEqualGUID(riid, &IID_IMFByteStream)) +enum async_stream_op_type +{ + ASYNC_STREAM_OP_READ, + ASYNC_STREAM_OP_WRITE, +}; + +struct async_stream_op +{ + IUnknown IUnknown_iface; + LONG refcount; + union { - *out = &This->IMFByteStream_iface; + const BYTE *src; + BYTE *dest; + } u; + 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; } - else if(IsEqualGUID(riid, &IID_IMFAttributes)) + + 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 %d.\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 %d.\n", iface, refcount); + + if (!refcount) { - *out = &This->attributes.IMFAttributes_iface; + if (op->caller) + IMFAsyncResult_Release(op->caller); + heap_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 bytestream_create_io_request(struct bytestream *stream, enum async_stream_op_type type, + const BYTE *data, ULONG size, IMFAsyncCallback *callback, IUnknown *state) +{ + struct async_stream_op *op; + IMFAsyncResult *request; + HRESULT hr; + + op = heap_alloc(sizeof(*op)); + if (!op) + return E_OUTOFMEMORY; + + op->IUnknown_iface.lpVtbl = &async_stream_op_vtbl; + op->refcount = 1; + op->u.src = data; + op->requested_length = size; + op->type = type; + if (FAILED(hr = MFCreateAsyncResult((IUnknown *)&stream->IMFByteStream_iface, callback, state, &op->caller))) + goto failed; + + if (FAILED(hr = MFCreateAsyncResult(&op->IUnknown_iface, type == ASYNC_STREAM_OP_READ ? &stream->read_callback : + &stream->write_callback, NULL, &request))) + goto failed; + + MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_STANDARD, request); + IMFAsyncResult_Release(request); + +failed: + IUnknown_Release(&op->IUnknown_iface); + return hr; +} + +static HRESULT bytestream_complete_io_request(struct bytestream *stream, enum async_stream_op_type type, + IMFAsyncResult *result, ULONG *actual_length) +{ + struct async_stream_op *op = NULL, *cur; + HRESULT hr; + + EnterCriticalSection(&stream->cs); + LIST_FOR_EACH_ENTRY(cur, &stream->pending, struct async_stream_op, entry) + { + if (cur->caller == result && cur->type == type) + { + op = cur; + list_remove(&cur->entry); + break; + } + } + LeaveCriticalSection(&stream->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_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IMFAsyncCallback) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFAsyncCallback_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI bytestream_read_callback_AddRef(IMFAsyncCallback *iface) +{ + struct bytestream *stream = impl_from_read_callback_IMFAsyncCallback(iface); + return IMFByteStream_AddRef(&stream->IMFByteStream_iface); +} + +static ULONG WINAPI bytestream_read_callback_Release(IMFAsyncCallback *iface) +{ + struct bytestream *stream = impl_from_read_callback_IMFAsyncCallback(iface); + return IMFByteStream_Release(&stream->IMFByteStream_iface); +} + +static HRESULT WINAPI bytestream_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static ULONG WINAPI bytestream_write_callback_AddRef(IMFAsyncCallback *iface) +{ + struct bytestream *stream = impl_from_write_callback_IMFAsyncCallback(iface); + return IMFByteStream_AddRef(&stream->IMFByteStream_iface); +} + +static ULONG WINAPI bytestream_write_callback_Release(IMFAsyncCallback *iface) +{ + struct bytestream *stream = impl_from_write_callback_IMFAsyncCallback(iface); + return IMFByteStream_Release(&stream->IMFByteStream_iface); +} + +static HRESULT WINAPI bytestream_QueryInterface(IMFByteStream *iface, REFIID riid, void **out) +{ + struct bytestream *stream = impl_from_IMFByteStream(iface); + + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), out); + + if (IsEqualIID(riid, &IID_IMFByteStream) || + IsEqualIID(riid, &IID_IUnknown)) + { + *out = &stream->IMFByteStream_iface; + } + else if (IsEqualIID(riid, &IID_IMFAttributes)) + { + *out = &stream->attributes.IMFAttributes_iface; } else { @@ -1828,46 +2024,55 @@ static HRESULT WINAPI mfbytestream_QueryInterface(IMFByteStream *iface, REFIID r return S_OK; } -static ULONG WINAPI mfbytestream_AddRef(IMFByteStream *iface) +static ULONG WINAPI bytestream_AddRef(IMFByteStream *iface) { - mfbytestream *This = impl_from_IMFByteStream(iface); - ULONG ref = InterlockedIncrement(&This->attributes.ref); + struct bytestream *stream = impl_from_IMFByteStream(iface); + ULONG refcount = InterlockedIncrement(&stream->attributes.ref); - TRACE("(%p) ref=%u\n", This, ref); + TRACE("%p, refcount %d.\n", iface, refcount); - return ref; + return refcount; } -static ULONG WINAPI mfbytestream_Release(IMFByteStream *iface) +static ULONG WINAPI bytestream_Release(IMFByteStream *iface) { - mfbytestream *This = impl_from_IMFByteStream(iface); - ULONG ref = InterlockedDecrement(&This->attributes.ref); + struct bytestream *stream = impl_from_IMFByteStream(iface); + ULONG refcount = InterlockedDecrement(&stream->attributes.ref); + struct async_stream_op *cur, *cur2; - TRACE("(%p) ref=%u\n", This, ref); + TRACE("%p, refcount %d.\n", iface, refcount); - if (!ref) + if (!refcount) { - clear_attributes_object(&This->attributes); - HeapFree(GetProcessHeap(), 0, This); + clear_attributes_object(&stream->attributes); + LIST_FOR_EACH_ENTRY_SAFE(cur, cur2, &stream->pending, struct async_stream_op, entry) + { + list_remove(&cur->entry); + IUnknown_Release(&cur->IUnknown_iface); + } + DeleteCriticalSection(&stream->cs); + if (stream->stream) + IStream_Release(stream->stream); + heap_free(stream); } - return ref; + return refcount; } -static HRESULT WINAPI mfbytestream_GetCapabilities(IMFByteStream *iface, DWORD *capabilities) +static HRESULT WINAPI bytestream_GetCapabilities(IMFByteStream *iface, DWORD *capabilities) { - mfbytestream *This = impl_from_IMFByteStream(iface); + struct bytestream *stream = impl_from_IMFByteStream(iface); - FIXME("%p, %p\n", This, capabilities); + TRACE("%p, %p.\n", iface, capabilities); - return E_NOTIMPL; + *capabilities = stream->capabilities; + + return S_OK; } static HRESULT WINAPI mfbytestream_GetLength(IMFByteStream *iface, QWORD *length) { - mfbytestream *This = impl_from_IMFByteStream(iface); - - FIXME("%p, %p\n", This, length); + FIXME("%p, %p.\n", iface, length); return E_NOTIMPL; } @@ -1920,23 +2125,23 @@ static HRESULT WINAPI mfbytestream_Read(IMFByteStream *iface, BYTE *data, ULONG return E_NOTIMPL; } -static HRESULT WINAPI mfbytestream_BeginRead(IMFByteStream *iface, BYTE *data, ULONG count, - IMFAsyncCallback *callback, IUnknown *state) +static HRESULT WINAPI bytestream_BeginRead(IMFByteStream *iface, BYTE *data, ULONG size, IMFAsyncCallback *callback, + IUnknown *state) { - mfbytestream *This = impl_from_IMFByteStream(iface); + struct bytestream *stream = impl_from_IMFByteStream(iface); - FIXME("%p, %p, %u, %p, %p\n", This, data, count, callback, state); + TRACE("%p, %p, %u, %p, %p.\n", iface, data, size, callback, state); - return E_NOTIMPL; + return bytestream_create_io_request(stream, ASYNC_STREAM_OP_READ, data, size, callback, state); } -static HRESULT WINAPI mfbytestream_EndRead(IMFByteStream *iface, IMFAsyncResult *result, ULONG *byte_read) +static HRESULT WINAPI bytestream_EndRead(IMFByteStream *iface, IMFAsyncResult *result, ULONG *byte_read) { - mfbytestream *This = impl_from_IMFByteStream(iface); + struct bytestream *stream = impl_from_IMFByteStream(iface); - FIXME("%p, %p, %p\n", This, result, byte_read); + TRACE("%p, %p, %p.\n", iface, result, byte_read); - return E_NOTIMPL; + return bytestream_complete_io_request(stream, ASYNC_STREAM_OP_READ, result, byte_read); } static HRESULT WINAPI mfbytestream_Write(IMFByteStream *iface, const BYTE *data, ULONG count, ULONG *written) @@ -1948,23 +2153,46 @@ static HRESULT WINAPI mfbytestream_Write(IMFByteStream *iface, const BYTE *data, return E_NOTIMPL; } -static HRESULT WINAPI mfbytestream_BeginWrite(IMFByteStream *iface, const BYTE *data, ULONG count, - IMFAsyncCallback *callback, IUnknown *state) +static HRESULT WINAPI bytestream_BeginWrite(IMFByteStream *iface, const BYTE *data, ULONG size, + IMFAsyncCallback *callback, IUnknown *state) { - mfbytestream *This = impl_from_IMFByteStream(iface); + struct bytestream *stream = impl_from_IMFByteStream(iface); + struct async_stream_op *op; + IMFAsyncResult *request; + HRESULT hr; - FIXME("%p, %p, %u, %p, %p\n", This, data, count, callback, state); + TRACE("%p, %p, %u, %p, %p.\n", iface, data, size, callback, state); - return E_NOTIMPL; + op = heap_alloc(sizeof(*op)); + if (!op) + return E_OUTOFMEMORY; + + op->IUnknown_iface.lpVtbl = &async_stream_op_vtbl; + op->refcount = 1; + op->u.src = data; + op->requested_length = size; + op->type = ASYNC_STREAM_OP_WRITE; + if (FAILED(hr = MFCreateAsyncResult((IUnknown *)iface, callback, state, &op->caller))) + goto failed; + + if (FAILED(hr = MFCreateAsyncResult(&op->IUnknown_iface, &stream->write_callback, NULL, &request))) + goto failed; + + MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_STANDARD, request); + IMFAsyncResult_Release(request); + +failed: + IUnknown_Release(&op->IUnknown_iface); + return hr; } -static HRESULT WINAPI mfbytestream_EndWrite(IMFByteStream *iface, IMFAsyncResult *result, ULONG *written) +static HRESULT WINAPI bytestream_EndWrite(IMFByteStream *iface, IMFAsyncResult *result, ULONG *written) { - mfbytestream *This = impl_from_IMFByteStream(iface); + struct bytestream *stream = impl_from_IMFByteStream(iface); - FIXME("%p, %p, %p\n", This, result, written); + TRACE("%p, %p, %p.\n", iface, result, written); - return E_NOTIMPL; + return bytestream_complete_io_request(stream, ASYNC_STREAM_OP_WRITE, result, written); } static HRESULT WINAPI mfbytestream_Seek(IMFByteStream *iface, MFBYTESTREAM_SEEK_ORIGIN seek, LONGLONG offset, @@ -1997,26 +2225,190 @@ static HRESULT WINAPI mfbytestream_Close(IMFByteStream *iface) static const IMFByteStreamVtbl mfbytestream_vtbl = { - mfbytestream_QueryInterface, - mfbytestream_AddRef, - mfbytestream_Release, - mfbytestream_GetCapabilities, + bytestream_QueryInterface, + bytestream_AddRef, + bytestream_Release, + bytestream_GetCapabilities, mfbytestream_GetLength, mfbytestream_SetLength, mfbytestream_GetCurrentPosition, mfbytestream_SetCurrentPosition, mfbytestream_IsEndOfStream, mfbytestream_Read, - mfbytestream_BeginRead, - mfbytestream_EndRead, + bytestream_BeginRead, + bytestream_EndRead, mfbytestream_Write, - mfbytestream_BeginWrite, - mfbytestream_EndWrite, + bytestream_BeginWrite, + bytestream_EndWrite, mfbytestream_Seek, mfbytestream_Flush, mfbytestream_Close }; +static HRESULT WINAPI bytestream_stream_GetLength(IMFByteStream *iface, QWORD *length) +{ + struct bytestream *stream = impl_from_IMFByteStream(iface); + STATSTG statstg; + HRESULT hr; + + TRACE("%p, %p.\n", iface, length); + + if (FAILED(hr = IStream_Stat(stream->stream, &statstg, STATFLAG_NONAME))) + return hr; + + *length = statstg.cbSize.QuadPart; + + return S_OK; +} + +static HRESULT WINAPI bytestream_stream_SetLength(IMFByteStream *iface, QWORD length) +{ + struct bytestream *stream = impl_from_IMFByteStream(iface); + ULARGE_INTEGER size; + + TRACE("%p, %s.\n", iface, wine_dbgstr_longlong(length)); + + size.QuadPart = length; + return IStream_SetSize(stream->stream, size); +} + +static HRESULT WINAPI bytestream_stream_GetCurrentPosition(IMFByteStream *iface, QWORD *position) +{ + struct bytestream *stream = impl_from_IMFByteStream(iface); + + TRACE("%p, %p.\n", iface, position); + + *position = stream->position; + + return S_OK; +} + +static HRESULT WINAPI bytestream_stream_SetCurrentPosition(IMFByteStream *iface, QWORD position) +{ + struct bytestream *stream = impl_from_IMFByteStream(iface); + + TRACE("%p, %s.\n", iface, wine_dbgstr_longlong(position)); + + stream->position = position; + + return S_OK; +} + +static HRESULT WINAPI bytestream_stream_IsEndOfStream(IMFByteStream *iface, BOOL *ret) +{ + struct bytestream *stream = impl_from_IMFByteStream(iface); + STATSTG statstg; + HRESULT hr; + + TRACE("%p, %p.\n", iface, ret); + + if (FAILED(hr = IStream_Stat(stream->stream, &statstg, STATFLAG_NONAME))) + return hr; + + *ret = stream->position >= statstg.cbSize.QuadPart; + + return S_OK; +} + +static HRESULT WINAPI bytestream_stream_Read(IMFByteStream *iface, BYTE *buffer, ULONG size, ULONG *read_len) +{ + struct bytestream *stream = impl_from_IMFByteStream(iface); + LARGE_INTEGER position; + HRESULT hr; + + TRACE("%p, %p, %u, %p.\n", iface, buffer, size, read_len); + + position.QuadPart = stream->position; + if (FAILED(hr = IStream_Seek(stream->stream, position, STREAM_SEEK_SET, NULL))) + return hr; + + if (SUCCEEDED(hr = IStream_Read(stream->stream, buffer, size, read_len))) + stream->position += *read_len; + + return hr; +} + +static HRESULT WINAPI bytestream_stream_Write(IMFByteStream *iface, const BYTE *buffer, ULONG size, ULONG *written) +{ + struct bytestream *stream = impl_from_IMFByteStream(iface); + LARGE_INTEGER position; + HRESULT hr; + + TRACE("%p, %p, %u, %p.\n", iface, buffer, size, written); + + position.QuadPart = stream->position; + if (FAILED(hr = IStream_Seek(stream->stream, position, STREAM_SEEK_SET, NULL))) + return hr; + + if (SUCCEEDED(hr = IStream_Write(stream->stream, buffer, size, written))) + stream->position += *written; + + return hr; +} + +static HRESULT WINAPI bytestream_stream_Seek(IMFByteStream *iface, MFBYTESTREAM_SEEK_ORIGIN origin, LONGLONG offset, + DWORD flags, QWORD *current) +{ + struct bytestream *stream = impl_from_IMFByteStream(iface); + + TRACE("%p, %u, %s, %#x, %p.\n", iface, origin, wine_dbgstr_longlong(offset), flags, current); + + switch (origin) + { + case msoBegin: + stream->position = offset; + break; + case msoCurrent: + stream->position += offset; + break; + default: + WARN("Unknown origin mode %d.\n", origin); + return E_INVALIDARG; + } + + *current = stream->position; + + return S_OK; +} + +static HRESULT WINAPI bytestream_stream_Flush(IMFByteStream *iface) +{ + struct bytestream *stream = impl_from_IMFByteStream(iface); + + TRACE("%p.\n", iface); + + return IStream_Commit(stream->stream, STGC_DEFAULT); +} + +static HRESULT WINAPI bytestream_stream_Close(IMFByteStream *iface) +{ + TRACE("%p.\n", iface); + + return S_OK; +} + +static const IMFByteStreamVtbl bytestream_stream_vtbl = +{ + bytestream_QueryInterface, + bytestream_AddRef, + bytestream_Release, + bytestream_GetCapabilities, + bytestream_stream_GetLength, + bytestream_stream_SetLength, + bytestream_stream_GetCurrentPosition, + bytestream_stream_SetCurrentPosition, + bytestream_stream_IsEndOfStream, + bytestream_stream_Read, + bytestream_BeginRead, + bytestream_EndRead, + bytestream_stream_Write, + bytestream_BeginWrite, + bytestream_EndWrite, + bytestream_stream_Seek, + bytestream_stream_Flush, + bytestream_stream_Close, +}; + static inline mfbytestream *impl_from_IMFByteStream_IMFAttributes(IMFAttributes *iface) { return CONTAINING_RECORD(iface, mfbytestream, attributes.IMFAttributes_iface); @@ -2078,15 +2470,99 @@ static const IMFAttributesVtbl mfbytestream_attributes_vtbl = mfattributes_CopyAllItems }; -HRESULT WINAPI MFCreateMFByteStreamOnStream(IStream *stream, IMFByteStream **bytestream) +static HRESULT WINAPI bytestream_stream_read_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) { - mfbytestream *object; + struct bytestream *stream = impl_from_read_callback_IMFAsyncCallback(iface); + struct async_stream_op *op; + LARGE_INTEGER position; + IUnknown *object; HRESULT hr; - TRACE("(%p, %p): stub\n", stream, bytestream); + if (FAILED(hr = IMFAsyncResult_GetObject(result, &object))) + return hr; - object = heap_alloc( sizeof(*object) ); - if(!object) + op = impl_async_stream_op_from_IUnknown(object); + + position.QuadPart = stream->position; + if (SUCCEEDED(hr = IStream_Seek(stream->stream, position, STREAM_SEEK_SET, NULL))) + { + if (SUCCEEDED(hr = IStream_Read(stream->stream, op->u.dest, op->requested_length, &op->actual_length))) + stream->position += op->actual_length; + } + + IMFAsyncResult_SetStatus(op->caller, hr); + + EnterCriticalSection(&stream->cs); + list_add_tail(&stream->pending, &op->entry); + LeaveCriticalSection(&stream->cs); + + MFInvokeCallback(op->caller); + + return S_OK; +} + +static HRESULT WINAPI bytestream_stream_write_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct bytestream *stream = impl_from_read_callback_IMFAsyncCallback(iface); + struct async_stream_op *op; + LARGE_INTEGER position; + IUnknown *object; + HRESULT hr; + + if (FAILED(hr = IMFAsyncResult_GetObject(result, &object))) + return hr; + + op = impl_async_stream_op_from_IUnknown(object); + + position.QuadPart = stream->position; + if (SUCCEEDED(hr = IStream_Seek(stream->stream, position, STREAM_SEEK_SET, NULL))) + { + if (SUCCEEDED(hr = IStream_Write(stream->stream, op->u.src, op->requested_length, &op->actual_length))) + stream->position += op->actual_length; + } + + IMFAsyncResult_SetStatus(op->caller, hr); + + EnterCriticalSection(&stream->cs); + list_add_tail(&stream->pending, &op->entry); + LeaveCriticalSection(&stream->cs); + + MFInvokeCallback(op->caller); + + return S_OK; +} + +static const IMFAsyncCallbackVtbl bytestream_stream_read_callback_vtbl = +{ + bytestream_callback_QueryInterface, + bytestream_read_callback_AddRef, + bytestream_read_callback_Release, + bytestream_callback_GetParameters, + bytestream_stream_read_callback_Invoke, +}; + +static const IMFAsyncCallbackVtbl bytestream_stream_write_callback_vtbl = +{ + bytestream_callback_QueryInterface, + bytestream_write_callback_AddRef, + bytestream_write_callback_Release, + bytestream_callback_GetParameters, + bytestream_stream_write_callback_Invoke, +}; + +/*********************************************************************** + * MFCreateMFByteStreamOnStream (mfplat.@) + */ +HRESULT WINAPI MFCreateMFByteStreamOnStream(IStream *stream, IMFByteStream **bytestream) +{ + struct bytestream *object; + LARGE_INTEGER position; + HRESULT hr; + + TRACE("%p, %p.\n", stream, bytestream); + + object = heap_alloc_zero(sizeof(*object)); + if (!object) return E_OUTOFMEMORY; if (FAILED(hr = init_attributes_object(&object->attributes, 0))) @@ -2094,14 +2570,59 @@ HRESULT WINAPI MFCreateMFByteStreamOnStream(IStream *stream, IMFByteStream **byt heap_free(object); return hr; } - object->IMFByteStream_iface.lpVtbl = &mfbytestream_vtbl; + + object->IMFByteStream_iface.lpVtbl = &bytestream_stream_vtbl; object->attributes.IMFAttributes_iface.lpVtbl = &mfbytestream_attributes_vtbl; + object->read_callback.lpVtbl = &bytestream_stream_read_callback_vtbl; + object->write_callback.lpVtbl = &bytestream_stream_write_callback_vtbl; + InitializeCriticalSection(&object->cs); + list_init(&object->pending); + + object->stream = stream; + IStream_AddRef(object->stream); + position.QuadPart = 0; + IStream_Seek(object->stream, position, STREAM_SEEK_SET, NULL); *bytestream = &object->IMFByteStream_iface; return S_OK; } +static HRESULT WINAPI bytestream_file_read_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + FIXME("%p, %p.\n", iface, result); + + return E_NOTIMPL; +} + +static HRESULT WINAPI bytestream_file_write_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + FIXME("%p, %p.\n", iface, result); + + return E_NOTIMPL; +} + +static const IMFAsyncCallbackVtbl bytestream_file_read_callback_vtbl = +{ + bytestream_callback_QueryInterface, + bytestream_read_callback_AddRef, + bytestream_read_callback_Release, + bytestream_callback_GetParameters, + bytestream_file_read_callback_Invoke, +}; + +static const IMFAsyncCallbackVtbl bytestream_file_write_callback_vtbl = +{ + bytestream_callback_QueryInterface, + bytestream_write_callback_AddRef, + bytestream_write_callback_Release, + bytestream_callback_GetParameters, + bytestream_file_write_callback_Invoke, +}; + +/*********************************************************************** + * MFCreateFile (mfplat.@) + */ HRESULT WINAPI MFCreateFile(MF_FILE_ACCESSMODE accessmode, MF_FILE_OPENMODE openmode, MF_FILE_FLAGS flags, LPCWSTR url, IMFByteStream **bytestream) { @@ -2161,8 +2682,8 @@ HRESULT WINAPI MFCreateFile(MF_FILE_ACCESSMODE accessmode, MF_FILE_OPENMODE open /* Close the file again, since we don't do anything with it yet */ CloseHandle(file); - object = heap_alloc( sizeof(*object) ); - if(!object) + object = heap_alloc_zero(sizeof(*object)); + if (!object) return E_OUTOFMEMORY; if (FAILED(hr = init_attributes_object(&object->attributes, 0))) @@ -2172,6 +2693,10 @@ HRESULT WINAPI MFCreateFile(MF_FILE_ACCESSMODE accessmode, MF_FILE_OPENMODE open } object->IMFByteStream_iface.lpVtbl = &mfbytestream_vtbl; object->attributes.IMFAttributes_iface.lpVtbl = &mfbytestream_attributes_vtbl; + object->read_callback.lpVtbl = &bytestream_file_read_callback_vtbl; + object->write_callback.lpVtbl = &bytestream_file_write_callback_vtbl; + InitializeCriticalSection(&object->cs); + list_init(&object->pending); *bytestream = &object->IMFByteStream_iface; diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index d53251c318b..f0148e1a5aa 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -1009,9 +1009,10 @@ static void test_MFCreateMFByteStreamOnStream(void) IMFByteStream *bytestream2; IStream *stream; IMFAttributes *attributes = NULL; + DWORD caps, written, count; IUnknown *unknown; + ULONG ref, size; HRESULT hr; - ULONG ref; if(!pMFCreateMFByteStreamOnStream) { @@ -1022,6 +1023,10 @@ static void test_MFCreateMFByteStreamOnStream(void) hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); ok(hr == S_OK, "got 0x%08x\n", hr); + caps = 0xffff0000; + hr = IStream_Write(stream, &caps, sizeof(caps), &written); + ok(hr == S_OK, "Failed to write, hr %#x.\n", hr); + hr = pMFCreateMFByteStreamOnStream(stream, &bytestream); ok(hr == S_OK, "got 0x%08x\n", hr); @@ -1054,6 +1059,9 @@ static void test_MFCreateMFByteStreamOnStream(void) } ok(attributes != NULL, "got NULL\n"); + hr = IMFAttributes_GetCount(attributes, &count); + ok(hr == S_OK, "Failed to get attributes count, hr %#x.\n", hr); + ok(count == 0, "Unexpected attributes count %u.\n", count); hr = IMFAttributes_QueryInterface(attributes, &IID_IUnknown, (void **)&unknown); @@ -1069,18 +1077,55 @@ static void test_MFCreateMFByteStreamOnStream(void) ref = IMFByteStream_Release(bytestream2); ok(ref == 2, "got %u\n", ref); + hr = IMFByteStream_QueryInterface(bytestream, &IID_IMFByteStreamBuffering, (void **)&unknown); + ok(hr == E_NOINTERFACE, "Unexpected hr %#x.\n", hr); + + hr = IMFByteStream_QueryInterface(bytestream, &IID_IMFByteStreamCacheControl, (void **)&unknown); + ok(hr == E_NOINTERFACE, "Unexpected hr %#x.\n", hr); + + hr = IMFByteStream_QueryInterface(bytestream, &IID_IMFMediaEventGenerator, (void **)&unknown); + ok(hr == E_NOINTERFACE, "Unexpected hr %#x.\n", hr); + + hr = IMFByteStream_QueryInterface(bytestream, &IID_IMFGetService, (void **)&unknown); + ok(hr == E_NOINTERFACE, "Unexpected hr %#x.\n", hr); + + hr = IMFByteStream_GetCapabilities(bytestream, &caps); + ok(hr == S_OK, "Failed to get stream capabilities, hr %#x.\n", hr); +todo_wine + ok(caps == (MFBYTESTREAM_IS_READABLE | MFBYTESTREAM_IS_SEEKABLE), "Unexpected caps %#x.\n", caps); + + hr = IMFByteStream_Close(bytestream); + ok(hr == S_OK, "Failed to close, hr %#x.\n", hr); + + hr = IMFByteStream_Close(bytestream); + ok(hr == S_OK, "Failed to close, hr %#x.\n", hr); + + hr = IMFByteStream_GetCapabilities(bytestream, &caps); + ok(hr == S_OK, "Failed to get stream capabilities, hr %#x.\n", hr); +todo_wine + ok(caps == (MFBYTESTREAM_IS_READABLE | MFBYTESTREAM_IS_SEEKABLE), "Unexpected caps %#x.\n", caps); + + caps = 0; + hr = IMFByteStream_Read(bytestream, (BYTE *)&caps, sizeof(caps), &size); + ok(hr == S_OK, "Failed to read from stream, hr %#x.\n", hr); + ok(caps == 0xffff0000, "Unexpected content.\n"); + IMFAttributes_Release(attributes); IMFByteStream_Release(bytestream); IStream_Release(stream); } -static void test_MFCreateFile(void) +static void test_file_stream(void) { IMFByteStream *bytestream; IMFByteStream *bytestream2; IMFAttributes *attributes = NULL; - HRESULT hr; + MF_ATTRIBUTE_TYPE item_type; + DWORD caps, count; WCHAR *filename; + IUnknown *unk; + HRESULT hr; + WCHAR *str; static const WCHAR newfilename[] = {'n','e','w','.','m','p','4',0}; @@ -1093,10 +1138,57 @@ static void test_MFCreateFile(void) MF_FILEFLAGS_NONE, filename, &bytestream); ok(hr == S_OK, "got 0x%08x\n", hr); + hr = IMFByteStream_QueryInterface(bytestream, &IID_IMFByteStreamBuffering, (void **)&unk); + ok(hr == E_NOINTERFACE, "Unexpected hr %#x.\n", hr); + + hr = IMFByteStream_QueryInterface(bytestream, &IID_IMFByteStreamCacheControl, (void **)&unk); + ok(hr == E_NOINTERFACE, "Unexpected hr %#x.\n", hr); + + hr = IMFByteStream_QueryInterface(bytestream, &IID_IMFMediaEventGenerator, (void **)&unk); + ok(hr == E_NOINTERFACE, "Unexpected hr %#x.\n", hr); + + hr = IMFByteStream_QueryInterface(bytestream, &IID_IMFGetService, (void **)&unk); +todo_wine + ok(hr == S_OK, "Failed to get interface pointer, hr %#x.\n", hr); + if (SUCCEEDED(hr)) + IUnknown_Release(unk); + + hr = IMFByteStream_GetCapabilities(bytestream, &caps); + ok(hr == S_OK, "Failed to get stream capabilities, hr %#x.\n", hr); + if (is_win8_plus) + { +todo_wine + ok(caps == (MFBYTESTREAM_IS_READABLE | MFBYTESTREAM_IS_SEEKABLE | MFBYTESTREAM_DOES_NOT_USE_NETWORK), + "Unexpected caps %#x.\n", caps); + } + else + ok(caps == (MFBYTESTREAM_IS_READABLE | MFBYTESTREAM_IS_SEEKABLE), "Unexpected caps %#x.\n", caps); + hr = IMFByteStream_QueryInterface(bytestream, &IID_IMFAttributes, (void **)&attributes); ok(hr == S_OK, "got 0x%08x\n", hr); ok(attributes != NULL, "got NULL\n"); + + hr = IMFAttributes_GetCount(attributes, &count); + ok(hr == S_OK, "Failed to get attributes count, hr %#x.\n", hr); +todo_wine + ok(count == 2, "Unexpected attributes count %u.\n", count); + + /* Original file name. */ + hr = IMFAttributes_GetAllocatedString(attributes, &MF_BYTESTREAM_ORIGIN_NAME, &str, &count); +todo_wine + ok(hr == S_OK, "Failed to get attribute, hr %#x.\n", hr); +if (SUCCEEDED(hr)) +{ + ok(!lstrcmpW(str, filename), "Unexpected name %s.\n", wine_dbgstr_w(str)); + CoTaskMemFree(str); +} + /* Modification time. */ + hr = IMFAttributes_GetItemType(attributes, &MF_BYTESTREAM_LAST_MODIFIED_TIME, &item_type); +todo_wine { + ok(hr == S_OK, "Failed to get item type, hr %#x.\n", hr); + ok(item_type == MF_ATTRIBUTE_BLOB, "Unexpected item type.\n"); +} IMFAttributes_Release(attributes); hr = MFCreateFile(MF_ACCESSMODE_READ, MF_OPENMODE_FAIL_IF_NOT_EXIST, @@ -2684,7 +2776,7 @@ START_TEST(mfplat) test_MFCreateMediaEvent(); test_attributes(); test_sample(); - test_MFCreateFile(); + test_file_stream(); test_MFCreateMFByteStreamOnStream(); test_system_memory_buffer(); test_source_resolver(); diff --git a/include/mfidl.idl b/include/mfidl.idl index 944f8f95bab..7f98d0e8214 100644 --- a/include/mfidl.idl +++ b/include/mfidl.idl @@ -211,6 +211,48 @@ interface IMFByteStreamHandler : IUnknown [out] QWORD *bytes); } +typedef [public] struct _MF_LEAKY_BUCKET_PAIR +{ + DWORD dwBitrate; + DWORD msBufferWindow; +} MF_LEAKY_BUCKET_PAIR; + +typedef [public] struct _MFBYTESTREAM_BUFFERING_PARAMS +{ + QWORD cbTotalFileSize; + QWORD cbPlayableDataSize; + MF_LEAKY_BUCKET_PAIR *prgBuckets; + DWORD cBuckets; + QWORD qwNetBufferingTime; + QWORD qwExtraBufferingTimeDuringSeek; + QWORD qwPlayDuration; + float dRate; +} MFBYTESTREAM_BUFFERING_PARAMS; + +[ + object, + uuid(6d66d782-1d4f-4db7-8c63-cb8c77f1ef5e), +] +interface IMFByteStreamBuffering : IUnknown +{ + HRESULT SetBufferingParams( + [in] MFBYTESTREAM_BUFFERING_PARAMS *params); + + HRESULT EnableBuffering( + [in] BOOL enable); + + HRESULT StopBuffering(); +} + +[ + object, + uuid(f5042ea4-7a96-4a75-aa7b-2be1ef7f88d5), +] +interface IMFByteStreamCacheControl : IUnknown +{ + HRESULT StopBackgroundTransfer(); +} + [ object, uuid(6d4c7b74-52a0-4bb7-b0db-55f29f47a668), diff --git a/include/mfobjects.idl b/include/mfobjects.idl index b67aec2a615..ff30b81755f 100644 --- a/include/mfobjects.idl +++ b/include/mfobjects.idl @@ -613,6 +613,7 @@ cpp_quote("#define MFBYTESTREAM_IS_DIRECTORY 0x00000080") cpp_quote("#define MFBYTESTREAM_HAS_SLOW_SEEK 0x00000100") cpp_quote("#define MFBYTESTREAM_IS_PARTIALLY_DOWNLOADED 0x00000200") cpp_quote("#define MFBYTESTREAM_SHARE_WRITE 0x00000400") +cpp_quote("#define MFBYTESTREAM_DOES_NOT_USE_NETWORK 0x00000800") cpp_quote("#define MFBYTESTREAM_SEEK_FLAG_CANCEL_PENDING_IO 0x00000001")