[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