[PATCH 4/7] mfplat: Implement IStream-based bytestream object.

Nikolay Sivov nsivov at codeweavers.com
Fri Mar 22 01:15:16 CDT 2019


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 dlls/mfplat/main.c         | 649 +++++++++++++++++++++++++++++++++----
 dlls/mfplat/tests/mfplat.c | 100 +++++-
 include/mfidl.idl          |  42 +++
 include/mfobjects.idl      |   1 +
 4 files changed, 726 insertions(+), 66 deletions(-)

diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c
index dd89f4c91a..2adf3819c1 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
+    {
+        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;
+    }
+
+    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)
+    {
+        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 = &This->IMFByteStream_iface;
+        *out = &stream->IMFByteStream_iface;
     }
-    else if(IsEqualGUID(riid, &IID_IMFAttributes))
+    else if (IsEqualIID(riid, &IID_IMFAttributes))
     {
-        *out = &This->attributes.IMFAttributes_iface;
+        *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
 };
 
+static HRESULT WINAPI bytestream_stream_read_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_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)
 {
-    mfbytestream *object;
+    struct bytestream *object;
+    LARGE_INTEGER position;
     HRESULT hr;
 
-    TRACE("(%p, %p): stub\n", stream, bytestream);
+    TRACE("%p, %p.\n", stream, bytestream);
 
-    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)))
@@ -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 d53251c318..f0148e1a5a 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 944f8f95ba..7f98d0e821 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 b67aec2a61..ff30b81755 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")
 
-- 
2.20.1




More information about the wine-devel mailing list