[PATCH 3/4] quartz/dsoundrender: Reimplement the DirectSound renderer directly on top of the strmbase filter.

Zebediah Figura z.figura12 at gmail.com
Mon Jul 20 10:25:29 CDT 2020


The DirectSound renderer does a lot of things differently. To a large degree
this is to be expected; it's an audio renderer and therefore needs to buffer
much farther in advance. However, it also doesn't participate in quality
management, doesn't block in Receive(), and has a few other mild differences in
behaviour. Weighing the features implemented by the base renderer against the
quirks necessary for the DirectSound renderer leads me to believe that it would
be easier not to use that framework.

Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
---
 dlls/quartz/dsoundrender.c       | 438 +++++++++++++++++++++++--------
 dlls/quartz/tests/dsoundrender.c |   3 +-
 dlls/strmbase/strmbase_private.h |   4 -
 include/wine/strmbase.h          |   4 +
 4 files changed, 333 insertions(+), 116 deletions(-)

diff --git a/dlls/quartz/dsoundrender.c b/dlls/quartz/dsoundrender.c
index fba206ecb60..a3155477fde 100644
--- a/dlls/quartz/dsoundrender.c
+++ b/dlls/quartz/dsoundrender.c
@@ -41,12 +41,25 @@ static const REFERENCE_TIME DSoundRenderer_Max_Fill = 150 * 10000;
 
 struct dsound_render
 {
-    struct strmbase_renderer renderer;
-
-    IBasicAudio IBasicAudio_iface;
+    struct strmbase_filter filter;
+    struct strmbase_passthrough passthrough;
     IAMDirectSound IAMDirectSound_iface;
+    IBasicAudio IBasicAudio_iface;
+    IQualityControl IQualityControl_iface;
     IUnknown *system_clock;
 
+    struct strmbase_sink sink;
+
+    CRITICAL_SECTION stream_cs;
+    /* Signaled when the filter has completed a state change. The filter waits
+     * for this event in IBaseFilter::GetState(). */
+    HANDLE state_event;
+    /* Signaled when a flush or state change occurs, i.e. anything that needs
+     * to immediately unblock the streaming thread. */
+    HANDLE flush_event;
+    REFERENCE_TIME stream_start;
+    BOOL eos;
+
     IDirectSound8 *dsound;
     LPDIRECTSOUNDBUFFER dsbuffer;
     DWORD buf_size;
@@ -59,9 +72,14 @@ struct dsound_render
     LONG pan;
 };
 
-static struct dsound_render *impl_from_strmbase_renderer(struct strmbase_renderer *iface)
+static struct dsound_render *impl_from_strmbase_pin(struct strmbase_pin *iface)
 {
-    return CONTAINING_RECORD(iface, struct dsound_render, renderer);
+    return CONTAINING_RECORD(iface, struct dsound_render, sink.pin);
+}
+
+static struct dsound_render *impl_from_strmbase_filter(struct strmbase_filter *iface)
+{
+    return CONTAINING_RECORD(iface, struct dsound_render, filter);
 }
 
 static struct dsound_render *impl_from_IBasicAudio(IBasicAudio *iface)
