Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/amstream/filter.c | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/dlls/amstream/filter.c b/dlls/amstream/filter.c index fb9b1d9aee..4b5e184f04 100644 --- a/dlls/amstream/filter.c +++ b/dlls/amstream/filter.c @@ -364,10 +364,16 @@ static HRESULT WINAPI filter_EnumPins(IMediaStreamFilter *iface, IEnumPins **enu EnterCriticalSection(&filter->cs);
if (!enum_pins) + { + LeaveCriticalSection(&filter->cs); return E_POINTER; + }
if (!(object = heap_alloc(sizeof(*object)))) + { + LeaveCriticalSection(&filter->cs); return E_OUTOFMEMORY; + }
object->IEnumPins_iface.lpVtbl = &enum_pins_vtbl; object->refcount = 1; @@ -376,6 +382,7 @@ static HRESULT WINAPI filter_EnumPins(IMediaStreamFilter *iface, IEnumPins **enu if (!(object->pins = heap_alloc(filter->nb_streams * sizeof(*object->pins)))) { heap_free(object); + LeaveCriticalSection(&filter->cs); return E_OUTOFMEMORY; } for (i = 0; i < filter->nb_streams; ++i)
Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/amstream/audiostream.c | 36 ++++++++-- dlls/amstream/tests/amstream.c | 123 +++++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+), 5 deletions(-)
diff --git a/dlls/amstream/audiostream.c b/dlls/amstream/audiostream.c index f9470fa5f9..8fba877719 100644 --- a/dlls/amstream/audiostream.c +++ b/dlls/amstream/audiostream.c @@ -56,6 +56,7 @@ struct audio_stream WAVEFORMATEX format; FILTER_STATE state; BOOL eos; + BOOL flushing; struct list receive_queue; struct list update_queue; }; @@ -1084,7 +1085,7 @@ static HRESULT WINAPI audio_sink_EndOfStream(IPin *iface)
EnterCriticalSection(&stream->cs);
- if (stream->eos) + if (stream->eos || stream->flushing) { LeaveCriticalSection(&stream->cs); return E_FAIL; @@ -1101,14 +1102,34 @@ static HRESULT WINAPI audio_sink_EndOfStream(IPin *iface)
static HRESULT WINAPI audio_sink_BeginFlush(IPin *iface) { - FIXME("iface %p, stub!\n", iface); - return E_NOTIMPL; + struct audio_stream *stream = impl_from_IPin(iface); + + TRACE("stream %p.\n", stream); + + EnterCriticalSection(&stream->cs); + + stream->flushing = TRUE; + stream->eos = FALSE; + flush_receive_queue(stream); + + LeaveCriticalSection(&stream->cs); + + return S_OK; }
static HRESULT WINAPI audio_sink_EndFlush(IPin *iface) { - FIXME("iface %p, stub!\n", iface); - return E_NOTIMPL; + struct audio_stream *stream = impl_from_IPin(iface); + + TRACE("stream %p.\n", stream); + + EnterCriticalSection(&stream->cs); + + stream->flushing = FALSE; + + LeaveCriticalSection(&stream->cs); + + return S_OK; }
static HRESULT WINAPI audio_sink_NewSegment(IPin *iface, REFERENCE_TIME start, REFERENCE_TIME stop, double rate) @@ -1219,6 +1240,11 @@ static HRESULT WINAPI audio_meminput_Receive(IMemInputPin *iface, IMediaSample * LeaveCriticalSection(&stream->cs); return VFW_E_WRONG_STATE; } + if (stream->flushing) + { + LeaveCriticalSection(&stream->cs); + return S_FALSE; + }
hr = IMediaSample_GetPointer(sample, &pointer); if (FAILED(hr)) diff --git a/dlls/amstream/tests/amstream.c b/dlls/amstream/tests/amstream.c index d5b90d9257..aa32c266c8 100644 --- a/dlls/amstream/tests/amstream.c +++ b/dlls/amstream/tests/amstream.c @@ -3084,6 +3084,128 @@ static void test_audiostream_initialize(void) ok(!ref, "Got outstanding refcount %d.\n", ref); }
+static void test_audiostream_begin_flush_end_flush(void) +{ + static const WAVEFORMATEX format = + { + .wFormatTag = WAVE_FORMAT_PCM, + .nChannels = 1, + .nSamplesPerSec = 11025, + .wBitsPerSample = 16, + .nBlockAlign = 2, + .nAvgBytesPerSec = 2 * 11025, + }; + + const AM_MEDIA_TYPE mt = + { + .majortype = MEDIATYPE_Audio, + .subtype = MEDIASUBTYPE_PCM, + .formattype = FORMAT_WaveFormatEx, + .cbFormat = sizeof(WAVEFORMATEX), + .pbFormat = (BYTE *)&format, + }; + + IAMMultiMediaStream *mmstream = create_ammultimediastream(); + IAudioStreamSample *stream_sample; + IAudioMediaStream *audio_stream; + IMediaSample *media_sample; + struct testfilter source; + IAudioData *audio_data; + IGraphBuilder *graph; + IMediaStream *stream; + HRESULT hr; + ULONG ref; + IPin *pin; + + hr = IAMMultiMediaStream_Initialize(mmstream, STREAMTYPE_READ, 0, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMultiMediaStream_AddMediaStream(mmstream, NULL, &MSPID_PrimaryAudio, 0, &stream); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMediaStream_QueryInterface(stream, &IID_IAudioMediaStream, (void **)&audio_stream); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMediaStream_QueryInterface(stream, &IID_IPin, (void **)&pin); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMultiMediaStream_GetFilterGraph(mmstream, &graph); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(graph != NULL, "Expected non-NULL graph.\n"); + testfilter_init(&source); + hr = IGraphBuilder_AddFilter(graph, &source.filter.IBaseFilter_iface, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = CoCreateInstance(&CLSID_AMAudioData, NULL, CLSCTX_INPROC_SERVER, &IID_IAudioData, (void **)&audio_data); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAudioData_SetBuffer(audio_data, 16, NULL, 0); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAudioMediaStream_CreateSample(audio_stream, audio_data, 0, &stream_sample); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IGraphBuilder_ConnectDirect(graph, &source.source.pin.IPin_iface, pin, &mt); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = BaseOutputPinImpl_GetDeliveryBuffer(&source.source, &media_sample, NULL, NULL, 0); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMemInputPin_Receive(source.source.pMemInputPin, media_sample); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ref = get_refcount(media_sample); + ok(ref == 2, "Got unexpected refcount %d.\n", ref); + + hr = IPin_EndOfStream(pin); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IPin_BeginFlush(pin); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + ref = IMediaSample_Release(media_sample); + ok(!ref, "Got outstanding refcount %d.\n", ref); + + hr = BaseOutputPinImpl_GetDeliveryBuffer(&source.source, &media_sample, NULL, NULL, 0); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMemInputPin_Receive(source.source.pMemInputPin, media_sample); + ok(hr == S_FALSE, "Got hr %#x.\n", hr); + + ref = IMediaSample_Release(media_sample); + ok(!ref, "Got outstanding refcount %d.\n", ref); + + hr = IAudioStreamSample_Update(stream_sample, SSUPDATE_ASYNC, NULL, NULL, 0); + ok(hr == MS_S_PENDING, "Got hr %#x.\n", hr); + + hr = IPin_EndOfStream(pin); + ok(hr == E_FAIL, "Got hr %#x.\n", hr); + + hr = IPin_EndFlush(pin); + + hr = BaseOutputPinImpl_GetDeliveryBuffer(&source.source, &media_sample, NULL, NULL, 0); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IMemInputPin_Receive(source.source.pMemInputPin, media_sample); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ref = IMediaSample_Release(media_sample); + ok(ref == 1, "Got outstanding refcount %d.\n", ref); + + hr = IPin_EndOfStream(pin); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + IGraphBuilder_Disconnect(graph, pin); + IGraphBuilder_Disconnect(graph, &source.source.pin.IPin_iface); + + ref = IAudioStreamSample_Release(stream_sample); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ref = IAudioData_Release(audio_data); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ref = IAMMultiMediaStream_Release(mmstream); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ref = IGraphBuilder_Release(graph); + ok(!ref, "Got outstanding refcount %d.\n", ref); + IPin_Release(pin); + IAudioMediaStream_Release(audio_stream); + ref = IMediaStream_Release(stream); + ok(!ref, "Got outstanding refcount %d.\n", ref); +} + static void CALLBACK apc_func(ULONG_PTR param) { } @@ -3820,6 +3942,7 @@ START_TEST(amstream) test_audiostream_end_of_stream(); test_audiostream_receive(); test_audiostream_initialize(); + test_audiostream_begin_flush_end_flush();
test_audiostreamsample_update(); test_audiostreamsample_completion_status();
On 6/1/20 11:13 PM, Anton Baskanov wrote:
- static const WAVEFORMATEX format =
- {
.wFormatTag = WAVE_FORMAT_PCM,
.nChannels = 1,
.nSamplesPerSec = 11025,
.wBitsPerSample = 16,
.nBlockAlign = 2,
.nAvgBytesPerSec = 2 * 11025,
- };
- const AM_MEDIA_TYPE mt =
- {
.majortype = MEDIATYPE_Audio,
.subtype = MEDIASUBTYPE_PCM,
.formattype = FORMAT_WaveFormatEx,
.cbFormat = sizeof(WAVEFORMATEX),
.pbFormat = (BYTE *)&format,
- };
- IAMMultiMediaStream *mmstream = create_ammultimediastream();
- IAudioStreamSample *stream_sample;
- IAudioMediaStream *audio_stream;
- IMediaSample *media_sample;
- struct testfilter source;
- IAudioData *audio_data;
- IGraphBuilder *graph;
- IMediaStream *stream;
- HRESULT hr;
- ULONG ref;
- IPin *pin;
- hr = IAMMultiMediaStream_Initialize(mmstream, STREAMTYPE_READ, 0, NULL);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- hr = IAMMultiMediaStream_AddMediaStream(mmstream, NULL, &MSPID_PrimaryAudio, 0, &stream);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- hr = IMediaStream_QueryInterface(stream, &IID_IAudioMediaStream, (void **)&audio_stream);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- hr = IMediaStream_QueryInterface(stream, &IID_IPin, (void **)&pin);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- hr = IAMMultiMediaStream_GetFilterGraph(mmstream, &graph);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- ok(graph != NULL, "Expected non-NULL graph.\n");
- testfilter_init(&source);
- hr = IGraphBuilder_AddFilter(graph, &source.filter.IBaseFilter_iface, NULL);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- hr = CoCreateInstance(&CLSID_AMAudioData, NULL, CLSCTX_INPROC_SERVER, &IID_IAudioData, (void **)&audio_data);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- hr = IAudioData_SetBuffer(audio_data, 16, NULL, 0);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- hr = IAudioMediaStream_CreateSample(audio_stream, audio_data, 0, &stream_sample);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- hr = IGraphBuilder_ConnectDirect(graph, &source.source.pin.IPin_iface, pin, &mt);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_RUN);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- hr = BaseOutputPinImpl_GetDeliveryBuffer(&source.source, &media_sample, NULL, NULL, 0);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- hr = IMemInputPin_Receive(source.source.pMemInputPin, media_sample);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- ref = get_refcount(media_sample);
- ok(ref == 2, "Got unexpected refcount %d.\n", ref);
- hr = IPin_EndOfStream(pin);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- hr = IPin_BeginFlush(pin);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- ref = IMediaSample_Release(media_sample);
- ok(!ref, "Got outstanding refcount %d.\n", ref);
- hr = BaseOutputPinImpl_GetDeliveryBuffer(&source.source, &media_sample, NULL, NULL, 0);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- hr = IMemInputPin_Receive(source.source.pMemInputPin, media_sample);
- ok(hr == S_FALSE, "Got hr %#x.\n", hr);
- ref = IMediaSample_Release(media_sample);
- ok(!ref, "Got outstanding refcount %d.\n", ref);
- hr = IAudioStreamSample_Update(stream_sample, SSUPDATE_ASYNC, NULL, NULL, 0);
- ok(hr == MS_S_PENDING, "Got hr %#x.\n", hr);
- hr = IPin_EndOfStream(pin);
- ok(hr == E_FAIL, "Got hr %#x.\n", hr);
- hr = IPin_EndFlush(pin);
Looks like you accidentally missed an ok() call here.
- hr = BaseOutputPinImpl_GetDeliveryBuffer(&source.source, &media_sample, NULL, NULL, 0);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- hr = IMemInputPin_Receive(source.source.pMemInputPin, media_sample);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- ref = IMediaSample_Release(media_sample);
- ok(ref == 1, "Got outstanding refcount %d.\n", ref);
- hr = IPin_EndOfStream(pin);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- hr = IAMMultiMediaStream_SetState(mmstream, STREAMSTATE_STOP);
- ok(hr == S_OK, "Got hr %#x.\n", hr);
- IGraphBuilder_Disconnect(graph, pin);
- IGraphBuilder_Disconnect(graph, &source.source.pin.IPin_iface);
- ref = IAudioStreamSample_Release(stream_sample);
- ok(!ref, "Got outstanding refcount %d.\n", ref);
- ref = IAudioData_Release(audio_data);
- ok(!ref, "Got outstanding refcount %d.\n", ref);
- ref = IAMMultiMediaStream_Release(mmstream);
- ok(!ref, "Got outstanding refcount %d.\n", ref);
- ref = IGraphBuilder_Release(graph);
- ok(!ref, "Got outstanding refcount %d.\n", ref);
- IPin_Release(pin);
- IAudioMediaStream_Release(audio_stream);
- ref = IMediaStream_Release(stream);
- ok(!ref, "Got outstanding refcount %d.\n", ref);
+}
static void CALLBACK apc_func(ULONG_PTR param) { }
Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/amstream/filter.c | 249 ++++++++++++++++++++++++- dlls/amstream/tests/amstream.c | 323 +++++++++++++++++++++++++++++++++ 2 files changed, 568 insertions(+), 4 deletions(-)
diff --git a/dlls/amstream/filter.c b/dlls/amstream/filter.c index 4b5e184f04..643cfcdbe4 100644 --- a/dlls/amstream/filter.c +++ b/dlls/amstream/filter.c @@ -163,6 +163,7 @@ static const IEnumPinsVtbl enum_pins_vtbl = struct filter { IMediaStreamFilter IMediaStreamFilter_iface; + IMediaSeeking IMediaSeeking_iface; LONG refcount; CRITICAL_SECTION cs;
@@ -171,6 +172,7 @@ struct filter IFilterGraph *graph; ULONG nb_streams; IAMMediaStream **streams; + IAMMediaStream *seekable_stream; FILTER_STATE state; };
@@ -181,6 +183,8 @@ static inline struct filter *impl_from_IMediaStreamFilter(IMediaStreamFilter *if
static HRESULT WINAPI filter_QueryInterface(IMediaStreamFilter *iface, REFIID riid, void **ret_iface) { + struct filter *filter = impl_from_IMediaStreamFilter(iface); + TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ret_iface);
*ret_iface = NULL; @@ -191,10 +195,12 @@ static HRESULT WINAPI filter_QueryInterface(IMediaStreamFilter *iface, REFIID ri IsEqualIID(riid, &IID_IBaseFilter) || IsEqualIID(riid, &IID_IMediaStreamFilter)) *ret_iface = iface; + else if (IsEqualIID(riid, &IID_IMediaSeeking) && filter->seekable_stream) + *ret_iface = &filter->IMediaSeeking_iface;
if (*ret_iface) { - IMediaStreamFilter_AddRef(*ret_iface); + IUnknown_AddRef((IUnknown *)*ret_iface); return S_OK; }
@@ -544,11 +550,74 @@ static HRESULT WINAPI filter_EnumMediaStreams(IMediaStreamFilter *iface, LONG in return S_OK; }
-static HRESULT WINAPI filter_SupportSeeking(IMediaStreamFilter *iface, BOOL bRenderer) +static IMediaSeeking *get_seeking(IAMMediaStream *stream) { - FIXME("(%p)->(%d): Stub!\n", iface, bRenderer); + IPin *pin; + IPin *peer; + IMediaSeeking *seeking;
- return E_NOTIMPL; + if (FAILED(IAMMediaStream_QueryInterface(stream, &IID_IPin, (void **)&pin))) + { + WARN("Stream %p does not support IPin.\n", stream); + return NULL; + } + + if (FAILED(IPin_ConnectedTo(pin, &peer))) + { + IPin_Release(pin); + return NULL; + } + + if (FAILED(IPin_QueryInterface(peer, &IID_IMediaSeeking, (void **)&seeking))) + { + IPin_Release(peer); + IPin_Release(pin); + return NULL; + } + + IPin_Release(peer); + IPin_Release(pin); + + return seeking; +} + +static HRESULT WINAPI filter_SupportSeeking(IMediaStreamFilter *iface, BOOL renderer) +{ + struct filter *filter = impl_from_IMediaStreamFilter(iface); + unsigned int i; + HRESULT hr = E_NOINTERFACE; + + TRACE("filter %p, renderer %d\n", iface, renderer); + + EnterCriticalSection(&filter->cs); + + if (filter->seekable_stream) + return HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED); + + for (i = 0; i < filter->nb_streams; ++i) + { + IMediaSeeking *seeking = get_seeking(filter->streams[i]); + LONGLONG duration; + + if (!seeking) + continue; + + if (FAILED(IMediaSeeking_GetDuration(seeking, &duration))) + { + IMediaSeeking_Release(seeking); + continue; + } + + filter->seekable_stream = filter->streams[i]; + hr = S_OK; + IMediaSeeking_Release(seeking); + + break; + } + + LeaveCriticalSection(&filter->cs); + + return hr; }
static HRESULT WINAPI filter_ReferenceTimeToStreamTime(IMediaStreamFilter *iface, REFERENCE_TIME *pTime) @@ -614,6 +683,177 @@ static const IMediaStreamFilterVtbl filter_vtbl = filter_EndOfStream };
+static inline struct filter *impl_from_IMediaSeeking(IMediaSeeking *iface) +{ + return CONTAINING_RECORD(iface, struct filter, IMediaSeeking_iface); +} + +static HRESULT WINAPI filter_seeking_QueryInterface(IMediaSeeking *iface, REFIID riid, void **ret_iface) +{ + struct filter *filter = impl_from_IMediaSeeking(iface); + return IMediaStreamFilter_QueryInterface(&filter->IMediaStreamFilter_iface, riid, ret_iface); +} + +static ULONG WINAPI filter_seeking_AddRef(IMediaSeeking *iface) +{ + struct filter *filter = impl_from_IMediaSeeking(iface); + return IMediaStreamFilter_AddRef(&filter->IMediaStreamFilter_iface); +} + +static ULONG WINAPI filter_seeking_Release(IMediaSeeking *iface) +{ + struct filter *filter = impl_from_IMediaSeeking(iface); + return IMediaStreamFilter_Release(&filter->IMediaStreamFilter_iface); +} + +static HRESULT WINAPI filter_seeking_GetCapabilities(IMediaSeeking *iface, DWORD *capabilities) +{ + FIXME("iface %p, capabilities %p, stub!\n", iface, capabilities); + + return E_NOTIMPL; +} + +static HRESULT WINAPI filter_seeking_CheckCapabilities(IMediaSeeking *iface, DWORD *capabilities) +{ + FIXME("iface %p, capabilities %p, stub!\n", iface, capabilities); + + return E_NOTIMPL; +} + +static HRESULT WINAPI filter_seeking_IsFormatSupported(IMediaSeeking *iface, const GUID *format) +{ + FIXME("iface %p, format %s, stub!\n", iface, debugstr_guid(format)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI filter_seeking_QueryPreferredFormat(IMediaSeeking *iface, GUID *format) +{ + FIXME("iface %p, format %p, stub!\n", iface, format); + + return E_NOTIMPL; +} + +static HRESULT WINAPI filter_seeking_GetTimeFormat(IMediaSeeking *iface, GUID *format) +{ + FIXME("iface %p, format %p, stub!\n", iface, format); + + return E_NOTIMPL; +} + +static HRESULT WINAPI filter_seeking_IsUsingTimeFormat(IMediaSeeking *iface, const GUID *format) +{ + FIXME("iface %p, format %s, stub!\n", iface, debugstr_guid(format)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI filter_seeking_SetTimeFormat(IMediaSeeking *iface, const GUID *format) +{ + FIXME("iface %p, format %s, stub!\n", iface, debugstr_guid(format)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI filter_seeking_GetDuration(IMediaSeeking *iface, LONGLONG *duration) +{ + FIXME("iface %p, duration %p, stub!\n", iface, duration); + + return E_NOTIMPL; +} + +static HRESULT WINAPI filter_seeking_GetStopPosition(IMediaSeeking *iface, LONGLONG *stop) +{ + FIXME("iface %p, stop %p, stub!\n", iface, stop); + + return E_NOTIMPL; +} + +static HRESULT WINAPI filter_seeking_GetCurrentPosition(IMediaSeeking *iface, LONGLONG *current) +{ + FIXME("iface %p, current %p, stub!\n", iface, current); + + return E_NOTIMPL; +} + +static HRESULT WINAPI filter_seeking_ConvertTimeFormat(IMediaSeeking *iface, LONGLONG *target, + const GUID *target_format, LONGLONG source, const GUID *source_format) +{ + FIXME("iface %p, target %p, target_format %s, source 0x%s, source_format %s, stub!\n", iface, target, debugstr_guid(target_format), + wine_dbgstr_longlong(source), debugstr_guid(source_format)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI filter_seeking_SetPositions(IMediaSeeking *iface, LONGLONG *current_ptr, DWORD current_flags, + LONGLONG *stop_ptr, DWORD stop_flags) +{ + FIXME("iface %p, current %s, current_flags %#x, stop %s, stop_flags %#x, stub!\n", iface, + current_ptr ? wine_dbgstr_longlong(*current_ptr) : "<null>", current_flags, + stop_ptr ? wine_dbgstr_longlong(*stop_ptr): "<null>", stop_flags); + + return E_NOTIMPL; +} + +static HRESULT WINAPI filter_seeking_GetPositions(IMediaSeeking *iface, LONGLONG *current, LONGLONG *stop) +{ + FIXME("iface %p, current %p, stop %p, stub!\n", iface, current, stop); + + return E_NOTIMPL; +} + +static HRESULT WINAPI filter_seeking_GetAvailable(IMediaSeeking *iface, LONGLONG *earliest, LONGLONG *latest) +{ + FIXME("iface %p, earliest %p, latest %p, stub!\n", iface, earliest, latest); + + return E_NOTIMPL; +} + +static HRESULT WINAPI filter_seeking_SetRate(IMediaSeeking *iface, double rate) +{ + FIXME("iface %p, rate %f, stub!\n", iface, rate); + + return E_NOTIMPL; +} + +static HRESULT WINAPI filter_seeking_GetRate(IMediaSeeking *iface, double *rate) +{ + FIXME("iface %p, rate %p, stub!\n", iface, rate); + + return E_NOTIMPL; +} + +static HRESULT WINAPI filter_seeking_GetPreroll(IMediaSeeking *iface, LONGLONG *preroll) +{ + FIXME("iface %p, preroll %p, stub!\n", iface, preroll); + + return E_NOTIMPL; +} + +static const IMediaSeekingVtbl filter_seeking_vtbl = +{ + filter_seeking_QueryInterface, + filter_seeking_AddRef, + filter_seeking_Release, + filter_seeking_GetCapabilities, + filter_seeking_CheckCapabilities, + filter_seeking_IsFormatSupported, + filter_seeking_QueryPreferredFormat, + filter_seeking_GetTimeFormat, + filter_seeking_IsUsingTimeFormat, + filter_seeking_SetTimeFormat, + filter_seeking_GetDuration, + filter_seeking_GetStopPosition, + filter_seeking_GetCurrentPosition, + filter_seeking_ConvertTimeFormat, + filter_seeking_SetPositions, + filter_seeking_GetPositions, + filter_seeking_GetAvailable, + filter_seeking_SetRate, + filter_seeking_GetRate, + filter_seeking_GetPreroll, +}; + HRESULT filter_create(IUnknown *outer, void **out) { struct filter *object; @@ -627,6 +867,7 @@ HRESULT filter_create(IUnknown *outer, void **out) return E_OUTOFMEMORY;
object->IMediaStreamFilter_iface.lpVtbl = &filter_vtbl; + object->IMediaSeeking_iface.lpVtbl = &filter_seeking_vtbl; object->refcount = 1; InitializeCriticalSection(&object->cs); object->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": MediaStreamFilter.cs"); diff --git a/dlls/amstream/tests/amstream.c b/dlls/amstream/tests/amstream.c index aa32c266c8..7beb6283e2 100644 --- a/dlls/amstream/tests/amstream.c +++ b/dlls/amstream/tests/amstream.c @@ -2348,6 +2348,8 @@ struct testfilter { struct strmbase_filter filter; struct strmbase_source source; + IMediaSeeking IMediaSeeking_iface; + HRESULT get_duration_hr; };
static inline struct testfilter *impl_from_BaseFilter(struct strmbase_filter *iface) @@ -2394,6 +2396,25 @@ static const struct strmbase_filter_ops testfilter_ops = .filter_cleanup_stream = testfilter_cleanup_stream, };
+static inline struct testfilter *impl_from_base_pin(struct strmbase_pin *iface) +{ + return CONTAINING_RECORD(iface, struct testfilter, source.pin); +} + +static HRESULT testsource_query_interface(struct strmbase_pin *iface, REFIID iid, void **out) +{ + struct testfilter *filter = impl_from_base_pin(iface); + + if (IsEqualGUID(iid, &IID_IMediaSeeking) && filter->IMediaSeeking_iface.lpVtbl) + *out = &filter->IMediaSeeking_iface; + else + return E_NOINTERFACE; + + IUnknown_AddRef((IUnknown *)*out); + + return S_OK; +} + static HRESULT WINAPI testsource_DecideBufferSize(struct strmbase_source *iface, IMemAllocator *alloc, ALLOCATOR_PROPERTIES *requested) { @@ -2413,6 +2434,7 @@ static HRESULT WINAPI testsource_DecideBufferSize(struct strmbase_source *iface,
static const struct strmbase_source_ops testsource_ops = { + .base.pin_query_interface = testsource_query_interface, .pfnAttemptConnection = BaseOutputPinImpl_AttemptConnection, .pfnDecideBufferSize = testsource_DecideBufferSize, .pfnDecideAllocator = BaseOutputPinImpl_DecideAllocator, @@ -2421,10 +2443,166 @@ static const struct strmbase_source_ops testsource_ops = static void testfilter_init(struct testfilter *filter) { static const GUID clsid = {0xabacab}; + memset(filter, 0, sizeof(*filter)); strmbase_filter_init(&filter->filter, NULL, &clsid, &testfilter_ops); strmbase_source_init(&filter->source, &filter->filter, L"", &testsource_ops); }
+static inline struct testfilter *impl_from_IMediaSeeking(IMediaSeeking *iface) +{ + return CONTAINING_RECORD(iface, struct testfilter, IMediaSeeking_iface); +} + +static HRESULT WINAPI testsource_seeking_QueryInterface(IMediaSeeking *iface, REFIID iid, void **out) +{ + struct testfilter *filter = impl_from_IMediaSeeking(iface); + return IBaseFilter_QueryInterface(&filter->filter.IBaseFilter_iface, iid, out); +} + +static ULONG WINAPI testsource_seeking_AddRef(IMediaSeeking *iface) +{ + struct testfilter *filter = impl_from_IMediaSeeking(iface); + return IBaseFilter_AddRef(&filter->filter.IBaseFilter_iface); +} + +static ULONG WINAPI testsource_seeking_Release(IMediaSeeking *iface) +{ + struct testfilter *filter = impl_from_IMediaSeeking(iface); + return IBaseFilter_Release(&filter->filter.IBaseFilter_iface); +} + +static HRESULT WINAPI testsource_seeking_GetCapabilities(IMediaSeeking *iface, DWORD *capabilities) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testsource_seeking_CheckCapabilities(IMediaSeeking *iface, DWORD *capabilities) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testsource_seeking_IsFormatSupported(IMediaSeeking *iface, const GUID *format) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testsource_seeking_QueryPreferredFormat(IMediaSeeking *iface, GUID *format) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testsource_seeking_GetTimeFormat(IMediaSeeking *iface, GUID *format) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testsource_seeking_IsUsingTimeFormat(IMediaSeeking *iface, const GUID *format) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testsource_seeking_SetTimeFormat(IMediaSeeking *iface, const GUID *format) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testsource_seeking_GetDuration(IMediaSeeking *iface, LONGLONG *duration) +{ + struct testfilter *filter = impl_from_IMediaSeeking(iface); + + if (SUCCEEDED(filter->get_duration_hr)) + *duration = 0x8000000000000000ULL; + + return filter->get_duration_hr; +} + +static HRESULT WINAPI testsource_seeking_GetStopPosition(IMediaSeeking *iface, LONGLONG *stop) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testsource_seeking_GetCurrentPosition(IMediaSeeking *iface, LONGLONG *current) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testsource_seeking_ConvertTimeFormat(IMediaSeeking *iface, LONGLONG *target, + const GUID *target_format, LONGLONG source, const GUID *source_format) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testsource_seeking_SetPositions(IMediaSeeking *iface, LONGLONG *current_ptr, DWORD current_flags, + LONGLONG *stop_ptr, DWORD stop_flags) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testsource_seeking_GetPositions(IMediaSeeking *iface, LONGLONG *current, LONGLONG *stop) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testsource_seeking_GetAvailable(IMediaSeeking *iface, LONGLONG *earliest, LONGLONG *latest) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testsource_seeking_SetRate(IMediaSeeking *iface, double rate) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testsource_seeking_GetRate(IMediaSeeking *iface, double *rate) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI testsource_seeking_GetPreroll(IMediaSeeking *iface, LONGLONG *preroll) +{ + ok(0, "Unexpected call.\n"); + return E_NOTIMPL; +} + +static const IMediaSeekingVtbl testsource_seeking_vtbl = +{ + testsource_seeking_QueryInterface, + testsource_seeking_AddRef, + testsource_seeking_Release, + testsource_seeking_GetCapabilities, + testsource_seeking_CheckCapabilities, + testsource_seeking_IsFormatSupported, + testsource_seeking_QueryPreferredFormat, + testsource_seeking_GetTimeFormat, + testsource_seeking_IsUsingTimeFormat, + testsource_seeking_SetTimeFormat, + testsource_seeking_GetDuration, + testsource_seeking_GetStopPosition, + testsource_seeking_GetCurrentPosition, + testsource_seeking_ConvertTimeFormat, + testsource_seeking_SetPositions, + testsource_seeking_GetPositions, + testsource_seeking_GetAvailable, + testsource_seeking_SetRate, + testsource_seeking_GetRate, + testsource_seeking_GetPreroll, +}; + static void test_audiostream_get_format(void) { static const WAVEFORMATEX pin_format = @@ -3902,6 +4080,150 @@ void test_mediastreamfilter_stop_pause_run(void) check_mediastreamfilter_state(State_Running, mediastreamfilter_run, mediastreamfilter_stop); }
+static void test_mediastreamfilter_support_seeking(void) +{ + static const WAVEFORMATEX format = + { + .wFormatTag = WAVE_FORMAT_PCM, + .nChannels = 1, + .nSamplesPerSec = 11025, + .wBitsPerSample = 16, + .nBlockAlign = 2, + .nAvgBytesPerSec = 2 * 11025, + }; + + const AM_MEDIA_TYPE mt = + { + .majortype = MEDIATYPE_Audio, + .subtype = MEDIASUBTYPE_PCM, + .formattype = FORMAT_WaveFormatEx, + .cbFormat = sizeof(WAVEFORMATEX), + .pbFormat = (BYTE *)&format, + }; + + IAMMultiMediaStream *mmstream = create_ammultimediastream(); + MSPID mspid1 = { 0x88888888, 1 }; + MSPID mspid2 = { 0x88888888, 2 }; + MSPID mspid3 = { 0x88888888, 3 }; + IMediaStreamFilter *filter; + struct testfilter source1; + struct testfilter source2; + struct testfilter source3; + IAMMediaStream *stream1; + IAMMediaStream *stream2; + IAMMediaStream *stream3; + IGraphBuilder *graph; + ULONG seeking_ref; + IPin *pin1; + IPin *pin2; + IPin *pin3; + HRESULT hr; + ULONG ref; + + hr = IAMMultiMediaStream_Initialize(mmstream, STREAMTYPE_READ, 0, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = CoCreateInstance(&CLSID_AMAudioStream, NULL, CLSCTX_INPROC_SERVER, &IID_IAMMediaStream, (void **)&stream1); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = CoCreateInstance(&CLSID_AMAudioStream, NULL, CLSCTX_INPROC_SERVER, &IID_IAMMediaStream, (void **)&stream2); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = CoCreateInstance(&CLSID_AMAudioStream, NULL, CLSCTX_INPROC_SERVER, &IID_IAMMediaStream, (void **)&stream3); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMediaStream_Initialize(stream1, NULL, 0, &mspid1, STREAMTYPE_READ); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMediaStream_Initialize(stream2, NULL, 0, &mspid2, STREAMTYPE_READ); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMediaStream_Initialize(stream3, NULL, 0, &mspid3, STREAMTYPE_READ); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMultiMediaStream_AddMediaStream(mmstream, (IUnknown *)stream1, &mspid1, 0, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMultiMediaStream_AddMediaStream(mmstream, (IUnknown *)stream2, &mspid2, 0, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMultiMediaStream_AddMediaStream(mmstream, (IUnknown *)stream3, &mspid3, 0, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMediaStream_QueryInterface(stream1, &IID_IPin, (void **)&pin1); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMediaStream_QueryInterface(stream2, &IID_IPin, (void **)&pin2); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMediaStream_QueryInterface(stream3, &IID_IPin, (void **)&pin3); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMultiMediaStream_GetFilter(mmstream, &filter); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMultiMediaStream_GetFilterGraph(mmstream, &graph); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(graph != NULL, "Expected non-NULL graph.\n"); + testfilter_init(&source1); + testfilter_init(&source2); + testfilter_init(&source3); + source2.IMediaSeeking_iface.lpVtbl = &testsource_seeking_vtbl; + source3.IMediaSeeking_iface.lpVtbl = &testsource_seeking_vtbl; + hr = IGraphBuilder_AddFilter(graph, &source1.filter.IBaseFilter_iface, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IGraphBuilder_AddFilter(graph, &source2.filter.IBaseFilter_iface, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IGraphBuilder_AddFilter(graph, &source3.filter.IBaseFilter_iface, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IMediaStreamFilter_SupportSeeking(filter, TRUE); + ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr); + + hr = IGraphBuilder_ConnectDirect(graph, &source1.source.pin.IPin_iface, pin1, &mt); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + source2.get_duration_hr = E_FAIL; + + hr = IMediaStreamFilter_SupportSeeking(filter, TRUE); + ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr); + + hr = IGraphBuilder_ConnectDirect(graph, &source2.source.pin.IPin_iface, pin2, &mt); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IMediaStreamFilter_SupportSeeking(filter, TRUE); + ok(hr == E_NOINTERFACE, "Got hr %#x.\n", hr); + + hr = IGraphBuilder_ConnectDirect(graph, &source3.source.pin.IPin_iface, pin3, &mt); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + check_interface(filter, &IID_IMediaSeeking, FALSE); + + seeking_ref = get_refcount(&source3.IMediaSeeking_iface); + + hr = IMediaStreamFilter_SupportSeeking(filter, TRUE); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + check_interface(filter, &IID_IMediaSeeking, TRUE); + + ref = get_refcount(&source3.IMediaSeeking_iface); + ok(ref == seeking_ref, "Expected outstanding refcount %d, got %d.\n", seeking_ref, ref); + + hr = IMediaStreamFilter_SupportSeeking(filter, TRUE); + ok(hr == HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED), "Got hr %#x.\n", hr); + + IGraphBuilder_Disconnect(graph, pin1); + IGraphBuilder_Disconnect(graph, &source1.source.pin.IPin_iface); + + IGraphBuilder_Disconnect(graph, pin2); + IGraphBuilder_Disconnect(graph, &source2.source.pin.IPin_iface); + + IGraphBuilder_Disconnect(graph, pin3); + IGraphBuilder_Disconnect(graph, &source3.source.pin.IPin_iface); + + ref = IAMMultiMediaStream_Release(mmstream); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ref = IGraphBuilder_Release(graph); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ref = IMediaStreamFilter_Release(filter); + ok(!ref, "Got outstanding refcount %d.\n", ref); + IPin_Release(pin1); + ref = IAMMediaStream_Release(stream1); + ok(!ref, "Got outstanding refcount %d.\n", ref); + IPin_Release(pin2); + ref = IAMMediaStream_Release(stream2); + ok(!ref, "Got outstanding refcount %d.\n", ref); + IPin_Release(pin3); + ref = IAMMediaStream_Release(stream3); + ok(!ref, "Got outstanding refcount %d.\n", ref); +} + START_TEST(amstream) { HANDLE file; @@ -3953,6 +4275,7 @@ START_TEST(amstream)
test_mediastreamfilter_get_state(); test_mediastreamfilter_stop_pause_run(); + test_mediastreamfilter_support_seeking();
CoUninitialize(); }
On 6/1/20 11:13 PM, Anton Baskanov wrote:
Signed-off-by: Anton Baskanov baskanov@gmail.com
dlls/amstream/filter.c | 249 ++++++++++++++++++++++++- dlls/amstream/tests/amstream.c | 323 +++++++++++++++++++++++++++++++++ 2 files changed, 568 insertions(+), 4 deletions(-)
diff --git a/dlls/amstream/filter.c b/dlls/amstream/filter.c index 4b5e184f04..643cfcdbe4 100644 --- a/dlls/amstream/filter.c +++ b/dlls/amstream/filter.c @@ -163,6 +163,7 @@ static const IEnumPinsVtbl enum_pins_vtbl = struct filter { IMediaStreamFilter IMediaStreamFilter_iface;
- IMediaSeeking IMediaSeeking_iface; LONG refcount; CRITICAL_SECTION cs;
@@ -171,6 +172,7 @@ struct filter IFilterGraph *graph; ULONG nb_streams; IAMMediaStream **streams;
- IAMMediaStream *seekable_stream; FILTER_STATE state;
};
@@ -181,6 +183,8 @@ static inline struct filter *impl_from_IMediaStreamFilter(IMediaStreamFilter *if
static HRESULT WINAPI filter_QueryInterface(IMediaStreamFilter *iface, REFIID riid, void **ret_iface) {
struct filter *filter = impl_from_IMediaStreamFilter(iface);
TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ret_iface);
*ret_iface = NULL;
@@ -191,10 +195,12 @@ static HRESULT WINAPI filter_QueryInterface(IMediaStreamFilter *iface, REFIID ri IsEqualIID(riid, &IID_IBaseFilter) || IsEqualIID(riid, &IID_IMediaStreamFilter)) *ret_iface = iface;
else if (IsEqualIID(riid, &IID_IMediaSeeking) && filter->seekable_stream)
*ret_iface = &filter->IMediaSeeking_iface;
if (*ret_iface) {
IMediaStreamFilter_AddRef(*ret_iface);
}IUnknown_AddRef((IUnknown *)*ret_iface); return S_OK;
@@ -544,11 +550,74 @@ static HRESULT WINAPI filter_EnumMediaStreams(IMediaStreamFilter *iface, LONG in return S_OK; }
-static HRESULT WINAPI filter_SupportSeeking(IMediaStreamFilter *iface, BOOL bRenderer) +static IMediaSeeking *get_seeking(IAMMediaStream *stream) {
- FIXME("(%p)->(%d): Stub!\n", iface, bRenderer);
- IPin *pin;
- IPin *peer;
- IMediaSeeking *seeking;
- return E_NOTIMPL;
- if (FAILED(IAMMediaStream_QueryInterface(stream, &IID_IPin, (void **)&pin)))
- {
WARN("Stream %p does not support IPin.\n", stream);
return NULL;
- }
- if (FAILED(IPin_ConnectedTo(pin, &peer)))
- {
IPin_Release(pin);
return NULL;
- }
- if (FAILED(IPin_QueryInterface(peer, &IID_IMediaSeeking, (void **)&seeking)))
- {
IPin_Release(peer);
IPin_Release(pin);
return NULL;
- }
- IPin_Release(peer);
- IPin_Release(pin);
- return seeking;
+}
+static HRESULT WINAPI filter_SupportSeeking(IMediaStreamFilter *iface, BOOL renderer) +{
- struct filter *filter = impl_from_IMediaStreamFilter(iface);
- unsigned int i;
- HRESULT hr = E_NOINTERFACE;
- TRACE("filter %p, renderer %d\n", iface, renderer);
Without tests to show whether/how the "renderer" value makes a difference, I think we should print a FIXME if it's FALSE.
- EnterCriticalSection(&filter->cs);
- if (filter->seekable_stream)
return HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED);
Missing LeaveCriticalSection().
- for (i = 0; i < filter->nb_streams; ++i)
- {
IMediaSeeking *seeking = get_seeking(filter->streams[i]);
LONGLONG duration;
if (!seeking)
continue;
if (FAILED(IMediaSeeking_GetDuration(seeking, &duration)))
{
IMediaSeeking_Release(seeking);
continue;
}
filter->seekable_stream = filter->streams[i];
hr = S_OK;
IMediaSeeking_Release(seeking);
Is it correct that only one stream is chosen for seeking, even if there are multiple available? Consider e.g. the quartz filter graph, which seeks all available streams.
For that matter, should we be delegating to the filter graph instead of seeking the pins directly?
One thing in particular to test is whether filters are paused when seeked. Even if we don't delegate to the filter graph, we likely want to do this anyway.
Another thing to test is whether GetCurrentPosition() returns something reflecting the reference clock time, or the last sample time, or whether it fails if no exposed streams support GetCurrentPosition().
On 6/2/20 10:54 AM, Zebediah Figura wrote:
On 6/1/20 11:13 PM, Anton Baskanov wrote:
Signed-off-by: Anton Baskanov baskanov@gmail.com
dlls/amstream/filter.c | 249 ++++++++++++++++++++++++- dlls/amstream/tests/amstream.c | 323 +++++++++++++++++++++++++++++++++ 2 files changed, 568 insertions(+), 4 deletions(-)
diff --git a/dlls/amstream/filter.c b/dlls/amstream/filter.c index 4b5e184f04..643cfcdbe4 100644 --- a/dlls/amstream/filter.c +++ b/dlls/amstream/filter.c @@ -163,6 +163,7 @@ static const IEnumPinsVtbl enum_pins_vtbl = struct filter { IMediaStreamFilter IMediaStreamFilter_iface;
- IMediaSeeking IMediaSeeking_iface; LONG refcount; CRITICAL_SECTION cs;
@@ -171,6 +172,7 @@ struct filter IFilterGraph *graph; ULONG nb_streams; IAMMediaStream **streams;
- IAMMediaStream *seekable_stream; FILTER_STATE state;
};
@@ -181,6 +183,8 @@ static inline struct filter *impl_from_IMediaStreamFilter(IMediaStreamFilter *if
static HRESULT WINAPI filter_QueryInterface(IMediaStreamFilter *iface, REFIID riid, void **ret_iface) {
struct filter *filter = impl_from_IMediaStreamFilter(iface);
TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ret_iface);
*ret_iface = NULL;
@@ -191,10 +195,12 @@ static HRESULT WINAPI filter_QueryInterface(IMediaStreamFilter *iface, REFIID ri IsEqualIID(riid, &IID_IBaseFilter) || IsEqualIID(riid, &IID_IMediaStreamFilter)) *ret_iface = iface;
else if (IsEqualIID(riid, &IID_IMediaSeeking) && filter->seekable_stream)
*ret_iface = &filter->IMediaSeeking_iface;
if (*ret_iface) {
IMediaStreamFilter_AddRef(*ret_iface);
}IUnknown_AddRef((IUnknown *)*ret_iface); return S_OK;
@@ -544,11 +550,74 @@ static HRESULT WINAPI filter_EnumMediaStreams(IMediaStreamFilter *iface, LONG in return S_OK; }
-static HRESULT WINAPI filter_SupportSeeking(IMediaStreamFilter *iface, BOOL bRenderer) +static IMediaSeeking *get_seeking(IAMMediaStream *stream) {
- FIXME("(%p)->(%d): Stub!\n", iface, bRenderer);
- IPin *pin;
- IPin *peer;
- IMediaSeeking *seeking;
- return E_NOTIMPL;
- if (FAILED(IAMMediaStream_QueryInterface(stream, &IID_IPin, (void **)&pin)))
- {
WARN("Stream %p does not support IPin.\n", stream);
return NULL;
- }
- if (FAILED(IPin_ConnectedTo(pin, &peer)))
- {
IPin_Release(pin);
return NULL;
- }
- if (FAILED(IPin_QueryInterface(peer, &IID_IMediaSeeking, (void **)&seeking)))
- {
IPin_Release(peer);
IPin_Release(pin);
return NULL;
- }
- IPin_Release(peer);
- IPin_Release(pin);
- return seeking;
+}
+static HRESULT WINAPI filter_SupportSeeking(IMediaStreamFilter *iface, BOOL renderer) +{
- struct filter *filter = impl_from_IMediaStreamFilter(iface);
- unsigned int i;
- HRESULT hr = E_NOINTERFACE;
- TRACE("filter %p, renderer %d\n", iface, renderer);
Without tests to show whether/how the "renderer" value makes a difference, I think we should print a FIXME if it's FALSE.
- EnterCriticalSection(&filter->cs);
- if (filter->seekable_stream)
return HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED);
Missing LeaveCriticalSection().
- for (i = 0; i < filter->nb_streams; ++i)
- {
IMediaSeeking *seeking = get_seeking(filter->streams[i]);
LONGLONG duration;
if (!seeking)
continue;
if (FAILED(IMediaSeeking_GetDuration(seeking, &duration)))
{
IMediaSeeking_Release(seeking);
continue;
}
filter->seekable_stream = filter->streams[i];
hr = S_OK;
IMediaSeeking_Release(seeking);
Is it correct that only one stream is chosen for seeking, even if there are multiple available? Consider e.g. the quartz filter graph, which seeks all available streams.
For that matter, should we be delegating to the filter graph instead of seeking the pins directly?
One thing in particular to test is whether filters are paused when seeked. Even if we don't delegate to the filter graph, we likely want to do this anyway.
Another thing to test is whether GetCurrentPosition() returns something reflecting the reference clock time, or the last sample time, or whether it fails if no exposed streams support GetCurrentPosition().
Sorry, on reflection, I realize this doesn't make sense, because we *are* the sink filter. multimedia stream ≠ stream filter...
I think the question about seeking multiple streams still stands, though.
On Tuesday, 2 June 2020 23:04:00 +07 you wrote:
On 6/2/20 10:54 AM, Zebediah Figura wrote:
On 6/1/20 11:13 PM, Anton Baskanov wrote:
Signed-off-by: Anton Baskanov baskanov@gmail.com
dlls/amstream/filter.c | 249 ++++++++++++++++++++++++- dlls/amstream/tests/amstream.c | 323 +++++++++++++++++++++++++++++++++ 2 files changed, 568 insertions(+), 4 deletions(-)
diff --git a/dlls/amstream/filter.c b/dlls/amstream/filter.c index 4b5e184f04..643cfcdbe4 100644 --- a/dlls/amstream/filter.c +++ b/dlls/amstream/filter.c @@ -163,6 +163,7 @@ static const IEnumPinsVtbl enum_pins_vtbl =
struct filter {
IMediaStreamFilter IMediaStreamFilter_iface;
IMediaSeeking IMediaSeeking_iface;
LONG refcount; CRITICAL_SECTION cs;
@@ -171,6 +172,7 @@ struct filter
IFilterGraph *graph; ULONG nb_streams; IAMMediaStream **streams;
IAMMediaStream *seekable_stream;
FILTER_STATE state;
};
@@ -181,6 +183,8 @@ static inline struct filter *impl_from_IMediaStreamFilter(IMediaStreamFilter *if>> static HRESULT WINAPI filter_QueryInterface(IMediaStreamFilter *iface, REFIID riid, void **ret_iface) {
struct filter *filter = impl_from_IMediaStreamFilter(iface);
TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ret_iface);
*ret_iface = NULL;
@@ -191,10 +195,12 @@ static HRESULT WINAPI filter_QueryInterface(IMediaStreamFilter *iface, REFIID ri>> IsEqualIID(riid, &IID_IBaseFilter) || IsEqualIID(riid, &IID_IMediaStreamFilter)) *ret_iface = iface;
- else if (IsEqualIID(riid, &IID_IMediaSeeking) &&
filter->seekable_stream) + *ret_iface = &filter->IMediaSeeking_iface;
if (*ret_iface) {
IMediaStreamFilter_AddRef(*ret_iface);
IUnknown_AddRef((IUnknown *)*ret_iface); return S_OK;
}
@@ -544,11 +550,74 @@ static HRESULT WINAPI filter_EnumMediaStreams(IMediaStreamFilter *iface, LONG in>> return S_OK;
}
-static HRESULT WINAPI filter_SupportSeeking(IMediaStreamFilter *iface, BOOL bRenderer) +static IMediaSeeking *get_seeking(IAMMediaStream *stream)
{
- FIXME("(%p)->(%d): Stub!\n", iface, bRenderer);
- IPin *pin;
- IPin *peer;
- IMediaSeeking *seeking;
- return E_NOTIMPL;
- if (FAILED(IAMMediaStream_QueryInterface(stream, &IID_IPin, (void
**)&pin))) + {
WARN("Stream %p does not support IPin.\n", stream);
return NULL;
- }
- if (FAILED(IPin_ConnectedTo(pin, &peer)))
- {
IPin_Release(pin);
return NULL;
- }
- if (FAILED(IPin_QueryInterface(peer, &IID_IMediaSeeking, (void
**)&seeking))) + {
IPin_Release(peer);
IPin_Release(pin);
return NULL;
- }
- IPin_Release(peer);
- IPin_Release(pin);
- return seeking;
+}
+static HRESULT WINAPI filter_SupportSeeking(IMediaStreamFilter *iface, BOOL renderer) +{
- struct filter *filter = impl_from_IMediaStreamFilter(iface);
- unsigned int i;
- HRESULT hr = E_NOINTERFACE;
- TRACE("filter %p, renderer %d\n", iface, renderer);
Without tests to show whether/how the "renderer" value makes a difference, I think we should print a FIXME if it's FALSE.
- EnterCriticalSection(&filter->cs);
- if (filter->seekable_stream)
return HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED);
Missing LeaveCriticalSection().
- for (i = 0; i < filter->nb_streams; ++i)
- {
IMediaSeeking *seeking = get_seeking(filter->streams[i]);
LONGLONG duration;
if (!seeking)
continue;
if (FAILED(IMediaSeeking_GetDuration(seeking, &duration)))
{
IMediaSeeking_Release(seeking);
continue;
}
filter->seekable_stream = filter->streams[i];
hr = S_OK;
IMediaSeeking_Release(seeking);
Is it correct that only one stream is chosen for seeking, even if there are multiple available? Consider e.g. the quartz filter graph, which seeks all available streams.
For that matter, should we be delegating to the filter graph instead of seeking the pins directly?
One thing in particular to test is whether filters are paused when seeked. Even if we don't delegate to the filter graph, we likely want to do this anyway.
Another thing to test is whether GetCurrentPosition() returns something reflecting the reference clock time, or the last sample time, or whether it fails if no exposed streams support GetCurrentPosition().
Sorry, on reflection, I realize this doesn't make sense, because we *are* the sink filter. multimedia stream â stream filter...
I think the question about seeking multiple streams still stands, though.
::SetPosition tests show that only one stream is used for seeking and that ::SupportSeeking determines which one will be used. Probably it's assumed that all the streams come from the same splitter filter.
On 6/3/20 11:36 AM, Anton Baskanov wrote:
On Tuesday, 2 June 2020 23:04:00 +07 you wrote:
On 6/2/20 10:54 AM, Zebediah Figura wrote:
On 6/1/20 11:13 PM, Anton Baskanov wrote:
Signed-off-by: Anton Baskanov baskanov@gmail.com
dlls/amstream/filter.c | 249 ++++++++++++++++++++++++- dlls/amstream/tests/amstream.c | 323 +++++++++++++++++++++++++++++++++ 2 files changed, 568 insertions(+), 4 deletions(-)
diff --git a/dlls/amstream/filter.c b/dlls/amstream/filter.c index 4b5e184f04..643cfcdbe4 100644 --- a/dlls/amstream/filter.c +++ b/dlls/amstream/filter.c @@ -163,6 +163,7 @@ static const IEnumPinsVtbl enum_pins_vtbl =
struct filter {
IMediaStreamFilter IMediaStreamFilter_iface;
IMediaSeeking IMediaSeeking_iface;
LONG refcount; CRITICAL_SECTION cs;
@@ -171,6 +172,7 @@ struct filter
IFilterGraph *graph; ULONG nb_streams; IAMMediaStream **streams;
IAMMediaStream *seekable_stream;
FILTER_STATE state;
};
@@ -181,6 +183,8 @@ static inline struct filter *impl_from_IMediaStreamFilter(IMediaStreamFilter *if>> static HRESULT WINAPI filter_QueryInterface(IMediaStreamFilter *iface, REFIID riid, void **ret_iface) {
struct filter *filter = impl_from_IMediaStreamFilter(iface);
TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ret_iface); *ret_iface = NULL;
@@ -191,10 +195,12 @@ static HRESULT WINAPI filter_QueryInterface(IMediaStreamFilter *iface, REFIID ri>> IsEqualIID(riid, &IID_IBaseFilter) || IsEqualIID(riid, &IID_IMediaStreamFilter)) *ret_iface = iface;
- else if (IsEqualIID(riid, &IID_IMediaSeeking) &&
filter->seekable_stream) + *ret_iface = &filter->IMediaSeeking_iface;
if (*ret_iface) {
IMediaStreamFilter_AddRef(*ret_iface);
IUnknown_AddRef((IUnknown *)*ret_iface); return S_OK; }
@@ -544,11 +550,74 @@ static HRESULT WINAPI filter_EnumMediaStreams(IMediaStreamFilter *iface, LONG in>> return S_OK;
}
-static HRESULT WINAPI filter_SupportSeeking(IMediaStreamFilter *iface, BOOL bRenderer) +static IMediaSeeking *get_seeking(IAMMediaStream *stream)
{
- FIXME("(%p)->(%d): Stub!\n", iface, bRenderer);
- IPin *pin;
- IPin *peer;
- IMediaSeeking *seeking;
- return E_NOTIMPL;
- if (FAILED(IAMMediaStream_QueryInterface(stream, &IID_IPin, (void
**)&pin))) + {
WARN("Stream %p does not support IPin.\n", stream);
return NULL;
- }
- if (FAILED(IPin_ConnectedTo(pin, &peer)))
- {
IPin_Release(pin);
return NULL;
- }
- if (FAILED(IPin_QueryInterface(peer, &IID_IMediaSeeking, (void
**)&seeking))) + {
IPin_Release(peer);
IPin_Release(pin);
return NULL;
- }
- IPin_Release(peer);
- IPin_Release(pin);
- return seeking;
+}
+static HRESULT WINAPI filter_SupportSeeking(IMediaStreamFilter *iface, BOOL renderer) +{
- struct filter *filter = impl_from_IMediaStreamFilter(iface);
- unsigned int i;
- HRESULT hr = E_NOINTERFACE;
- TRACE("filter %p, renderer %d\n", iface, renderer);
Without tests to show whether/how the "renderer" value makes a difference, I think we should print a FIXME if it's FALSE.
- EnterCriticalSection(&filter->cs);
- if (filter->seekable_stream)
return HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED);
Missing LeaveCriticalSection().
- for (i = 0; i < filter->nb_streams; ++i)
- {
IMediaSeeking *seeking = get_seeking(filter->streams[i]);
LONGLONG duration;
if (!seeking)
continue;
if (FAILED(IMediaSeeking_GetDuration(seeking, &duration)))
{
IMediaSeeking_Release(seeking);
continue;
}
filter->seekable_stream = filter->streams[i];
hr = S_OK;
IMediaSeeking_Release(seeking);
Is it correct that only one stream is chosen for seeking, even if there are multiple available? Consider e.g. the quartz filter graph, which seeks all available streams.
For that matter, should we be delegating to the filter graph instead of seeking the pins directly?
One thing in particular to test is whether filters are paused when seeked. Even if we don't delegate to the filter graph, we likely want to do this anyway.
Another thing to test is whether GetCurrentPosition() returns something reflecting the reference clock time, or the last sample time, or whether it fails if no exposed streams support GetCurrentPosition().
Sorry, on reflection, I realize this doesn't make sense, because we *are* the sink filter. multimedia stream ≠stream filter...
I think the question about seeking multiple streams still stands, though.
::SetPosition tests show that only one stream is used for seeking and that ::SupportSeeking determines which one will be used. Probably it's assumed that all the streams come from the same splitter filter.
Ah yes, I see you're right; I clearly didn't read closely enough.
I guess it makes sense anyway, after all, in the case of amstream everything's expected to go through OpenFile anyway.
Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/amstream/filter.c | 24 +++- dlls/amstream/tests/amstream.c | 245 ++++++++++++++++++++++++++++++++- 2 files changed, 265 insertions(+), 4 deletions(-)
diff --git a/dlls/amstream/filter.c b/dlls/amstream/filter.c index 643cfcdbe4..68ee52dfcc 100644 --- a/dlls/amstream/filter.c +++ b/dlls/amstream/filter.c @@ -788,11 +788,31 @@ static HRESULT WINAPI filter_seeking_ConvertTimeFormat(IMediaSeeking *iface, LON static HRESULT WINAPI filter_seeking_SetPositions(IMediaSeeking *iface, LONGLONG *current_ptr, DWORD current_flags, LONGLONG *stop_ptr, DWORD stop_flags) { - FIXME("iface %p, current %s, current_flags %#x, stop %s, stop_flags %#x, stub!\n", iface, + struct filter *filter = impl_from_IMediaSeeking(iface); + IMediaSeeking *seeking; + HRESULT hr; + + TRACE("iface %p, current %s, current_flags %#x, stop %s, stop_flags %#x\n", iface, current_ptr ? wine_dbgstr_longlong(*current_ptr) : "<null>", current_flags, stop_ptr ? wine_dbgstr_longlong(*stop_ptr): "<null>", stop_flags);
- return E_NOTIMPL; + EnterCriticalSection(&filter->cs); + + seeking = get_seeking(filter->seekable_stream); + + if (!seeking) + { + LeaveCriticalSection(&filter->cs); + return E_NOTIMPL; + } + + hr = IMediaSeeking_SetPositions(seeking, current_ptr, current_flags, stop_ptr, stop_flags); + + IMediaSeeking_Release(seeking); + + LeaveCriticalSection(&filter->cs); + + return hr; }
static HRESULT WINAPI filter_seeking_GetPositions(IMediaSeeking *iface, LONGLONG *current, LONGLONG *stop) diff --git a/dlls/amstream/tests/amstream.c b/dlls/amstream/tests/amstream.c index 7beb6283e2..a759a17680 100644 --- a/dlls/amstream/tests/amstream.c +++ b/dlls/amstream/tests/amstream.c @@ -2349,7 +2349,10 @@ struct testfilter struct strmbase_filter filter; struct strmbase_source source; IMediaSeeking IMediaSeeking_iface; + LONGLONG current_position; + LONGLONG stop_position; HRESULT get_duration_hr; + HRESULT set_positions_hr; };
static inline struct testfilter *impl_from_BaseFilter(struct strmbase_filter *iface) @@ -2446,6 +2449,7 @@ static void testfilter_init(struct testfilter *filter) memset(filter, 0, sizeof(*filter)); strmbase_filter_init(&filter->filter, NULL, &clsid, &testfilter_ops); strmbase_source_init(&filter->source, &filter->filter, L"", &testsource_ops); + filter->stop_position = 0x8000000000000000ULL; }
static inline struct testfilter *impl_from_IMediaSeeking(IMediaSeeking *iface) @@ -2545,8 +2549,18 @@ static HRESULT WINAPI testsource_seeking_ConvertTimeFormat(IMediaSeeking *iface, static HRESULT WINAPI testsource_seeking_SetPositions(IMediaSeeking *iface, LONGLONG *current_ptr, DWORD current_flags, LONGLONG *stop_ptr, DWORD stop_flags) { - ok(0, "Unexpected call.\n"); - return E_NOTIMPL; + struct testfilter *filter = impl_from_IMediaSeeking(iface); + + if (SUCCEEDED(filter->set_positions_hr)) + { + if (current_ptr) + filter->current_position = *current_ptr; + + if (stop_ptr) + filter->stop_position = *stop_ptr; + } + + return filter->set_positions_hr; }
static HRESULT WINAPI testsource_seeking_GetPositions(IMediaSeeking *iface, LONGLONG *current, LONGLONG *stop) @@ -4224,6 +4238,232 @@ static void test_mediastreamfilter_support_seeking(void) ok(!ref, "Got outstanding refcount %d.\n", ref); }
+static void test_mediastreamfilter_set_positions(void) +{ + static const WAVEFORMATEX format = + { + .wFormatTag = WAVE_FORMAT_PCM, + .nChannels = 1, + .nSamplesPerSec = 11025, + .wBitsPerSample = 16, + .nBlockAlign = 2, + .nAvgBytesPerSec = 2 * 11025, + }; + + const AM_MEDIA_TYPE mt = + { + .majortype = MEDIATYPE_Audio, + .subtype = MEDIASUBTYPE_PCM, + .formattype = FORMAT_WaveFormatEx, + .cbFormat = sizeof(WAVEFORMATEX), + .pbFormat = (BYTE *)&format, + }; + + IAMMultiMediaStream *mmstream = create_ammultimediastream(); + MSPID mspid1 = { 0x88888888, 1 }; + MSPID mspid2 = { 0x88888888, 2 }; + MSPID mspid3 = { 0x88888888, 3 }; + IMediaStreamFilter *filter; + struct testfilter source1; + struct testfilter source2; + struct testfilter source3; + LONGLONG current_position; + IAMMediaStream *stream1; + IAMMediaStream *stream2; + IAMMediaStream *stream3; + LONGLONG stop_position; + IMediaSeeking *seeking; + IGraphBuilder *graph; + IPin *pin1; + IPin *pin2; + IPin *pin3; + HRESULT hr; + ULONG ref; + + hr = IAMMultiMediaStream_Initialize(mmstream, STREAMTYPE_READ, 0, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = CoCreateInstance(&CLSID_AMAudioStream, NULL, CLSCTX_INPROC_SERVER, &IID_IAMMediaStream, (void **)&stream1); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = CoCreateInstance(&CLSID_AMAudioStream, NULL, CLSCTX_INPROC_SERVER, &IID_IAMMediaStream, (void **)&stream2); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = CoCreateInstance(&CLSID_AMAudioStream, NULL, CLSCTX_INPROC_SERVER, &IID_IAMMediaStream, (void **)&stream3); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMediaStream_Initialize(stream1, NULL, 0, &mspid1, STREAMTYPE_READ); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMediaStream_Initialize(stream2, NULL, 0, &mspid2, STREAMTYPE_READ); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMediaStream_Initialize(stream3, NULL, 0, &mspid3, STREAMTYPE_READ); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMultiMediaStream_AddMediaStream(mmstream, (IUnknown *)stream1, &mspid1, 0, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMultiMediaStream_AddMediaStream(mmstream, (IUnknown *)stream2, &mspid2, 0, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMultiMediaStream_AddMediaStream(mmstream, (IUnknown *)stream3, &mspid3, 0, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMediaStream_QueryInterface(stream1, &IID_IPin, (void **)&pin1); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMediaStream_QueryInterface(stream2, &IID_IPin, (void **)&pin2); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMediaStream_QueryInterface(stream3, &IID_IPin, (void **)&pin3); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMultiMediaStream_GetFilter(mmstream, &filter); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IAMMultiMediaStream_GetFilterGraph(mmstream, &graph); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(graph != NULL, "Expected non-NULL graph.\n"); + testfilter_init(&source1); + testfilter_init(&source2); + testfilter_init(&source3); + source1.IMediaSeeking_iface.lpVtbl = &testsource_seeking_vtbl; + source2.IMediaSeeking_iface.lpVtbl = &testsource_seeking_vtbl; + source3.IMediaSeeking_iface.lpVtbl = &testsource_seeking_vtbl; + hr = IGraphBuilder_AddFilter(graph, &source1.filter.IBaseFilter_iface, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IGraphBuilder_AddFilter(graph, &source2.filter.IBaseFilter_iface, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IGraphBuilder_AddFilter(graph, &source3.filter.IBaseFilter_iface, NULL); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IGraphBuilder_ConnectDirect(graph, &source2.source.pin.IPin_iface, pin2, &mt); + ok(hr == S_OK, "Got hr %#x.\n", hr); + hr = IGraphBuilder_ConnectDirect(graph, &source3.source.pin.IPin_iface, pin3, &mt); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IMediaStreamFilter_SupportSeeking(filter, TRUE); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IGraphBuilder_ConnectDirect(graph, &source1.source.pin.IPin_iface, pin1, &mt); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + hr = IMediaStreamFilter_QueryInterface(filter, &IID_IMediaSeeking, (void **)&seeking); + ok(hr == S_OK, "Got hr %#x.\n", hr); + + current_position = 12345678; + stop_position = 87654321; + source1.current_position = 0xdeadbeefdeadbeefULL; + source1.stop_position = 0xdeadbeefdeadbeefULL; + source2.current_position = 0xdeadbeefdeadbeefULL; + source2.stop_position = 0xdeadbeefdeadbeefULL; + source3.current_position = 0xdeadbeefdeadbeefULL; + source3.stop_position = 0xdeadbeefdeadbeefULL; + hr = IMediaSeeking_SetPositions(seeking, ¤t_position, AM_SEEKING_AbsolutePositioning, + &stop_position, AM_SEEKING_AbsolutePositioning); + ok(hr == S_OK, "Got hr %#x.\n", hr); + ok(source1.current_position == 0xdeadbeefdeadbeefULL, "Got current position %s.\n", + wine_dbgstr_longlong(source1.current_position)); + ok(source1.stop_position == 0xdeadbeefdeadbeefULL, "Got stop position %s.\n", + wine_dbgstr_longlong(source1.stop_position)); + ok(source2.current_position == 12345678, "Got current position %s.\n", + wine_dbgstr_longlong(source2.current_position)); + ok(source2.stop_position == 87654321, "Got stop position %s.\n", + wine_dbgstr_longlong(source2.stop_position)); + ok(source3.current_position == 0xdeadbeefdeadbeefULL, "Got current position %s.\n", + wine_dbgstr_longlong(source3.current_position)); + ok(source3.stop_position == 0xdeadbeefdeadbeefULL, "Got stop position %s.\n", + wine_dbgstr_longlong(source3.stop_position)); + + source2.set_positions_hr = E_FAIL; + source1.current_position = 0xdeadbeefdeadbeefULL; + source1.stop_position = 0xdeadbeefdeadbeefULL; + source3.current_position = 0xdeadbeefdeadbeefULL; + source3.stop_position = 0xdeadbeefdeadbeefULL; + current_position = 12345678; + stop_position = 87654321; + hr = IMediaSeeking_SetPositions(seeking, ¤t_position, AM_SEEKING_AbsolutePositioning, + &stop_position, AM_SEEKING_AbsolutePositioning); + ok(hr == E_FAIL, "Got hr %#x.\n", hr); + ok(source1.current_position == 0xdeadbeefdeadbeefULL, "Got current position %s.\n", + wine_dbgstr_longlong(source1.current_position)); + ok(source1.stop_position == 0xdeadbeefdeadbeefULL, "Got stop position %s.\n", + wine_dbgstr_longlong(source1.stop_position)); + ok(source3.current_position == 0xdeadbeefdeadbeefULL, "Got current position %s.\n", + wine_dbgstr_longlong(source3.current_position)); + ok(source3.stop_position == 0xdeadbeefdeadbeefULL, "Got stop position %s.\n", + wine_dbgstr_longlong(source3.stop_position)); + + source2.set_positions_hr = E_NOTIMPL; + source1.current_position = 0xdeadbeefdeadbeefULL; + source1.stop_position = 0xdeadbeefdeadbeefULL; + source3.current_position = 0xdeadbeefdeadbeefULL; + source3.stop_position = 0xdeadbeefdeadbeefULL; + current_position = 12345678; + stop_position = 87654321; + hr = IMediaSeeking_SetPositions(seeking, ¤t_position, AM_SEEKING_AbsolutePositioning, + &stop_position, AM_SEEKING_AbsolutePositioning); + ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr); + ok(source1.current_position == 0xdeadbeefdeadbeefULL, "Got current position %s.\n", + wine_dbgstr_longlong(source1.current_position)); + ok(source1.stop_position == 0xdeadbeefdeadbeefULL, "Got stop position %s.\n", + wine_dbgstr_longlong(source1.stop_position)); + ok(source3.current_position == 0xdeadbeefdeadbeefULL, "Got current position %s.\n", + wine_dbgstr_longlong(source3.current_position)); + ok(source3.stop_position == 0xdeadbeefdeadbeefULL, "Got stop position %s.\n", + wine_dbgstr_longlong(source3.stop_position)); + + source2.IMediaSeeking_iface.lpVtbl = NULL; + source1.current_position = 0xdeadbeefdeadbeefULL; + source1.stop_position = 0xdeadbeefdeadbeefULL; + source3.current_position = 0xdeadbeefdeadbeefULL; + source3.stop_position = 0xdeadbeefdeadbeefULL; + current_position = 12345678; + stop_position = 87654321; + hr = IMediaSeeking_SetPositions(seeking, ¤t_position, AM_SEEKING_AbsolutePositioning, + &stop_position, AM_SEEKING_AbsolutePositioning); + ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr); + ok(source1.current_position == 0xdeadbeefdeadbeefULL, "Got current position %s.\n", + wine_dbgstr_longlong(source1.current_position)); + ok(source1.stop_position == 0xdeadbeefdeadbeefULL, "Got stop position %s.\n", + wine_dbgstr_longlong(source1.stop_position)); + ok(source3.current_position == 0xdeadbeefdeadbeefULL, "Got current position %s.\n", + wine_dbgstr_longlong(source3.current_position)); + ok(source3.stop_position == 0xdeadbeefdeadbeefULL, "Got stop position %s.\n", + wine_dbgstr_longlong(source3.stop_position)); + + IGraphBuilder_Disconnect(graph, pin2); + IGraphBuilder_Disconnect(graph, &source2.source.pin.IPin_iface); + + source2.IMediaSeeking_iface.lpVtbl = NULL; + source1.current_position = 0xdeadbeefdeadbeefULL; + source1.stop_position = 0xdeadbeefdeadbeefULL; + source3.current_position = 0xdeadbeefdeadbeefULL; + source3.stop_position = 0xdeadbeefdeadbeefULL; + current_position = 12345678; + stop_position = 87654321; + hr = IMediaSeeking_SetPositions(seeking, ¤t_position, AM_SEEKING_AbsolutePositioning, + &stop_position, AM_SEEKING_AbsolutePositioning); + ok(hr == E_NOTIMPL, "Got hr %#x.\n", hr); + ok(source1.current_position == 0xdeadbeefdeadbeefULL, "Got current position %s.\n", + wine_dbgstr_longlong(source1.current_position)); + ok(source1.stop_position == 0xdeadbeefdeadbeefULL, "Got stop position %s.\n", + wine_dbgstr_longlong(source1.stop_position)); + ok(source3.current_position == 0xdeadbeefdeadbeefULL, "Got current position %s.\n", + wine_dbgstr_longlong(source3.current_position)); + ok(source3.stop_position == 0xdeadbeefdeadbeefULL, "Got stop position %s.\n", + wine_dbgstr_longlong(source3.stop_position)); + + IGraphBuilder_Disconnect(graph, pin2); + IGraphBuilder_Disconnect(graph, &source2.source.pin.IPin_iface); + IGraphBuilder_Disconnect(graph, pin3); + IGraphBuilder_Disconnect(graph, &source3.source.pin.IPin_iface); + + ref = IAMMultiMediaStream_Release(mmstream); + ok(!ref, "Got outstanding refcount %d.\n", ref); + ref = IGraphBuilder_Release(graph); + ok(!ref, "Got outstanding refcount %d.\n", ref); + IMediaSeeking_Release(seeking); + ref = IMediaStreamFilter_Release(filter); + ok(!ref, "Got outstanding refcount %d.\n", ref); + IPin_Release(pin1); + ref = IAMMediaStream_Release(stream1); + ok(!ref, "Got outstanding refcount %d.\n", ref); + IPin_Release(pin2); + ref = IAMMediaStream_Release(stream2); + ok(!ref, "Got outstanding refcount %d.\n", ref); + IPin_Release(pin3); + ref = IAMMediaStream_Release(stream3); + ok(!ref, "Got outstanding refcount %d.\n", ref); +} + START_TEST(amstream) { HANDLE file; @@ -4276,6 +4516,7 @@ START_TEST(amstream) test_mediastreamfilter_get_state(); test_mediastreamfilter_stop_pause_run(); test_mediastreamfilter_support_seeking(); + test_mediastreamfilter_set_positions();
CoUninitialize(); }
Signed-off-by: Anton Baskanov baskanov@gmail.com --- dlls/amstream/multimedia.c | 2 ++ dlls/amstream/tests/amstream.c | 4 ++++ 2 files changed, 6 insertions(+)
diff --git a/dlls/amstream/multimedia.c b/dlls/amstream/multimedia.c index e77392e483..e058e639dd 100644 --- a/dlls/amstream/multimedia.c +++ b/dlls/amstream/multimedia.c @@ -442,6 +442,8 @@ static HRESULT WINAPI multimedia_stream_OpenFile(IAMMultiMediaStream *iface, if (SUCCEEDED(ret) && !(flags & AMMSF_NORENDER)) ret = IGraphBuilder_Render(This->graph, This->ipin);
+ IMediaStreamFilter_SupportSeeking(This->filter, This->type == STREAMTYPE_READ); + if (EnumPins) IEnumPins_Release(EnumPins); if (BaseFilter) diff --git a/dlls/amstream/tests/amstream.c b/dlls/amstream/tests/amstream.c index a759a17680..e1c89ac16d 100644 --- a/dlls/amstream/tests/amstream.c +++ b/dlls/amstream/tests/amstream.c @@ -224,9 +224,13 @@ static void test_openfile(void) if (pgraph) IGraphBuilder_Release(pgraph);
+ check_interface(pams, &IID_IMediaSeeking, FALSE); + hr = IAMMultiMediaStream_OpenFile(pams, L"test.avi", 0); ok(hr==S_OK, "IAMMultiMediaStream_OpenFile returned: %x\n", hr);
+ check_interface(pams, &IID_IMediaSeeking, TRUE); + hr = IAMMultiMediaStream_GetFilterGraph(pams, &pgraph); ok(hr==S_OK, "IAMMultiMediaStream_GetFilterGraph returned: %x\n", hr); ok(pgraph!=NULL, "Filtergraph should be created\n");
Hello Anton, these patches look mostly good, just a few nitpicks...
On 6/1/20 11:13 PM, Anton Baskanov wrote:
Signed-off-by: Anton Baskanov baskanov@gmail.com
dlls/amstream/filter.c | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/dlls/amstream/filter.c b/dlls/amstream/filter.c index fb9b1d9aee..4b5e184f04 100644 --- a/dlls/amstream/filter.c +++ b/dlls/amstream/filter.c @@ -364,10 +364,16 @@ static HRESULT WINAPI filter_EnumPins(IMediaStreamFilter *iface, IEnumPins **enu EnterCriticalSection(&filter->cs);
if (!enum_pins)
{
LeaveCriticalSection(&filter->cs); return E_POINTER;
}
if (!(object = heap_alloc(sizeof(*object))))
{
LeaveCriticalSection(&filter->cs); return E_OUTOFMEMORY;
}
This could be simplified a bit by just moving the call to EnterCriticalSection() down; we don't need to protect these calls.
object->IEnumPins_iface.lpVtbl = &enum_pins_vtbl; object->refcount = 1;
@@ -376,6 +382,7 @@ static HRESULT WINAPI filter_EnumPins(IMediaStreamFilter *iface, IEnumPins **enu if (!(object->pins = heap_alloc(filter->nb_streams * sizeof(*object->pins)))) { heap_free(object);
} for (i = 0; i < filter->nb_streams; ++i)LeaveCriticalSection(&filter->cs); return E_OUTOFMEMORY;
Hello Zeb, thanks for your comments, I've sent the updated patches.
On Tuesday, 2 June 2020 22:40:33 +07 you wrote:
Hello Anton, these patches look mostly good, just a few nitpicks...
On 6/1/20 11:13 PM, Anton Baskanov wrote:
Signed-off-by: Anton Baskanov baskanov@gmail.com
dlls/amstream/filter.c | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/dlls/amstream/filter.c b/dlls/amstream/filter.c index fb9b1d9aee..4b5e184f04 100644 --- a/dlls/amstream/filter.c +++ b/dlls/amstream/filter.c @@ -364,10 +364,16 @@ static HRESULT WINAPI filter_EnumPins(IMediaStreamFilter *iface, IEnumPins **enu> EnterCriticalSection(&filter->cs);
if (!enum_pins)
{
LeaveCriticalSection(&filter->cs); return E_POINTER;
}
if (!(object = heap_alloc(sizeof(*object))))
{
LeaveCriticalSection(&filter->cs); return E_OUTOFMEMORY;
}
This could be simplified a bit by just moving the call to EnterCriticalSection() down; we don't need to protect these calls.
object->IEnumPins_iface.lpVtbl = &enum_pins_vtbl; object->refcount = 1;
@@ -376,6 +382,7 @@ static HRESULT WINAPI filter_EnumPins(IMediaStreamFilter *iface, IEnumPins **enu> if (!(object->pins = heap_alloc(filter->nb_streams * sizeof(*object->pins)))) {
heap_free(object);
LeaveCriticalSection(&filter->cs); return E_OUTOFMEMORY;
} for (i = 0; i < filter->nb_streams; ++i)