Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mfreadwrite/reader.c | 146 ++++++++++++++++++++------------ dlls/mfreadwrite/tests/mfplat.c | 43 ++++++---- 2 files changed, 119 insertions(+), 70 deletions(-)
diff --git a/dlls/mfreadwrite/reader.c b/dlls/mfreadwrite/reader.c index e46b6003eb..c4a5981ea7 100644 --- a/dlls/mfreadwrite/reader.c +++ b/dlls/mfreadwrite/reader.c @@ -98,9 +98,10 @@ enum media_source_state
enum media_stream_flags { - STREAM_FLAG_SAMPLE_REQUESTED = 0x1, - STREAM_FLAG_SELECTED = 0x2, - STREAM_FLAG_PRESENTED = 0x4, + STREAM_FLAG_SAMPLE_REQUESTED = 0x1, /* Protects from making multiple sample requests. */ + STREAM_FLAG_SELECTED = 0x2, /* Mirrors descriptor, used to simplify tests when starting the source. */ + STREAM_FLAG_PRESENTED = 0x4, /* Set if stream was selected last time Start() was called. */ + STREAM_FLAG_REQUESTED_ONCE = 0x8, /* Used for MF_SOURCE_READER_ANY_STREAM in synchronous mode. */ };
struct media_stream @@ -363,7 +364,9 @@ static HRESULT source_reader_request_sample(struct source_reader *reader, struct if (FAILED(hr = IMFMediaStream_RequestSample(stream->stream, NULL))) WARN("Sample request failed, hr %#x.\n", hr); else - stream->flags |= STREAM_FLAG_SAMPLE_REQUESTED; + { + stream->flags |= (STREAM_FLAG_SAMPLE_REQUESTED | STREAM_FLAG_REQUESTED_ONCE); + } }
return hr; @@ -852,16 +855,19 @@ static HRESULT source_reader_start_source(struct source_reader *reader) HRESULT hr = S_OK; unsigned int i;
+ for (i = 0; i < reader->stream_count; ++i) + { + source_reader_get_stream_selection(reader, i, &selected); + if (selected) + reader->streams[i].flags |= STREAM_FLAG_SELECTED; + else + reader->streams[i].flags &= ~STREAM_FLAG_SELECTED; + } + if (reader->source_state == SOURCE_STATE_STARTED) { for (i = 0; i < reader->stream_count; ++i) { - if (FAILED(hr = source_reader_get_stream_selection(reader, i, &selected))) - return hr; - if (selected) - reader->streams[i].flags |= STREAM_FLAG_SELECTED; - else - reader->streams[i].flags &= ~STREAM_FLAG_SELECTED; selection_changed = !!(reader->streams[i].flags & STREAM_FLAG_SELECTED) ^ !!(reader->streams[i].flags & STREAM_FLAG_PRESENTED); if (selection_changed) @@ -940,10 +946,29 @@ static BOOL source_reader_get_read_result(struct source_reader *reader, struct m return !request_sample; }
-static HRESULT source_reader_get_stream_read_index(struct source_reader *reader, unsigned int index, unsigned int *stream_index) +static HRESULT source_reader_get_first_selected_stream(struct source_reader *reader, unsigned int flags, + unsigned int *stream_index) { unsigned int i; BOOL selected; + + for (i = 0; i < reader->stream_count; ++i) + { + source_reader_get_stream_selection(reader, i, &selected); + if (SUCCEEDED(source_reader_get_stream_selection(reader, i, &selected)) && selected && + !(reader->streams[i].flags & flags)) + { + *stream_index = i; + break; + } + } + + return i == reader->stream_count ? MF_E_MEDIA_SOURCE_NO_STREAMS_SELECTED : S_OK; +} + +static HRESULT source_reader_get_stream_read_index(struct source_reader *reader, unsigned int index, unsigned int *stream_index) +{ + BOOL selected; HRESULT hr;
switch (index) @@ -958,23 +983,15 @@ static HRESULT source_reader_get_stream_read_index(struct source_reader *reader, if (reader->async_callback) { /* Pick first selected stream. */ - for (i = 0; i < reader->stream_count; ++i) - { - if (SUCCEEDED(source_reader_get_stream_selection(reader, i, &selected)) && selected) - { - *stream_index = i; - break; - } - } - - if (i == reader->stream_count) - return MF_E_MEDIA_SOURCE_NO_STREAMS_SELECTED; + hr = source_reader_get_first_selected_stream(reader, 0, stream_index); } else { - FIXME("Non-specific requests are not supported.\n"); - return E_NOTIMPL; + /* Cycle through all selected streams once, next pick first selected. */ + if (FAILED(hr = source_reader_get_first_selected_stream(reader, STREAM_FLAG_REQUESTED_ONCE, stream_index))) + hr = source_reader_get_first_selected_stream(reader, 0, stream_index); } + return hr; default: *stream_index = index; } @@ -1233,48 +1250,69 @@ static HRESULT WINAPI src_reader_GetStreamSelection(IMFSourceReader *iface, DWOR return source_reader_get_stream_selection(reader, index, selected); }
-static HRESULT WINAPI src_reader_SetStreamSelection(IMFSourceReader *iface, DWORD index, BOOL selected) +static HRESULT WINAPI src_reader_SetStreamSelection(IMFSourceReader *iface, DWORD index, BOOL selection) { struct source_reader *reader = impl_from_IMFSourceReader(iface); - unsigned int count; - HRESULT hr; + HRESULT hr = S_OK; + BOOL selection_changed = FALSE, selected; + unsigned int i;
- TRACE("%p, %#x, %d.\n", iface, index, selected); + TRACE("%p, %#x, %d.\n", iface, index, selection);
- switch (index) - { - case MF_SOURCE_READER_FIRST_VIDEO_STREAM: - index = reader->first_video_stream_index; - break; - case MF_SOURCE_READER_FIRST_AUDIO_STREAM: - index = reader->first_audio_stream_index; - break; - case MF_SOURCE_READER_ALL_STREAMS: - if (FAILED(hr = IMFPresentationDescriptor_GetStreamDescriptorCount(reader->descriptor, &count))) - return hr; + selection = !!selection;
- for (index = 0; index < count; ++index) + EnterCriticalSection(&reader->cs); + + if (index == MF_SOURCE_READER_ALL_STREAMS) + { + for (i = 0; i < reader->stream_count; ++i) + { + if (!selection_changed) { - if (selected) - IMFPresentationDescriptor_SelectStream(reader->descriptor, index); - else - IMFPresentationDescriptor_DeselectStream(reader->descriptor, index); + source_reader_get_stream_selection(reader, i, &selected); + if (selected ^ selection) + selection_changed = TRUE; }
- return S_OK; - default: - ; + if (selection) + IMFPresentationDescriptor_SelectStream(reader->descriptor, i); + else + IMFPresentationDescriptor_DeselectStream(reader->descriptor, i); + } } - - if (selected) - hr = IMFPresentationDescriptor_SelectStream(reader->descriptor, index); else - hr = IMFPresentationDescriptor_DeselectStream(reader->descriptor, index); + { + switch (index) + { + case MF_SOURCE_READER_FIRST_VIDEO_STREAM: + index = reader->first_video_stream_index; + break; + case MF_SOURCE_READER_FIRST_AUDIO_STREAM: + index = reader->first_audio_stream_index; + break; + default: + ; + }
- if (FAILED(hr)) - return MF_E_INVALIDSTREAMNUMBER; + source_reader_get_stream_selection(reader, index, &selected); + if (selected ^ selection) + selection_changed = TRUE;
- return S_OK; + if (selection) + hr = IMFPresentationDescriptor_SelectStream(reader->descriptor, index); + else + hr = IMFPresentationDescriptor_DeselectStream(reader->descriptor, index); + } + + if (SUCCEEDED(hr) && selection_changed) + { + for (i = 0; i < reader->stream_count; ++i) + reader->streams[i].flags &= ~STREAM_FLAG_REQUESTED_ONCE; + } + + LeaveCriticalSection(&reader->cs); + + return SUCCEEDED(hr) ? S_OK : MF_E_INVALIDSTREAMNUMBER; }
static HRESULT source_reader_get_native_media_type(struct source_reader *reader, DWORD index, DWORD type_index, diff --git a/dlls/mfreadwrite/tests/mfplat.c b/dlls/mfreadwrite/tests/mfplat.c index c35cfa3c0c..8ed9428970 100644 --- a/dlls/mfreadwrite/tests/mfplat.c +++ b/dlls/mfreadwrite/tests/mfplat.c @@ -847,38 +847,49 @@ static void test_source_reader_from_media_source(void)
hr = IMFSourceReader_ReadSample(reader, MF_SOURCE_READER_ANY_STREAM, 0, &actual_index, &stream_flags, ×tamp, &sample); -todo_wine { ok(hr == S_OK, "Failed to get a sample, hr %#x.\n", hr); - if (SUCCEEDED(hr)) + ok(actual_index == 1, "Unexpected stream index %u\n", actual_index); + ok(!stream_flags, "Unexpected stream flags %#x.\n", stream_flags); + ok(timestamp == 123, "Unexpected timestamp.\n"); + ok(!!sample, "Expected sample object.\n"); + IMFSample_Release(sample); + + hr = IMFSourceReader_SetStreamSelection(reader, 0, TRUE); + ok(hr == S_OK, "Failed to select a stream, hr %#x.\n", hr); + + hr = IMFSourceReader_SetStreamSelection(reader, 2, TRUE); + ok(hr == S_OK, "Failed to select a stream, hr %#x.\n", hr); + + for (i = 0; i < 2 * TEST_SOURCE_NUM_STREAMS; ++i) { - ok(actual_index == 1, "Unexpected stream index %u\n", actual_index); + hr = IMFSourceReader_ReadSample(reader, MF_SOURCE_READER_ANY_STREAM, 0, &actual_index, &stream_flags, + ×tamp, &sample); + ok(hr == S_OK, "Failed to get a sample, hr %#x.\n", hr); + ok(actual_index == (i < TEST_SOURCE_NUM_STREAMS ? i : 0), "%d: Unexpected stream index %u\n", + i, actual_index); ok(!stream_flags, "Unexpected stream flags %#x.\n", stream_flags); ok(timestamp == 123, "Unexpected timestamp.\n"); ok(!!sample, "Expected sample object.\n"); IMFSample_Release(sample); } -} - hr = IMFSourceReader_SetStreamSelection(reader, 0, TRUE); + + hr = IMFSourceReader_SetStreamSelection(reader, 0, FALSE); ok(hr == S_OK, "Failed to select a stream, hr %#x.\n", hr);
- hr = IMFSourceReader_SetStreamSelection(reader, 2, TRUE); + hr = IMFSourceReader_SetStreamSelection(reader, 0, TRUE); ok(hr == S_OK, "Failed to select a stream, hr %#x.\n", hr);
for (i = 0; i < 2 * TEST_SOURCE_NUM_STREAMS; ++i) { hr = IMFSourceReader_ReadSample(reader, MF_SOURCE_READER_ANY_STREAM, 0, &actual_index, &stream_flags, ×tamp, &sample); -todo_wine ok(hr == S_OK, "Failed to get a sample, hr %#x.\n", hr); - if (SUCCEEDED(hr)) - { - ok(actual_index == (i < TEST_SOURCE_NUM_STREAMS ? i : 0), "%d: Unexpected stream index %u\n", - i, actual_index); - ok(!stream_flags, "Unexpected stream flags %#x.\n", stream_flags); - ok(timestamp == 123, "Unexpected timestamp.\n"); - ok(!!sample, "Expected sample object.\n"); - IMFSample_Release(sample); - } + ok(actual_index == (i < TEST_SOURCE_NUM_STREAMS ? i : 0), "%d: Unexpected stream index %u\n", + i, actual_index); + ok(!stream_flags, "Unexpected stream flags %#x.\n", stream_flags); + ok(timestamp == 123, "Unexpected timestamp.\n"); + ok(!!sample, "Expected sample object.\n"); + IMFSample_Release(sample); }
IMFSourceReader_Release(reader);
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/mfreadwrite/reader.c | 147 +++++++++++++++++++++++++++++--------- 1 file changed, 115 insertions(+), 32 deletions(-)
diff --git a/dlls/mfreadwrite/reader.c b/dlls/mfreadwrite/reader.c index c4a5981ea7..e66eb03135 100644 --- a/dlls/mfreadwrite/reader.c +++ b/dlls/mfreadwrite/reader.c @@ -119,6 +119,7 @@ struct media_stream enum source_reader_async_op { SOURCE_READER_ASYNC_READ, + SOURCE_READER_ASYNC_SEEK, SOURCE_READER_ASYNC_FLUSH, SOURCE_READER_ASYNC_SAMPLE_READY, }; @@ -128,14 +129,34 @@ struct source_reader_async_command IUnknown IUnknown_iface; LONG refcount; enum source_reader_async_op op; - unsigned int flags; - unsigned int stream_index; + union + { + struct + { + unsigned int flags; + unsigned int stream_index; + } read; + struct + { + GUID format; + PROPVARIANT position; + } seek; + struct + { + unsigned int stream_index; + } flush; + struct + { + unsigned int stream_index; + } sample; + } u; };
enum source_reader_flags { SOURCE_READER_FLUSHING = 0x1, - SOURCE_READER_SHUTDOWN_ON_RELEASE = 0x2, + SOURCE_READER_SEEKING = 0x2, + SOURCE_READER_SHUTDOWN_ON_RELEASE = 0x4, };
struct source_reader @@ -157,6 +178,7 @@ struct source_reader struct list responses; CRITICAL_SECTION cs; CONDITION_VARIABLE sample_event; + CONDITION_VARIABLE state_event; };
static inline struct source_reader *impl_from_IMFSourceReader(IMFSourceReader *iface) @@ -210,7 +232,11 @@ static ULONG WINAPI source_reader_async_command_Release(IUnknown *iface) ULONG refcount = InterlockedIncrement(&command->refcount);
if (!refcount) + { + if (command->op == SOURCE_READER_ASYNC_SEEK) + PropVariantClear(&command->u.seek.position); heap_free(command); + }
return refcount; } @@ -341,7 +367,7 @@ static void source_reader_queue_response(struct source_reader *reader, struct me { if (SUCCEEDED(source_reader_create_async_op(SOURCE_READER_ASYNC_SAMPLE_READY, &command))) { - command->stream_index = stream->index; + command->u.sample.stream_index = stream->index; if (FAILED(hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, &reader->async_commands_callback, &command->IUnknown_iface))) WARN("Failed to submit async result, hr %#x.\n", hr); @@ -429,25 +455,27 @@ static HRESULT source_reader_new_stream_handler(struct source_reader *reader, IM
static HRESULT source_reader_source_state_handler(struct source_reader *reader, MediaEventType event_type) { - enum media_source_state state; + EnterCriticalSection(&reader->cs);
switch (event_type) { case MESourceStarted: - state = SOURCE_STATE_STARTED; + reader->source_state = SOURCE_STATE_STARTED; break; case MESourceStopped: - state = SOURCE_STATE_STOPPED; + reader->source_state = SOURCE_STATE_STOPPED; + break; + case MESourceSeeked: + reader->flags &= ~SOURCE_READER_SEEKING; break; default: - WARN("Unhandled state %d.\n", event_type); - return E_FAIL; + WARN("Unhandled event %d.\n", event_type); }
- EnterCriticalSection(&reader->cs); - reader->source_state = state; LeaveCriticalSection(&reader->cs);
+ WakeAllConditionVariable(&reader->state_event); + return S_OK; }
@@ -478,6 +506,7 @@ static HRESULT WINAPI source_reader_source_events_callback_Invoke(IMFAsyncCallba case MESourceStarted: case MESourcePaused: case MESourceStopped: + case MESourceSeeked: hr = source_reader_source_state_handler(reader, event_type); break; case MEBufferingStarted: @@ -1089,12 +1118,12 @@ static HRESULT WINAPI source_reader_async_commands_callback_Invoke(IMFAsyncCallb
if (SUCCEEDED(hr = source_reader_start_source(reader))) { - if (SUCCEEDED(hr = source_reader_get_stream_read_index(reader, command->stream_index, &stream_index))) + if (SUCCEEDED(hr = source_reader_get_stream_read_index(reader, command->u.read.stream_index, &stream_index))) { stream = &reader->streams[stream_index];
- if (!(report_sample = source_reader_get_read_result(reader, stream, command->flags, &status, &stream_index, - &stream_flags, ×tamp, &sample))) + if (!(report_sample = source_reader_get_read_result(reader, stream, command->u.read.flags, &status, + &stream_index, &stream_flags, ×tamp, &sample))) { stream->requests++; source_reader_request_sample(reader, stream); @@ -1103,7 +1132,7 @@ static HRESULT WINAPI source_reader_async_commands_callback_Invoke(IMFAsyncCallb } else { - stub_stream.index = command->stream_index; + stub_stream.index = command->u.read.stream_index; source_reader_queue_response(reader, &stub_stream, hr, MF_SOURCE_READERF_ERROR, 0, NULL); } } @@ -1119,6 +1148,18 @@ static HRESULT WINAPI source_reader_async_commands_callback_Invoke(IMFAsyncCallb
break;
+ case SOURCE_READER_ASYNC_SEEK: + + EnterCriticalSection(&reader->cs); + if (SUCCEEDED(IMFMediaSource_Start(reader->source, reader->descriptor, &command->u.seek.format, + &command->u.seek.position))) + { + reader->flags |= SOURCE_READER_SEEKING; + } + LeaveCriticalSection(&reader->cs); + + break; + case SOURCE_READER_ASYNC_SAMPLE_READY:
EnterCriticalSection(&reader->cs); @@ -1135,11 +1176,11 @@ static HRESULT WINAPI source_reader_async_commands_callback_Invoke(IMFAsyncCallb break; case SOURCE_READER_ASYNC_FLUSH: EnterCriticalSection(&reader->cs); - source_reader_flush(reader, command->stream_index); + source_reader_flush(reader, command->u.flush.stream_index); reader->flags &= ~SOURCE_READER_FLUSHING; LeaveCriticalSection(&reader->cs);
- IMFSourceReaderCallback_OnFlush(reader->async_callback, command->stream_index); + IMFSourceReaderCallback_OnFlush(reader->async_callback, command->u.flush.stream_index); break; default: ; @@ -1620,20 +1661,60 @@ static HRESULT WINAPI src_reader_SetCurrentMediaType(IMFSourceReader *iface, DWO static HRESULT WINAPI src_reader_SetCurrentPosition(IMFSourceReader *iface, REFGUID format, REFPROPVARIANT position) { struct source_reader *reader = impl_from_IMFSourceReader(iface); - DWORD flags; + struct source_reader_async_command *command; + unsigned int i, flags; HRESULT hr;
TRACE("%p, %s, %p.\n", iface, debugstr_guid(format), position);
- /* FIXME: fail if we got pending samples. */ - if (FAILED(hr = IMFMediaSource_GetCharacteristics(reader->source, &flags))) return hr;
if (!(flags & MFMEDIASOURCE_CAN_SEEK)) return MF_E_INVALIDREQUEST;
- return IMFMediaSource_Start(reader->source, reader->descriptor, format, position); + EnterCriticalSection(&reader->cs); + + /* Check if we got pending requests. */ + for (i = 0; i < reader->stream_count; ++i) + { + if (reader->streams[i].requests) + { + hr = MF_E_INVALIDREQUEST; + break; + } + } + + if (SUCCEEDED(hr)) + { + if (reader->async_callback) + { + if (SUCCEEDED(hr = source_reader_create_async_op(SOURCE_READER_ASYNC_SEEK, &command))) + { + command->u.seek.format = *format; + PropVariantCopy(&command->u.seek.position, position); + + hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_MULTITHREADED, &reader->async_commands_callback, + &command->IUnknown_iface); + IUnknown_Release(&command->IUnknown_iface); + } + } + else + { + if (SUCCEEDED(IMFMediaSource_Start(reader->source, reader->descriptor, format, position))) + { + reader->flags |= SOURCE_READER_SEEKING; + while (reader->flags & SOURCE_READER_SEEKING) + { + SleepConditionVariableCS(&reader->state_event, &reader->cs, INFINITE); + } + } + } + } + + LeaveCriticalSection(&reader->cs); + + return hr; }
static HRESULT source_reader_read_sample(struct source_reader *reader, DWORD index, DWORD flags, DWORD *actual_index, @@ -1656,8 +1737,6 @@ static HRESULT source_reader_read_sample(struct source_reader *reader, DWORD ind if (!actual_index) actual_index = &actual_index_tmp;
- EnterCriticalSection(&reader->cs); - if (SUCCEEDED(hr = source_reader_start_source(reader))) { if (SUCCEEDED(hr = source_reader_get_stream_read_index(reader, index, &stream_index))) @@ -1689,8 +1768,6 @@ static HRESULT source_reader_read_sample(struct source_reader *reader, DWORD ind } }
- LeaveCriticalSection(&reader->cs); - TRACE("Stream %u, got sample %p, flags %#x.\n", *actual_index, *sample, *stream_flags);
return hr; @@ -1705,24 +1782,20 @@ static HRESULT source_reader_read_sample_async(struct source_reader *reader, uns if (actual_index || stream_flags || timestamp || sample) return E_INVALIDARG;
- EnterCriticalSection(&reader->cs); - if (reader->flags & SOURCE_READER_FLUSHING) hr = MF_E_NOTACCEPTING; else { if (SUCCEEDED(hr = source_reader_create_async_op(SOURCE_READER_ASYNC_READ, &command))) { - command->stream_index = index; - command->flags = flags; + command->u.read.stream_index = index; + command->u.read.flags = flags;
hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, &reader->async_commands_callback, &command->IUnknown_iface); IUnknown_Release(&command->IUnknown_iface); } }
- LeaveCriticalSection(&reader->cs); - return hr; }
@@ -1734,11 +1807,20 @@ static HRESULT WINAPI src_reader_ReadSample(IMFSourceReader *iface, DWORD index,
TRACE("%p, %#x, %#x, %p, %p, %p, %p\n", iface, index, flags, actual_index, stream_flags, timestamp, sample);
+ EnterCriticalSection(&reader->cs); + + while (reader->flags & SOURCE_READER_SEEKING) + { + SleepConditionVariableCS(&reader->state_event, &reader->cs, INFINITE); + } + if (reader->async_callback) hr = source_reader_read_sample_async(reader, index, flags, actual_index, stream_flags, timestamp, sample); else hr = source_reader_read_sample(reader, index, flags, actual_index, stream_flags, timestamp, sample);
+ LeaveCriticalSection(&reader->cs); + return hr; }
@@ -1771,7 +1853,7 @@ static HRESULT source_reader_flush_async(struct source_reader *reader, unsigned if (FAILED(hr = source_reader_create_async_op(SOURCE_READER_ASYNC_FLUSH, &command))) return hr;
- command->stream_index = stream_index; + command->u.flush.stream_index = stream_index;
hr = MFPutWorkItem(MFASYNC_CALLBACK_QUEUE_STANDARD, &reader->async_commands_callback, &command->IUnknown_iface); IUnknown_Release(&command->IUnknown_iface); @@ -1964,6 +2046,7 @@ static HRESULT create_source_reader_from_source(IMFMediaSource *source, IMFAttri IMFMediaSource_AddRef(object->source); InitializeCriticalSection(&object->cs); InitializeConditionVariable(&object->sample_event); + InitializeConditionVariable(&object->state_event);
if (FAILED(hr = IMFMediaSource_CreatePresentationDescriptor(object->source, &object->descriptor))) goto failed;