@@ -76,7 +94,7 @@ static struct dsound_render *impl_from_IAMDirectSound(IAMDirectSound *iface)
 
 static REFERENCE_TIME time_from_pos(struct dsound_render *This, DWORD pos)
 {
-    WAVEFORMATEX *wfx = (WAVEFORMATEX *)This->renderer.sink.pin.mt.pbFormat;
+    WAVEFORMATEX *wfx = (WAVEFORMATEX *)This->sink.pin.mt.pbFormat;
     REFERENCE_TIME ret = 10000000;
     ret = ret * pos / wfx->nAvgBytesPerSec;
     return ret;
@@ -84,7 +102,7 @@ static REFERENCE_TIME time_from_pos(struct dsound_render *This, DWORD pos)
 
 static DWORD pos_from_time(struct dsound_render *This, REFERENCE_TIME time)
 {
-    WAVEFORMATEX *wfx = (WAVEFORMATEX *)This->renderer.sink.pin.mt.pbFormat;
+    WAVEFORMATEX *wfx = (WAVEFORMATEX *)This->sink.pin.mt.pbFormat;
     REFERENCE_TIME ret = time;
     ret *= wfx->nAvgBytesPerSec;
     ret /= 10000000;
@@ -94,7 +112,7 @@ static DWORD pos_from_time(struct dsound_render *This, REFERENCE_TIME time)
 
 static void DSoundRender_UpdatePositions(struct dsound_render *This, DWORD *seqwritepos, DWORD *minwritepos)
 {
-    WAVEFORMATEX *wfx = (WAVEFORMATEX *)This->renderer.sink.pin.mt.pbFormat;
+    WAVEFORMATEX *wfx = (WAVEFORMATEX *)This->sink.pin.mt.pbFormat;
     BYTE *buf1, *buf2;
     DWORD size1, size2, playpos, writepos, old_writepos, old_playpos, adv;
     BOOL writepos_set = This->writepos < This->buf_size;
@@ -133,17 +151,17 @@ static void DSoundRender_UpdatePositions(struct dsound_render *This, DWORD *seqw
 static HRESULT DSoundRender_GetWritePos(struct dsound_render *This,
         DWORD *ret_writepos, REFERENCE_TIME write_at, DWORD *pfree, DWORD *skip)
 {
-    WAVEFORMATEX *wfx = (WAVEFORMATEX *)This->renderer.sink.pin.mt.pbFormat;
+    WAVEFORMATEX *wfx = (WAVEFORMATEX *)This->sink.pin.mt.pbFormat;
     DWORD writepos, min_writepos, playpos;
     REFERENCE_TIME max_lag = 50 * 10000;
     REFERENCE_TIME cur, writepos_t, delta_t;
 
     DSoundRender_UpdatePositions(This, &writepos, &min_writepos);
     playpos = This->last_playpos;
-    if (This->renderer.filter.clock)
+    if (This->filter.clock)
     {
-        IReferenceClock_GetTime(This->renderer.filter.clock, &cur);
-        cur -= This->renderer.stream_start;
+        IReferenceClock_GetTime(This->filter.clock, &cur);
+        cur -= This->stream_start;
     } else
         write_at = -1;
 
@@ -212,7 +230,7 @@ end:
 
 static HRESULT DSoundRender_HandleEndOfStream(struct dsound_render *This)
 {
-    while (This->renderer.filter.state == State_Running)
+    while (This->filter.state == State_Running)
     {
         DWORD pos1, pos2;
         DSoundRender_UpdatePositions(This, &pos1, &pos2);
@@ -220,9 +238,9 @@ static HRESULT DSoundRender_HandleEndOfStream(struct dsound_render *This)
             break;
 
         This->in_loop = 1;
-        LeaveCriticalSection(&This->renderer.csRenderLock);
-        WaitForSingleObject(This->renderer.flush_event, 10);
-        EnterCriticalSection(&This->renderer.csRenderLock);
+        LeaveCriticalSection(&This->stream_cs);
+        WaitForSingleObject(This->flush_event, 10);
+        EnterCriticalSection(&This->stream_cs);
         This->in_loop = 0;
     }
 
@@ -234,23 +252,23 @@ static HRESULT DSoundRender_SendSampleData(struct dsound_render *This,
 {
     HRESULT hr;
 
-    while (size && This->renderer.filter.state != State_Stopped) {
+    while (size && This->filter.state != State_Stopped) {
         DWORD writepos, skip = 0, free, size1, size2, ret;
         BYTE *buf1, *buf2;
 
-        if (This->renderer.filter.state == State_Running)
+        if (This->filter.state == State_Running)
             hr = DSoundRender_GetWritePos(This, &writepos, tStart, &free, &skip);
         else
             hr = S_FALSE;
 
         if (hr != S_OK) {
             This->in_loop = 1;
-            LeaveCriticalSection(&This->renderer.csRenderLock);
-            ret = WaitForSingleObject(This->renderer.flush_event, 10);
-            EnterCriticalSection(&This->renderer.csRenderLock);
+            LeaveCriticalSection(&This->stream_cs);
+            ret = WaitForSingleObject(This->flush_event, 10);
+            EnterCriticalSection(&This->stream_cs);
             This->in_loop = 0;
-            if (This->renderer.sink.flushing || This->renderer.filter.state == State_Stopped)
-                return This->renderer.filter.state == State_Paused ? S_OK : VFW_E_WRONG_STATE;
+            if (This->sink.flushing || This->filter.state == State_Stopped)
+                return This->filter.state == State_Paused ? S_OK : VFW_E_WRONG_STATE;
             if (ret != WAIT_TIMEOUT)
                 ERR("%x\n", ret);
             continue;
@@ -281,26 +299,20 @@ static HRESULT DSoundRender_SendSampleData(struct dsound_render *This,
     return S_OK;
 }
 
-static HRESULT WINAPI DSoundRender_ShouldDrawSampleNow(struct strmbase_renderer *iface,
-        IMediaSample *sample, REFERENCE_TIME *start, REFERENCE_TIME *end)
-{
-    /* We time ourselves do not use the base renderers timing */
-    return S_OK;
-}
-
-
-static HRESULT WINAPI DSoundRender_PrepareReceive(struct strmbase_renderer *iface, IMediaSample *pSample)
+static HRESULT WINAPI DSoundRender_PrepareReceive(struct dsound_render *This, IMediaSample *pSample)
 {
-    struct dsound_render *This = impl_from_strmbase_renderer(iface);
     HRESULT hr;
     AM_MEDIA_TYPE *amt;
 
     if (IMediaSample_GetMediaType(pSample, &amt) == S_OK)
     {
-        AM_MEDIA_TYPE *orig = &This->renderer.sink.pin.mt;
+        AM_MEDIA_TYPE *orig = &This->sink.pin.mt;
         WAVEFORMATEX *origfmt = (WAVEFORMATEX *)orig->pbFormat;
         WAVEFORMATEX *newfmt = (WAVEFORMATEX *)amt->pbFormat;
 
+        TRACE("Format change.\n");
+        strmbase_dump_media_type(amt);
+
         if (origfmt->wFormatTag == newfmt->wFormatTag &&
             origfmt->nChannels == newfmt->nChannels &&
             origfmt->nBlockAlign == newfmt->nBlockAlign &&
@@ -324,20 +336,13 @@ static HRESULT WINAPI DSoundRender_PrepareReceive(struct strmbase_renderer *ifac
     return S_OK;
 }
 
-static HRESULT WINAPI DSoundRender_DoRenderSample(struct strmbase_renderer *iface, IMediaSample *pSample)
+static HRESULT WINAPI DSoundRender_DoRenderSample(struct dsound_render *This, IMediaSample *pSample)
 {
-    struct dsound_render *This = impl_from_strmbase_renderer(iface);
     LPBYTE pbSrcStream = NULL;
     LONG cbSrcStream = 0;
     REFERENCE_TIME tStart, tStop;
     HRESULT hr;
 
-    TRACE("%p %p\n", iface, pSample);
-
-    /* Slightly incorrect, Pause completes when a frame is received so we should signal
-     * pause completion here, but for sound playing a single frame doesn't make sense
-     */
-
     hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
     if (FAILED(hr))
     {
@@ -363,39 +368,59 @@ static HRESULT WINAPI DSoundRender_DoRenderSample(struct strmbase_renderer *ifac
     return DSoundRender_SendSampleData(This, tStart, tStop, pbSrcStream, cbSrcStream);
 }
 
-static HRESULT WINAPI DSoundRender_CheckMediaType(struct strmbase_renderer *iface, const AM_MEDIA_TYPE * pmt)
+static HRESULT WINAPI dsound_render_sink_Receive(struct strmbase_sink *iface, IMediaSample *sample)
 {
-    if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Audio))
+    struct dsound_render *filter = impl_from_strmbase_pin(&iface->pin);
+    REFERENCE_TIME start, stop;
+    HRESULT hr;
+
+    if (filter->eos || filter->sink.flushing)
         return S_FALSE;
 
-    return S_OK;
+    if (filter->filter.state == State_Stopped)
+        return VFW_E_WRONG_STATE;
+
+    if (FAILED(hr = DSoundRender_PrepareReceive(filter, sample)))
+        return hr;
+
+    EnterCriticalSection(&filter->stream_cs);
+
+    if (filter->filter.clock && SUCCEEDED(IMediaSample_GetTime(sample, &start, &stop)))
+        strmbase_passthrough_update_time(&filter->passthrough, start);
+
+    if (filter->filter.state == State_Paused)
+        SetEvent(filter->state_event);
+
+    hr = DSoundRender_DoRenderSample(filter, sample);
+
+    LeaveCriticalSection(&filter->stream_cs);
+    return hr;
 }
 
-static void dsound_render_stop_stream(struct strmbase_renderer *iface)
+static HRESULT dsound_render_sink_query_interface(struct strmbase_pin *iface, REFIID iid, void **out)
 {
-    struct dsound_render *This = impl_from_strmbase_renderer(iface);
+    struct dsound_render *filter = impl_from_strmbase_pin(iface);
 
-    TRACE("(%p/%p)->()\n", This, iface);
+    if (IsEqualGUID(iid, &IID_IMemInputPin))
+        *out = &filter->sink.IMemInputPin_iface;
+    else
+        return E_NOINTERFACE;
 
-    IDirectSoundBuffer_Stop(This->dsbuffer);
-    This->writepos = This->buf_size;
+    IUnknown_AddRef((IUnknown *)*out);
+    return S_OK;
 }
 
-static void dsound_render_start_stream(struct strmbase_renderer *iface)
+static HRESULT dsound_render_sink_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE * pmt)
 {
-    struct dsound_render *This = impl_from_strmbase_renderer(iface);
-
-    TRACE("(%p)\n", This);
+    if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Audio))
+        return S_FALSE;
 
-    if (This->renderer.sink.pin.peer)
-    {
-        IDirectSoundBuffer_Play(This->dsbuffer, 0, 0, DSBPLAY_LOOPING);
-    }
+    return S_OK;
 }
 
-static HRESULT dsound_render_connect(struct strmbase_renderer *iface, const AM_MEDIA_TYPE *mt)
+static HRESULT dsound_render_sink_connect(struct strmbase_sink *iface, IPin *peer, const AM_MEDIA_TYPE *mt)
 {
-    struct dsound_render *This = impl_from_strmbase_renderer(iface);
+    struct dsound_render *This = impl_from_strmbase_pin(&iface->pin);
     const WAVEFORMATEX *format = (WAVEFORMATEX *)mt->pbFormat;
     HRESULT hr = S_OK;
     DSBUFFERDESC buf_desc;
@@ -436,47 +461,93 @@ static HRESULT dsound_render_connect(struct strmbase_renderer *iface, const AM_M
     return hr;
 }
 
-static HRESULT WINAPI DSoundRender_BreakConnect(struct strmbase_renderer *iface)
+static void dsound_render_sink_disconnect(struct strmbase_sink *iface)
 {
-    struct dsound_render *This = impl_from_strmbase_renderer(iface);
+    struct dsound_render *This = impl_from_strmbase_pin(&iface->pin);
 
     TRACE("(%p)->()\n", iface);
 
     if (This->dsbuffer)
         IDirectSoundBuffer_Release(This->dsbuffer);
     This->dsbuffer = NULL;
+}
+
+static HRESULT dsound_render_sink_eos(struct strmbase_sink *iface)
+{
+    struct dsound_render *filter = impl_from_strmbase_pin(&iface->pin);
+    IFilterGraph *graph = filter->filter.graph;
+    IMediaEventSink *event_sink;
+
+    EnterCriticalSection(&filter->stream_cs);
 
+    filter->eos = TRUE;
+
+    if (graph && SUCCEEDED(IFilterGraph_QueryInterface(graph,
+            &IID_IMediaEventSink, (void **)&event_sink)))
+    {
+        IMediaEventSink_Notify(event_sink, EC_COMPLETE, S_OK,
+                (LONG_PTR)&filter->filter.IBaseFilter_iface);
+        IMediaEventSink_Release(event_sink);
+    }
+    strmbase_passthrough_eos(&filter->passthrough);
+    SetEvent(filter->state_event);
+
+    DSoundRender_HandleEndOfStream(filter);
+
+    LeaveCriticalSection(&filter->stream_cs);
     return S_OK;
 }
 
-static HRESULT WINAPI DSoundRender_EndOfStream(struct strmbase_renderer *iface)
+static HRESULT dsound_render_sink_begin_flush(struct strmbase_sink *iface)
 {
-    struct dsound_render *This = impl_from_strmbase_renderer(iface);
-    return DSoundRender_HandleEndOfStream(This);
+    struct dsound_render *filter = impl_from_strmbase_pin(&iface->pin);
+
+    SetEvent(filter->flush_event);
+    return S_OK;
 }
 
-static HRESULT WINAPI DSoundRender_EndFlush(struct strmbase_renderer *iface)
+static HRESULT dsound_render_sink_end_flush(struct strmbase_sink *iface)
 {
-    struct dsound_render *This = impl_from_strmbase_renderer(iface);
+    struct dsound_render *filter = impl_from_strmbase_pin(&iface->pin);
 
-    if (This->dsbuffer)
+    EnterCriticalSection(&filter->stream_cs);
+
+    filter->eos = FALSE;
+    strmbase_passthrough_invalidate_time(&filter->passthrough);
+    ResetEvent(filter->flush_event);
+
+    if (filter->dsbuffer)
     {
-        LPBYTE buffer;
+        void *buffer;
         DWORD size;
 
         /* Force a reset */
-        IDirectSoundBuffer_Lock(This->dsbuffer, 0, 0, (LPVOID *)&buffer, &size, NULL, NULL, DSBLOCK_ENTIREBUFFER);
+        IDirectSoundBuffer_Lock(filter->dsbuffer, 0, 0, &buffer, &size, NULL, NULL, DSBLOCK_ENTIREBUFFER);
         memset(buffer, 0, size);
-        IDirectSoundBuffer_Unlock(This->dsbuffer, buffer, size, NULL, 0);
-        This->writepos = This->buf_size;
+        IDirectSoundBuffer_Unlock(filter->dsbuffer, buffer, size, NULL, 0);
+        filter->writepos = filter->buf_size;
     }
 
+    LeaveCriticalSection(&filter->stream_cs);
     return S_OK;
 }
 
-static void dsound_render_destroy(struct strmbase_renderer *iface)
+static const struct strmbase_sink_ops sink_ops =
 {
-    struct dsound_render *filter = impl_from_strmbase_renderer(iface);
+    .base.pin_query_interface = dsound_render_sink_query_interface,
+    .base.pin_query_accept = dsound_render_sink_query_accept,
+    .base.pin_get_media_type = strmbase_pin_get_media_type,
+    .pfnReceive = dsound_render_sink_Receive,
+    .sink_connect = dsound_render_sink_connect,
+    .sink_disconnect = dsound_render_sink_disconnect,
+    .sink_eos = dsound_render_sink_eos,
+    .sink_begin_flush = dsound_render_sink_begin_flush,
+    .sink_end_flush = dsound_render_sink_end_flush,
+};
+
+static void dsound_render_destroy(struct strmbase_filter *iface)
+{
+    struct dsound_render *filter = impl_from_strmbase_filter(iface);
 
     if (filter->dsbuffer)
         IDirectSoundBuffer_Release(filter->dsbuffer);
@@ -485,22 +556,49 @@ static void dsound_render_destroy(struct strmbase_renderer *iface)
         IDirectSound8_Release(filter->dsound);
     filter->dsound = NULL;
 
-    strmbase_renderer_cleanup(&filter->renderer);
+    if (filter->sink.pin.peer)
+        IPin_Disconnect(filter->sink.pin.peer);
+    IPin_Disconnect(&filter->sink.pin.IPin_iface);
+    strmbase_sink_cleanup(&filter->sink);
+
+    filter->stream_cs.DebugInfo->Spare[0] = 0;
+    DeleteCriticalSection(&filter->stream_cs);
+
+    CloseHandle(filter->state_event);
+    CloseHandle(filter->flush_event);
+
+    strmbase_passthrough_cleanup(&filter->passthrough);
+    strmbase_filter_cleanup(&filter->filter);
     free(filter);
 
     InterlockedDecrement(&object_locks);
 }
 
-static HRESULT dsound_render_query_interface(struct strmbase_renderer *iface, REFIID iid, void **out)
+static struct strmbase_pin *dsound_render_get_pin(struct strmbase_filter *iface, unsigned int index)
 {
-    struct dsound_render *filter = impl_from_strmbase_renderer(iface);
+    struct dsound_render *filter = impl_from_strmbase_filter(iface);
 
-    if (IsEqualGUID(iid, &IID_IBasicAudio))
+    if (index == 0)
+        return &filter->sink.pin;
+    return NULL;
+}
+
+static HRESULT dsound_render_query_interface(struct strmbase_filter *iface, REFIID iid, void **out)
+{
+    struct dsound_render *filter = impl_from_strmbase_filter(iface);
+
+    if (IsEqualGUID(iid, &IID_IAMDirectSound))
+        *out = &filter->IAMDirectSound_iface;
+    else if (IsEqualGUID(iid, &IID_IBasicAudio))
         *out = &filter->IBasicAudio_iface;
+    else if (IsEqualGUID(iid, &IID_IMediaPosition))
+        *out = &filter->passthrough.IMediaPosition_iface;
+    else if (IsEqualGUID(iid, &IID_IMediaSeeking))
+        *out = &filter->passthrough.IMediaSeeking_iface;
+    else if (IsEqualGUID(iid, &IID_IQualityControl))
+        *out = &filter->IQualityControl_iface;
     else if (IsEqualGUID(iid, &IID_IReferenceClock))
         return IUnknown_QueryInterface(filter->system_clock, iid, out);
-    else if (IsEqualGUID(iid, &IID_IAMDirectSound))
-        *out = &filter->IAMDirectSound_iface;
     else
         return E_NOINTERFACE;
 
@@ -508,20 +606,77 @@ static HRESULT dsound_render_query_interface(struct strmbase_renderer *iface, RE
     return S_OK;
 }
 
-static const struct strmbase_renderer_ops renderer_ops =
-{
-    .pfnCheckMediaType = DSoundRender_CheckMediaType,
-    .pfnDoRenderSample = DSoundRender_DoRenderSample,
-    .renderer_start_stream = dsound_render_start_stream,
-    .renderer_stop_stream = dsound_render_stop_stream,
-    .pfnShouldDrawSampleNow = DSoundRender_ShouldDrawSampleNow,
-    .pfnPrepareReceive = DSoundRender_PrepareReceive,
-    .renderer_connect = dsound_render_connect,
-    .pfnBreakConnect = DSoundRender_BreakConnect,
-    .pfnEndOfStream = DSoundRender_EndOfStream,
-    .pfnEndFlush = DSoundRender_EndFlush,
-    .renderer_destroy = dsound_render_destroy,
-    .renderer_query_interface = dsound_render_query_interface,
+static HRESULT dsound_render_init_stream(struct strmbase_filter *iface)
+{
+    struct dsound_render *filter = impl_from_strmbase_filter(iface);
+
+    if (filter->sink.pin.peer)
+        ResetEvent(filter->state_event);
+    filter->eos = FALSE;
+    ResetEvent(filter->flush_event);
+
+    return filter->sink.pin.peer ? S_FALSE : S_OK;
+}
+
+static HRESULT dsound_render_start_stream(struct strmbase_filter *iface, REFERENCE_TIME start)
+{
+    struct dsound_render *filter = impl_from_strmbase_filter(iface);
+
+    filter->stream_start = start;
+
+    SetEvent(filter->state_event);
+
+    if (filter->sink.pin.peer)
+    {
+        filter->eos = FALSE;
+        IDirectSoundBuffer_Play(filter->dsbuffer, 0, 0, DSBPLAY_LOOPING);
+    }
+
+    return S_OK;
+}
+
+static HRESULT dsound_render_stop_stream(struct strmbase_filter *iface)
+{
+    struct dsound_render *filter = impl_from_strmbase_filter(iface);
+
+    if (filter->sink.pin.peer)
+    {
+        IDirectSoundBuffer_Stop(filter->dsbuffer);
+        filter->writepos = filter->buf_size;
+    }
+    return S_OK;
+}
+
+static HRESULT dsound_render_cleanup_stream(struct strmbase_filter *iface)
+{
+    struct dsound_render *filter = impl_from_strmbase_filter(iface);
+
+    strmbase_passthrough_invalidate_time(&filter->passthrough);
+    SetEvent(filter->state_event);
+    SetEvent(filter->flush_event);
+
+    return S_OK;
+}
+
+static HRESULT dsound_render_wait_state(struct strmbase_filter *iface, DWORD timeout)
+{
+    struct dsound_render *filter = impl_from_strmbase_filter(iface);
+
+    if (WaitForSingleObject(filter->state_event, timeout) == WAIT_TIMEOUT)
+        return VFW_S_STATE_INTERMEDIATE;
+    return S_OK;
+}
+
+static const struct strmbase_filter_ops filter_ops =
+{
+    .filter_destroy = dsound_render_destroy,
+    .filter_get_pin = dsound_render_get_pin,
+    .filter_query_interface = dsound_render_query_interface,
+    .filter_init_stream = dsound_render_init_stream,
+    .filter_start_stream = dsound_render_start_stream,
+    .filter_stop_stream = dsound_render_stop_stream,
+    .filter_cleanup_stream = dsound_render_cleanup_stream,
+    .filter_wait_state = dsound_render_wait_state,
 };
 
 /*** IUnknown methods ***/
@@ -532,7 +687,7 @@ static HRESULT WINAPI Basicaudio_QueryInterface(IBasicAudio *iface,
 
     TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppvObj);
 
-    return IUnknown_QueryInterface(This->renderer.filter.outer_unk, riid, ppvObj);
+    return IUnknown_QueryInterface(This->filter.outer_unk, riid, ppvObj);
 }
 
 static ULONG WINAPI Basicaudio_AddRef(IBasicAudio *iface) {
@@ -540,7 +695,7 @@ static ULONG WINAPI Basicaudio_AddRef(IBasicAudio *iface) {
 
     TRACE("(%p/%p)->()\n", This, iface);
 
-    return IUnknown_AddRef(This->renderer.filter.outer_unk);
+    return IUnknown_AddRef(This->filter.outer_unk);
 }
 
 static ULONG WINAPI Basicaudio_Release(IBasicAudio *iface) {
@@ -548,7 +703,7 @@ static ULONG WINAPI Basicaudio_Release(IBasicAudio *iface) {
 
     TRACE("(%p/%p)->()\n", This, iface);
 
-    return IUnknown_Release(This->renderer.filter.outer_unk);
+    return IUnknown_Release(This->filter.outer_unk);
 }
 
 HRESULT WINAPI basic_audio_GetTypeInfoCount(IBasicAudio *iface, UINT *count)
@@ -685,7 +840,7 @@ static HRESULT WINAPI AMDirectSound_QueryInterface(IAMDirectSound *iface,
 
     TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppvObj);
 
-    return IUnknown_QueryInterface(This->renderer.filter.outer_unk, riid, ppvObj);
+    return IUnknown_QueryInterface(This->filter.outer_unk, riid, ppvObj);
 }
 
 static ULONG WINAPI AMDirectSound_AddRef(IAMDirectSound *iface)
@@ -694,7 +849,7 @@ static ULONG WINAPI AMDirectSound_AddRef(IAMDirectSound *iface)
 
     TRACE("(%p/%p)->()\n", This, iface);
 
-    return IUnknown_AddRef(This->renderer.filter.outer_unk);
+    return IUnknown_AddRef(This->filter.outer_unk);
 }
 
 static ULONG WINAPI AMDirectSound_Release(IAMDirectSound *iface)
@@ -703,7 +858,7 @@ static ULONG WINAPI AMDirectSound_Release(IAMDirectSound *iface)
 
     TRACE("(%p/%p)->()\n", This, iface);
 
-    return IUnknown_Release(This->renderer.filter.outer_unk);
+    return IUnknown_Release(This->filter.outer_unk);
 }
 
 /*** IAMDirectSound methods ***/
@@ -794,6 +949,59 @@ static const IAMDirectSoundVtbl IAMDirectSound_Vtbl =
     AMDirectSound_GetFocusWindow
 };
 
+static struct dsound_render *impl_from_IQualityControl(IQualityControl *iface)
+{
+    return CONTAINING_RECORD(iface, struct dsound_render, IQualityControl_iface);
+}
+
+static HRESULT WINAPI dsound_render_qc_QueryInterface(IQualityControl *iface,
+        REFIID iid, void **out)
+{
+    struct dsound_render *filter = impl_from_IQualityControl(iface);
+    return IUnknown_QueryInterface(filter->filter.outer_unk, iid, out);
+}
+
+static ULONG WINAPI dsound_render_qc_AddRef(IQualityControl *iface)
+{
+    struct dsound_render *filter = impl_from_IQualityControl(iface);
+    return IUnknown_AddRef(filter->filter.outer_unk);
+}
+
+static ULONG WINAPI dsound_render_qc_Release(IQualityControl *iface)
+{
+    struct dsound_render *filter = impl_from_IQualityControl(iface);
+    return IUnknown_AddRef(filter->filter.outer_unk);
+}
+
+static HRESULT WINAPI dsound_render_qc_Notify(IQualityControl *iface,
+        IBaseFilter *sender, Quality q)
+{
+    struct dsound_render *filter = impl_from_IQualityControl(iface);
+
+    FIXME("filter %p, sender %p, type %#x, proportion %u, late %s, timestamp %s, stub!\n",
+            filter, sender, q.Type, q.Proportion, debugstr_time(q.Late), debugstr_time(q.TimeStamp));
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI dsound_render_qc_SetSink(IQualityControl *iface, IQualityControl *sink)
+{
+    struct dsound_render *filter = impl_from_IQualityControl(iface);
+
+    FIXME("filter %p, sink %p, stub!\n", filter, sink);
+
+    return E_NOTIMPL;
+}
+
+static const IQualityControlVtbl dsound_render_qc_vtbl =
+{
+    dsound_render_qc_QueryInterface,
+    dsound_render_qc_AddRef,
+    dsound_render_qc_Release,
+    dsound_render_qc_Notify,
+    dsound_render_qc_SetSink,
+};
+
 HRESULT dsound_render_create(IUnknown *outer, IUnknown **out)
 {
     static const DSBUFFERDESC buffer_desc = {
@@ -808,23 +1016,19 @@ HRESULT dsound_render_create(IUnknown *outer, IUnknown **out)
     if (!(object = calloc(1, sizeof(*object))))
         return E_OUTOFMEMORY;
 
-    strmbase_renderer_init(&object->renderer, outer,
-            &CLSID_DSoundRender, L"Audio Input pin (rendered)", &renderer_ops);
+    strmbase_filter_init(&object->filter, outer, &CLSID_DSoundRender, &filter_ops);
 
-    if (FAILED(hr = system_clock_create(&object->renderer.filter.IUnknown_inner, &object->system_clock)))
+    if (FAILED(hr = system_clock_create(&object->filter.IUnknown_inner, &object->system_clock)))
     {
-        strmbase_renderer_cleanup(&object->renderer);
+        strmbase_filter_cleanup(&object->filter);
         free(object);
         return hr;
     }
 
-    object->IBasicAudio_iface.lpVtbl = &IBasicAudio_Vtbl;
-    object->IAMDirectSound_iface.lpVtbl = &IAMDirectSound_Vtbl;
-
     if (FAILED(hr = DirectSoundCreate8(NULL, &object->dsound, NULL)))
     {
         IUnknown_Release(object->system_clock);
-        strmbase_renderer_cleanup(&object->renderer);
+        strmbase_filter_cleanup(&object->filter);
         free(object);
         return hr;
     }
@@ -834,7 +1038,7 @@ HRESULT dsound_render_create(IUnknown *outer, IUnknown **out)
     {
         IDirectSound8_Release(object->dsound);
         IUnknown_Release(object->system_clock);
-        strmbase_renderer_cleanup(&object->renderer);
+        strmbase_filter_cleanup(&object->filter);
         free(object);
         return hr;
     }
@@ -846,8 +1050,22 @@ HRESULT dsound_render_create(IUnknown *outer, IUnknown **out)
         IDirectSoundBuffer_Release(buffer);
     }
 
+    strmbase_passthrough_init(&object->passthrough, (IUnknown *)&object->filter.IBaseFilter_iface);
+    ISeekingPassThru_Init(&object->passthrough.ISeekingPassThru_iface, TRUE, &object->sink.pin.IPin_iface);
+
+    strmbase_sink_init(&object->sink, &object->filter, L"Audio Input pin (rendered)", &sink_ops, NULL);
+
+    InitializeCriticalSection(&object->stream_cs);
+    object->stream_cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__": dsound_render.stream_cs");
+    object->state_event = CreateEventW(NULL, TRUE, TRUE, NULL);
+    object->flush_event = CreateEventW(NULL, TRUE, TRUE, NULL);
+
+    object->IBasicAudio_iface.lpVtbl = &IBasicAudio_Vtbl;
+    object->IAMDirectSound_iface.lpVtbl = &IAMDirectSound_Vtbl;
+    object->IQualityControl_iface.lpVtbl = &dsound_render_qc_vtbl;
+
     TRACE("Created DirectSound renderer %p.\n", object);
-    *out = &object->renderer.filter.IUnknown_inner;
+    *out = &object->filter.IUnknown_inner;
 
     return S_OK;
 }
diff --git a/dlls/quartz/tests/dsoundrender.c b/dlls/quartz/tests/dsoundrender.c
index 0e7fed4c259..9506abc7977 100644
--- a/dlls/quartz/tests/dsoundrender.c
+++ b/dlls/quartz/tests/dsoundrender.c
@@ -615,8 +615,7 @@ static const struct strmbase_filter_ops testfilter_ops =
 
 static HRESULT testsource_query_interface(struct strmbase_pin *iface, REFIID iid, void **out)
 {
-    todo_wine_if (IsEqualGUID(iid, &IID_IQualityControl))
-        ok(!IsEqualGUID(iid, &IID_IQualityControl), "Unexpected query for IQualityControl.\n");
+    ok(!IsEqualGUID(iid, &IID_IQualityControl), "Unexpected query for IQualityControl.\n");
     return E_NOINTERFACE;
 }
 
diff --git a/dlls/strmbase/strmbase_private.h b/dlls/strmbase/strmbase_private.h
index d589213b713..ff82d942813 100644
--- a/dlls/strmbase/strmbase_private.h
+++ b/dlls/strmbase/strmbase_private.h
@@ -58,8 +58,4 @@ void QualityControlRender_DoQOS(struct strmbase_qc *priv);
 void QualityControlRender_BeginRender(struct strmbase_qc *This, REFERENCE_TIME start, REFERENCE_TIME stop);
 void QualityControlRender_EndRender(struct strmbase_qc *This);
 
-void strmbase_passthrough_update_time(struct strmbase_passthrough *passthrough, REFERENCE_TIME time);
-void strmbase_passthrough_invalidate_time(struct strmbase_passthrough *passthrough);
-void strmbase_passthrough_eos(struct strmbase_passthrough *passthrough);
-
 #endif /* __WINE_STRMBASE_PRIVATE_H */
diff --git a/include/wine/strmbase.h b/include/wine/strmbase.h
index 2e84edb7cdd..9c3cdc4ac5e 100644
--- a/include/wine/strmbase.h
+++ b/include/wine/strmbase.h
@@ -271,6 +271,10 @@ struct strmbase_passthrough
 void strmbase_passthrough_init(struct strmbase_passthrough *passthrough, IUnknown *outer);
 void strmbase_passthrough_cleanup(struct strmbase_passthrough *passthrough);
 
+void strmbase_passthrough_eos(struct strmbase_passthrough *passthrough);
+void strmbase_passthrough_invalidate_time(struct strmbase_passthrough *passthrough);
+void strmbase_passthrough_update_time(struct strmbase_passthrough *passthrough, REFERENCE_TIME time);
+
 struct strmbase_qc
 {
     IQualityControl IQualityControl_iface;
-- 
2.27.0




More information about the wine-devel mailing list