This also fixes a bug happening when the reader has only one stream,
in which case the iteration body was not even executed once.
Signed-off-by: Giovanni Mascellani <gmascellani@codeweavers.com>
---
dlls/mfreadwrite/reader.c | 14 ++--
dlls/mfreadwrite/tests/mfplat.c | 142 ++++++++++++++++++++++++++++++++
2 files changed, 149 insertions(+), 7 deletions(-)
diff --git a/dlls/mfreadwrite/reader.c b/dlls/mfreadwrite/reader.c
index 2819b800eb9..ab9f6c83674 100644
--- a/dlls/mfreadwrite/reader.c
+++ b/dlls/mfreadwrite/reader.c
@@ -1087,13 +1087,10 @@ static BOOL source_reader_get_read_result(struct source_reader *reader, struct m
static HRESULT source_reader_get_next_selected_stream(struct source_reader *reader, unsigned int *stream_index)
{
- unsigned int i, start_idx, stop_idx, first_selected = ~0u, requests = ~0u;
+ unsigned int i, first_selected = ~0u, requests = ~0u;
BOOL selected, stream_drained;
- start_idx = (reader->last_read_index + 1) % reader->stream_count;
- stop_idx = reader->last_read_index == ~0u ? reader->stream_count : reader->last_read_index;
-
- for (i = start_idx; i < reader->stream_count && i != stop_idx; i = (i + 1) % (reader->stream_count + 1))
+ for (i = (reader->last_read_index + 1) % reader->stream_count; ; i = (i + 1) % reader->stream_count)
{
stream_drained = reader->streams[i].state == STREAM_STATE_EOS && !reader->streams[i].responses;
selected = SUCCEEDED(source_reader_get_stream_selection(reader, i, &selected)) && selected;
@@ -1110,6 +1107,9 @@ static HRESULT source_reader_get_next_selected_stream(struct source_reader *read
*stream_index = i;
}
}
+
+ if (i == reader->last_read_index)
+ break;
}
This looks good.
/* If all selected streams reached EOS, use first selected. */
@@ -1477,7 +1477,7 @@ static HRESULT WINAPI src_reader_SetStreamSelection(IMFSourceReader *iface, DWOR
}
if (selection_changed)
- reader->last_read_index = ~0u;
+ reader->last_read_index = reader->stream_count - 1;
LeaveCriticalSection(&reader->cs);
@@ -2360,7 +2360,7 @@ static HRESULT create_source_reader_from_source(IMFMediaSource *source, IMFAttri
/* At least one major type has to be set. */
object->first_audio_stream_index = reader_get_first_stream_index(object->descriptor, &MFMediaType_Audio);
object->first_video_stream_index = reader_get_first_stream_index(object->descriptor, &MFMediaType_Video);
- object->last_read_index = ~0u;
+ object->last_read_index = object->stream_count - 1;
if (object->first_audio_stream_index == MF_SOURCE_READER_INVALID_STREAM_INDEX &&
object->first_video_stream_index == MF_SOURCE_READER_INVALID_STREAM_INDEX)
diff --git a/dlls/mfreadwrite/tests/mfplat.c b/dlls/mfreadwrite/tests/mfplat.c
index 25aa29ac272..e3ac0ae8006 100644
--- a/dlls/mfreadwrite/tests/mfplat.c
+++ b/dlls/mfreadwrite/tests/mfplat.c
@@ -564,6 +564,105 @@ static void test_factory(void)
CoUninitialize();
}
+struct read_all_async_callback
+{
+ IMFSourceReaderCallback IMFSourceReaderCallback_iface;
+ LONG refcount;
+ HANDLE event;
+ IMFSourceReader *reader;
+};
+
+static struct read_all_async_callback *impl_read_all_async_callback_from_IMFSourceReaderCallback(IMFSourceReaderCallback *iface)
+{
+ return CONTAINING_RECORD(iface, struct read_all_async_callback, IMFSourceReaderCallback_iface);
+}
+
+static HRESULT WINAPI read_all_async_callback_QueryInterface(IMFSourceReaderCallback *iface, REFIID riid, void **out)
+{
+ if (IsEqualIID(riid, &IID_IMFSourceReaderCallback) ||
+ IsEqualIID(riid, &IID_IUnknown))
+ {
+ *out = iface;
+ IMFSourceReaderCallback_AddRef(iface);
+ return S_OK;
+ }
+
+ *out = NULL;
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI read_all_async_callback_AddRef(IMFSourceReaderCallback *iface)
+{
+ struct read_all_async_callback *callback = impl_read_all_async_callback_from_IMFSourceReaderCallback(iface);
+ return InterlockedIncrement(&callback->refcount);
+}
+
+static ULONG WINAPI read_all_async_callback_Release(IMFSourceReaderCallback *iface)
+{
+ struct read_all_async_callback *callback = impl_read_all_async_callback_from_IMFSourceReaderCallback(iface);
+ ULONG refcount = InterlockedDecrement(&callback->refcount);
+
+ if (!refcount)
+ {
+ CloseHandle(callback->event);
+ heap_free(callback);
+ }
+
+ return refcount;
+}
+
+static HRESULT WINAPI read_all_async_callback_OnReadSample(IMFSourceReaderCallback *iface, HRESULT hr, DWORD stream_index,
+ DWORD stream_flags, LONGLONG timestamp, IMFSample *sample)
+{
+ struct read_all_async_callback *callback = impl_read_all_async_callback_from_IMFSourceReaderCallback(iface);
+
+ ok(hr == S_OK, "Failed to read sample, hr %#x.\n", hr);
+
+ if (stream_flags & MF_SOURCE_READERF_ENDOFSTREAM)
+ SetEvent(callback->event);
+ else
+ {
+ hr = IMFSourceReader_ReadSample(callback->reader, MF_SOURCE_READER_ANY_STREAM, 0, NULL, NULL, NULL, NULL);
+ ok(hr == S_OK, "Failed to read sample, hr %#x.\n", hr);
+ }
+
+ return S_OK;
+}
+
+static HRESULT WINAPI read_all_async_callback_OnFlush(IMFSourceReaderCallback *iface, DWORD stream_index)
+{
+ ok(0, "Unexpected call.\n");
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI read_all_async_callback_OnEvent(IMFSourceReaderCallback *iface, DWORD stream_index, IMFMediaEvent *event)
+{
+ ok(0, "Unexpected call.\n");
+ return E_NOTIMPL;
+}
+
+static const IMFSourceReaderCallbackVtbl read_all_async_callback_vtbl =
+{
+ read_all_async_callback_QueryInterface,
+ read_all_async_callback_AddRef,
+ read_all_async_callback_Release,
+ read_all_async_callback_OnReadSample,
+ read_all_async_callback_OnFlush,
+ read_all_async_callback_OnEvent,
+};
+
+static struct read_all_async_callback *create_read_all_async_callback(void)
+{
+ struct read_all_async_callback *callback;
+
+ callback = heap_alloc(sizeof(*callback));
+ callback->IMFSourceReaderCallback_iface.lpVtbl = &read_all_async_callback_vtbl;
+ callback->refcount = 1;
+ callback->event = CreateEventW(NULL, FALSE, FALSE, NULL);
+
+ return callback;
+}
+
struct async_callback
{
IMFSourceReaderCallback IMFSourceReaderCallback_iface;
@@ -1144,6 +1243,48 @@ done:
DestroyWindow(window);
}
+static void test_read_all(void)
+{
+ IMFByteStream *stream;
+ IMFSourceReader *reader;
+ IMFAttributes *attributes;
+ struct read_all_async_callback *callback;
+ HRESULT hr;
+
+ if (!pMFCreateMFByteStreamOnStream)
+ {
+ win_skip("MFCreateMFByteStreamOnStream() not found\n");
+ return;
+ }
+
+ stream = get_resource_stream("test.wav");
+ callback = create_read_all_async_callback();
+
+ hr = MFCreateAttributes(&attributes, 1);
+ ok(hr == S_OK, "Failed to create attributes object, hr %#x.\n", hr);
+
+ hr = IMFAttributes_SetUnknown(attributes, &MF_SOURCE_READER_ASYNC_CALLBACK,
+ (IUnknown*)&callback->IMFSourceReaderCallback_iface);
+ ok(hr == S_OK, "Failed to set attribute value, hr %#x.\n", hr);
+
+ hr = MFCreateSourceReaderFromByteStream(stream, attributes, &reader);
+ ok(hr == S_OK, "Failed to create source reader, hr %#x.\n", hr);
+ callback->reader = reader;
+
+ hr = IMFSourceReader_SetStreamSelection(reader, MF_SOURCE_READER_ALL_STREAMS, TRUE);
+ ok(hr == S_OK, "Failed to select streams, hr %#x.\n", hr);
+
+ hr = IMFSourceReader_ReadSample(reader, MF_SOURCE_READER_ANY_STREAM, 0, NULL, NULL, NULL, NULL);
+ ok(hr == S_OK, "Failed to read sample, hr %#x.\n", hr);
+
+ WaitForSingleObject(callback->event, INFINITE);
+
+ IMFSourceReader_Release(reader);
+ IMFAttributes_Release(attributes);
+ IMFSourceReaderCallback_Release(&callback->IMFSourceReaderCallback_iface);
+ IMFByteStream_Release(stream);
+}
+
START_TEST(mfplat)
{
HRESULT hr;
@@ -1157,6 +1298,7 @@ START_TEST(mfplat)
test_source_reader();
test_source_reader_from_media_source();
test_reader_d3d9();
+ test_read_all();
hr = MFShutdown();
ok(hr == S_OK, "Failed to shut down, hr %#x.\n", hr);
I don't see why such test complexity is necessary.