[PATCH 2/4] quartz/avidec: Reimplement the AVI decoder directly on top of strmbase_filter.

Zebediah Figura z.figura12 at gmail.com
Mon Mar 9 21:21:21 CDT 2020


Signed-off-by: Zebediah Figura <zfigura at codeweavers.com>
---
 dlls/quartz/avidec.c       | 378 ++++++++++++++++++++++++++++---------
 dlls/quartz/tests/avidec.c |   4 +-
 2 files changed, 289 insertions(+), 93 deletions(-)

diff --git a/dlls/quartz/avidec.c b/dlls/quartz/avidec.c
index d900b6e27d0..b91a77de463 100644
--- a/dlls/quartz/avidec.c
+++ b/dlls/quartz/avidec.c
@@ -38,51 +38,51 @@ WINE_DEFAULT_DEBUG_CHANNEL(quartz);
 
 struct avi_decompressor
 {
-    TransformFilter tf;
+    struct strmbase_filter filter;
+    CRITICAL_SECTION stream_cs;
 
+    struct strmbase_source source;
+    IQualityControl source_IQualityControl_iface;
+    IUnknown *seeking;
+
+    struct strmbase_sink sink;
+
+    AM_MEDIA_TYPE mt;
     HIC hvid;
     BITMAPINFOHEADER* pBihIn;
     BITMAPINFOHEADER* pBihOut;
     REFERENCE_TIME late;
 };
 
-static inline struct avi_decompressor *impl_from_TransformFilter( TransformFilter *iface )
+static struct avi_decompressor *impl_from_strmbase_filter(struct strmbase_filter *iface)
 {
-    return CONTAINING_RECORD(iface, struct avi_decompressor, tf);
+    return CONTAINING_RECORD(iface, struct avi_decompressor, filter);
 }
 
-static HRESULT WINAPI AVIDec_StartStreaming(TransformFilter* pTransformFilter)
+static HRESULT avi_decompressor_sink_query_interface(struct strmbase_pin *iface, REFIID iid, void **out)
 {
-    struct avi_decompressor *This = impl_from_TransformFilter(pTransformFilter);
-    DWORD result;
+    struct avi_decompressor *filter = impl_from_strmbase_filter(iface->filter);
 
-    TRACE("(%p)->()\n", This);
-    This->late = -1;
+    if (IsEqualGUID(iid, &IID_IMemInputPin))
+        *out = &filter->sink.IMemInputPin_iface;
+    else
+        return E_NOINTERFACE;
 
-    result = ICDecompressBegin(This->hvid, This->pBihIn, This->pBihOut);
-    if (result != ICERR_OK)
-    {
-        ERR("Cannot start processing (%d)\n", result);
-	return E_FAIL;
-    }
+    IUnknown_AddRef((IUnknown *)*out);
     return S_OK;
 }
 
-static HRESULT WINAPI AVIDec_EndFlush(TransformFilter *pTransformFilter) {
-    struct avi_decompressor *This = impl_from_TransformFilter(pTransformFilter);
-    This->late = -1;
+static HRESULT avi_decompressor_sink_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE *mt)
+{
     return S_OK;
 }
 
-static HRESULT WINAPI AVIDec_NotifyDrop(TransformFilter *pTransformFilter, IBaseFilter *sender, Quality qm) {
-    struct avi_decompressor *This = impl_from_TransformFilter(pTransformFilter);
-
-    EnterCriticalSection(&This->tf.filter.csFilter);
-    if (qm.Late > 0)
-        This->late = qm.Late + qm.TimeStamp;
-    else
-        This->late = -1;
-    LeaveCriticalSection(&This->tf.filter.csFilter);
+static HRESULT avi_decompressor_sink_end_flush(struct strmbase_sink *iface)
+{
+    struct avi_decompressor *filter = impl_from_strmbase_filter(iface->pin.filter);
+    filter->late = -1;
+    if (filter->source.pin.peer)
+        return IPin_EndFlush(filter->source.pin.peer);
     return S_OK;
 }
 
@@ -98,9 +98,9 @@ static int AVIDec_DropSample(struct avi_decompressor *This, REFERENCE_TIME tStar
     return 0;
 }
 
-static HRESULT WINAPI AVIDec_Receive(TransformFilter *tf, IMediaSample *pSample)
+static HRESULT WINAPI avi_decompressor_sink_Receive(struct strmbase_sink *iface, IMediaSample *pSample)
 {
-    struct avi_decompressor *This = impl_from_TransformFilter(tf);
+    struct avi_decompressor *This = impl_from_strmbase_filter(iface->pin.filter);
     HRESULT hr;
     DWORD res;
     IMediaSample* pOutSample = NULL;
@@ -111,10 +111,29 @@ static HRESULT WINAPI AVIDec_Receive(TransformFilter *tf, IMediaSample *pSample)
     LONGLONG tStart, tStop;
     DWORD flags = 0;
 
+    /* We do not expect pin connection state to change while the filter is
+     * running. This guarantee is necessary, since otherwise we would have to
+     * take the filter lock, and we can't take the filter lock from a streaming
+     * thread. */
+    if (!This->source.pMemInputPin)
+    {
+        WARN("Source is not connected, returning VFW_E_NOT_CONNECTED.\n");
+        return VFW_E_NOT_CONNECTED;
+    }
+
+    if (This->filter.state == State_Stopped)
+        return VFW_E_WRONG_STATE;
+
+    if (This->sink.flushing)
+        return S_FALSE;
+
+    EnterCriticalSection(&This->stream_cs);
+
     hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
     if (FAILED(hr))
     {
         ERR("Cannot get pointer to sample data (%x)\n", hr);
+        LeaveCriticalSection(&This->stream_cs);
         return hr;
     }
 
@@ -125,9 +144,10 @@ static HRESULT WINAPI AVIDec_Receive(TransformFilter *tf, IMediaSample *pSample)
     /* Update input size to match sample size */
     This->pBihIn->biSizeImage = cbSrcStream;
 
-    hr = BaseOutputPinImpl_GetDeliveryBuffer(&This->tf.source, &pOutSample, NULL, NULL, 0);
+    hr = BaseOutputPinImpl_GetDeliveryBuffer(&This->source, &pOutSample, NULL, NULL, 0);
     if (FAILED(hr)) {
         ERR("Unable to get delivery buffer (%x)\n", hr);
+        LeaveCriticalSection(&This->stream_cs);
         return hr;
     }
 
@@ -138,12 +158,14 @@ static HRESULT WINAPI AVIDec_Receive(TransformFilter *tf, IMediaSample *pSample)
     if (FAILED(hr)) {
 	ERR("Unable to get pointer to buffer (%x)\n", hr);
         IMediaSample_Release(pOutSample);
+        LeaveCriticalSection(&This->stream_cs);
         return hr;
     }
     cbDstStream = IMediaSample_GetSize(pOutSample);
     if (cbDstStream < This->pBihOut->biSizeImage) {
         ERR("Sample size is too small %d < %d\n", cbDstStream, This->pBihOut->biSizeImage);
         IMediaSample_Release(pOutSample);
+        LeaveCriticalSection(&This->stream_cs);
         return E_FAIL;
     }
 
@@ -162,6 +184,7 @@ static HRESULT WINAPI AVIDec_Receive(TransformFilter *tf, IMediaSample *pSample)
     /* Drop sample if it's intended to be dropped */
     if (flags & ICDECOMPRESS_HURRYUP) {
         IMediaSample_Release(pOutSample);
+        LeaveCriticalSection(&This->stream_cs);
         return S_OK;
     }
 
@@ -183,36 +206,18 @@ static HRESULT WINAPI AVIDec_Receive(TransformFilter *tf, IMediaSample *pSample)
     else
         IMediaSample_SetMediaTime(pOutSample, NULL, NULL);
 
-    hr = IMemInputPin_Receive(This->tf.source.pMemInputPin, pOutSample);
+    hr = IMemInputPin_Receive(This->source.pMemInputPin, pOutSample);
     if (hr != S_OK && hr != VFW_E_NOT_CONNECTED)
         ERR("Error sending sample (%x)\n", hr);
 
     IMediaSample_Release(pOutSample);
+    LeaveCriticalSection(&This->stream_cs);
     return hr;
 }
 
-static HRESULT WINAPI AVIDec_StopStreaming(TransformFilter* pTransformFilter)
+static HRESULT avi_decompressor_sink_connect(struct strmbase_sink *iface, IPin *peer, const AM_MEDIA_TYPE *pmt)
 {
-    struct avi_decompressor *This = impl_from_TransformFilter(pTransformFilter);
-    DWORD result;
-
-    TRACE("(%p)->()\n", This);
-
-    if (!This->hvid)
-        return S_OK;
-
-    result = ICDecompressEnd(This->hvid);
-    if (result != ICERR_OK)
-    {
-        ERR("Cannot stop processing (%d)\n", result);
-        return E_FAIL;
-    }
-    return S_OK;
-}
-
-static HRESULT avi_dec_connect_sink(TransformFilter *tf, const AM_MEDIA_TYPE *pmt)
-{
-    struct avi_decompressor *This = impl_from_TransformFilter(tf);
+    struct avi_decompressor *This = impl_from_strmbase_filter(iface->pin.filter);
     HRESULT hr = VFW_E_TYPE_NOT_ACCEPTED;
 
     /* Check root (GUID w/o FOURCC) */
@@ -233,12 +238,11 @@ static HRESULT avi_dec_connect_sink(TransformFilter *tf, const AM_MEDIA_TYPE *pm
         This->hvid = ICLocate(pmt->majortype.Data1, pmt->subtype.Data1, bmi, NULL, ICMODE_DECOMPRESS);
         if (This->hvid)
         {
-            AM_MEDIA_TYPE* outpmt = &This->tf.pmt;
             const CLSID* outsubtype;
             DWORD bih_size;
             DWORD output_depth = bmi->biBitCount;
             DWORD result;
-            FreeMediaType(outpmt);
+            FreeMediaType(&This->mt);
 
             switch(bmi->biBitCount)
             {
@@ -283,13 +287,13 @@ static HRESULT avi_dec_connect_sink(TransformFilter *tf, const AM_MEDIA_TYPE *pm
             }
 
             /* Update output media type */
-            CopyMediaType(outpmt, pmt);
-            outpmt->subtype = *outsubtype;
+            CopyMediaType(&This->mt, pmt);
+            This->mt.subtype = *outsubtype;
 
             if (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo))
-                memcpy(&(((VIDEOINFOHEADER *)outpmt->pbFormat)->bmiHeader), This->pBihOut, This->pBihOut->biSize);
+                memcpy(&(((VIDEOINFOHEADER *)This->mt.pbFormat)->bmiHeader), This->pBihOut, This->pBihOut->biSize);
             else if (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo2))
-                memcpy(&(((VIDEOINFOHEADER2 *)outpmt->pbFormat)->bmiHeader), This->pBihOut, This->pBihOut->biSize);
+                memcpy(&(((VIDEOINFOHEADER2 *)This->mt.pbFormat)->bmiHeader), This->pBihOut, This->pBihOut->biSize);
             else
                 assert(0);
 
@@ -305,29 +309,71 @@ failed:
     return hr;
 }
 
-static HRESULT WINAPI AVIDec_BreakConnect(TransformFilter *tf, PIN_DIRECTION dir)
+static void avi_decompressor_sink_disconnect(struct strmbase_sink *iface)
 {
-    struct avi_decompressor *This = impl_from_TransformFilter(tf);
+    struct avi_decompressor *filter = impl_from_strmbase_filter(iface->pin.filter);
+
+    if (filter->hvid)
+        ICClose(filter->hvid);
+    CoTaskMemFree(filter->pBihIn);
+    CoTaskMemFree(filter->pBihOut);
+    filter->hvid = NULL;
+    filter->pBihIn = NULL;
+    filter->pBihOut = NULL;
+}
 
-    TRACE("(%p)->()\n", This);
+static const struct strmbase_sink_ops sink_ops =
+{
+    .base.pin_query_interface = avi_decompressor_sink_query_interface,
+    .base.pin_query_accept = avi_decompressor_sink_query_accept,
+    .base.pin_get_media_type = strmbase_pin_get_media_type,
+    .pfnReceive = avi_decompressor_sink_Receive,
+    .sink_connect = avi_decompressor_sink_connect,
+    .sink_disconnect = avi_decompressor_sink_disconnect,
+    .sink_end_flush = avi_decompressor_sink_end_flush,
+};
 
-    if (dir == PINDIR_INPUT)
-    {
-        if (This->hvid)
-            ICClose(This->hvid);
-        CoTaskMemFree(This->pBihIn);
-        CoTaskMemFree(This->pBihOut);
-        This->hvid = NULL;
-        This->pBihIn = NULL;
-        This->pBihOut = NULL;
-    }
+static HRESULT avi_decompressor_source_query_interface(struct strmbase_pin *iface, REFIID iid, void **out)
+{
+    struct avi_decompressor *filter = impl_from_strmbase_filter(iface->filter);
+
+    if (IsEqualGUID(iid, &IID_IQualityControl))
+        *out = &filter->source_IQualityControl_iface;
+    else if (IsEqualGUID(iid, &IID_IMediaSeeking))
+        return IUnknown_QueryInterface(filter->seeking, iid, out);
+    else
+        return E_NOINTERFACE;
 
+    IUnknown_AddRef((IUnknown *)*out);
     return S_OK;
 }
 
-static HRESULT WINAPI AVIDec_DecideBufferSize(TransformFilter *tf, IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
+static HRESULT avi_decompressor_source_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE *mt)
 {
-    struct avi_decompressor *pAVI = impl_from_TransformFilter(tf);
+    struct avi_decompressor *filter = impl_from_strmbase_filter(iface->filter);
+
+    if (IsEqualGUID(&mt->majortype, &filter->mt.majortype)
+            && (IsEqualGUID(&mt->subtype, &filter->mt.subtype)
+            || IsEqualGUID(&filter->mt.subtype, &GUID_NULL)))
+        return S_OK;
+    return S_FALSE;
+}
+
+static HRESULT avi_decompressor_source_get_media_type(struct strmbase_pin *iface,
+        unsigned int index, AM_MEDIA_TYPE *mt)
+{
+    struct avi_decompressor *filter = impl_from_strmbase_filter(iface->filter);
+
+    if (index)
+        return VFW_S_NO_MORE_ITEMS;
+    CopyMediaType(mt, &filter->mt);
+    return S_OK;
+}
+
+static HRESULT WINAPI avi_decompressor_source_DecideBufferSize(struct strmbase_source *iface,
+        IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
+{
+    struct avi_decompressor *pAVI = impl_from_strmbase_filter(iface->pin.filter);
     ALLOCATOR_PROPERTIES actual;
 
     if (!ppropInputRequest->cbAlign)
@@ -342,35 +388,185 @@ static HRESULT WINAPI AVIDec_DecideBufferSize(TransformFilter *tf, IMemAllocator
     return IMemAllocator_SetProperties(pAlloc, ppropInputRequest, &actual);
 }
 
-static const TransformFilterFuncTable AVIDec_FuncsTable = {
-    .pfnDecideBufferSize = AVIDec_DecideBufferSize,
-    .pfnStartStreaming = AVIDec_StartStreaming,
-    .pfnReceive = AVIDec_Receive,
-    .pfnStopStreaming = AVIDec_StopStreaming,
-    .transform_connect_sink = avi_dec_connect_sink,
-    .pfnBreakConnect = AVIDec_BreakConnect,
-    .pfnEndFlush = AVIDec_EndFlush,
-    .pfnNotify = AVIDec_NotifyDrop,
+static const struct strmbase_source_ops source_ops =
+{
+    .base.pin_query_interface = avi_decompressor_source_query_interface,
+    .base.pin_query_accept = avi_decompressor_source_query_accept,
+    .base.pin_get_media_type = avi_decompressor_source_get_media_type,
+    .pfnAttemptConnection = BaseOutputPinImpl_AttemptConnection,
+    .pfnDecideAllocator = BaseOutputPinImpl_DecideAllocator,
+    .pfnDecideBufferSize = avi_decompressor_source_DecideBufferSize,
+};
+
+static struct avi_decompressor *impl_from_source_IQualityControl(IQualityControl *iface)
+{
+    return CONTAINING_RECORD(iface, struct avi_decompressor, source_IQualityControl_iface);
+}
+
+static HRESULT WINAPI acm_wrapper_source_qc_QueryInterface(IQualityControl *iface,
+        REFIID iid, void **out)
+{
+    struct avi_decompressor *filter = impl_from_source_IQualityControl(iface);
+    return IPin_QueryInterface(&filter->source.pin.IPin_iface, iid, out);
+}
+
+static ULONG WINAPI acm_wrapper_source_qc_AddRef(IQualityControl *iface)
+{
+    struct avi_decompressor *filter = impl_from_source_IQualityControl(iface);
+    return IPin_AddRef(&filter->source.pin.IPin_iface);
+}
+
+static ULONG WINAPI acm_wrapper_source_qc_Release(IQualityControl *iface)
+{
+    struct avi_decompressor *filter = impl_from_source_IQualityControl(iface);
+    return IPin_Release(&filter->source.pin.IPin_iface);
+}
+
+static HRESULT WINAPI acm_wrapper_source_qc_Notify(IQualityControl *iface,
+        IBaseFilter *sender, Quality q)
+{
+    struct avi_decompressor *filter = impl_from_source_IQualityControl(iface);
+
+    TRACE("filter %p, sender %p, type %#x, proportion %u, late %s, timestamp %s.\n",
+            filter, sender, q.Type, q.Proportion, debugstr_time(q.Late), debugstr_time(q.TimeStamp));
+
+    EnterCriticalSection(&filter->stream_cs);
+    if (q.Late > 0)
+        filter->late = q.Late + q.TimeStamp;
+    else
+        filter->late = -1;
+    LeaveCriticalSection(&filter->stream_cs);
+    return S_OK;
+}
+
+static HRESULT WINAPI acm_wrapper_source_qc_SetSink(IQualityControl *iface, IQualityControl *sink)
+{
+    struct avi_decompressor *filter = impl_from_source_IQualityControl(iface);
+
+    TRACE("filter %p, sink %p.\n", filter, sink);
+
+    return S_OK;
+}
+
+static const IQualityControlVtbl source_qc_vtbl =
+{
+    acm_wrapper_source_qc_QueryInterface,
+    acm_wrapper_source_qc_AddRef,
+    acm_wrapper_source_qc_Release,
+    acm_wrapper_source_qc_Notify,
+    acm_wrapper_source_qc_SetSink,
+};
+
+static struct strmbase_pin *avi_decompressor_get_pin(struct strmbase_filter *iface, unsigned int index)
+{
+    struct avi_decompressor *filter = impl_from_strmbase_filter(iface);
+
+    if (index == 0)
+        return &filter->sink.pin;
+    else if (index == 1)
+        return &filter->source.pin;
+    return NULL;
+}
+
+static void avi_decompressor_destroy(struct strmbase_filter *iface)
+{
+    struct avi_decompressor *filter = impl_from_strmbase_filter(iface);
+
+    if (filter->sink.pin.peer)
+        IPin_Disconnect(filter->sink.pin.peer);
+    IPin_Disconnect(&filter->sink.pin.IPin_iface);
+
+    if (filter->source.pin.peer)
+        IPin_Disconnect(filter->source.pin.peer);
+    IPin_Disconnect(&filter->source.pin.IPin_iface);
+
+    strmbase_sink_cleanup(&filter->sink);
+    strmbase_source_cleanup(&filter->source);
+
+    filter->stream_cs.DebugInfo->Spare[0] = 0;
+    DeleteCriticalSection(&filter->stream_cs);
+    FreeMediaType(&filter->mt);
+    IUnknown_Release(filter->seeking);
+    strmbase_filter_cleanup(&filter->filter);
+    free(filter);
+}
+
+static HRESULT avi_decompressor_init_stream(struct strmbase_filter *iface)
+{
+    struct avi_decompressor *filter = impl_from_strmbase_filter(iface);
+    LRESULT res;
+
+    filter->late = -1;
+
+    if ((res = ICDecompressBegin(filter->hvid, filter->pBihIn, filter->pBihOut)))
+    {
+        ERR("ICDecompressBegin() failed, error %ld.\n", res);
+        return E_FAIL;
+    }
+
+    BaseOutputPinImpl_Active(&filter->source);
+    return S_OK;
+}
+
+static HRESULT avi_decompressor_cleanup_stream(struct strmbase_filter *iface)
+{
+    struct avi_decompressor *filter = impl_from_strmbase_filter(iface);
+    LRESULT res;
+
+    if (filter->hvid && (res = ICDecompressEnd(filter->hvid)))
+    {
+        ERR("ICDecompressEnd() failed, error %ld.\n", res);
+        return E_FAIL;
+    }
+
+    BaseOutputPinImpl_Inactive(&filter->source);
+    return S_OK;
+}
+
+static const struct strmbase_filter_ops filter_ops =
+{
+    .filter_get_pin = avi_decompressor_get_pin,
+    .filter_destroy = avi_decompressor_destroy,
+    .filter_init_stream = avi_decompressor_init_stream,
+    .filter_cleanup_stream = avi_decompressor_cleanup_stream,
 };
 
 HRESULT avi_dec_create(IUnknown *outer, IUnknown **out)
 {
-    struct avi_decompressor *This;
+    struct avi_decompressor *object;
+    ISeekingPassThru *passthrough;
     HRESULT hr;
 
-    *out = NULL;
+    if (!(object = calloc(1, sizeof(*object))))
+        return E_OUTOFMEMORY;
 
-    hr = strmbase_transform_create(sizeof(*This), outer, &CLSID_AVIDec,
-            &AVIDec_FuncsTable, (IBaseFilter **)&This);
+    strmbase_filter_init(&object->filter, outer, &CLSID_AVIDec, &filter_ops);
 
-    if (FAILED(hr))
+    InitializeCriticalSection(&object->stream_cs);
+    object->stream_cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__": avi_decompressor.stream_cs");
+
+    strmbase_sink_init(&object->sink, &object->filter, L"In", &sink_ops, NULL);
+
+    strmbase_source_init(&object->source, &object->filter, L"Out", &source_ops);
+    object->source_IQualityControl_iface.lpVtbl = &source_qc_vtbl;
+
+    if (FAILED(hr = CoCreateInstance(&CLSID_SeekingPassThru,
+            (IUnknown *)&object->source.pin.IPin_iface, CLSCTX_INPROC_SERVER,
+            &IID_IUnknown, (void **)&object->seeking)))
+    {
+        strmbase_sink_cleanup(&object->sink);
+        strmbase_source_cleanup(&object->source);
+        strmbase_filter_cleanup(&object->filter);
+        free(object);
         return hr;
+    }
 
-    This->hvid = NULL;
-    This->pBihIn = NULL;
-    This->pBihOut = NULL;
+    IUnknown_QueryInterface(object->seeking, &IID_ISeekingPassThru, (void **)&passthrough);
+    ISeekingPassThru_Init(passthrough, FALSE, &object->sink.pin.IPin_iface);
+    ISeekingPassThru_Release(passthrough);
 
-    *out = &This->tf.filter.IUnknown_inner;
+    TRACE("Created AVI decompressor %p.\n", object);
+    *out = &object->filter.IUnknown_inner;
 
-    return hr;
+    return S_OK;
 }
diff --git a/dlls/quartz/tests/avidec.c b/dlls/quartz/tests/avidec.c
index 81d153d4c45..725e034ae27 100644
--- a/dlls/quartz/tests/avidec.c
+++ b/dlls/quartz/tests/avidec.c
@@ -701,7 +701,7 @@ static void test_unconnected_filter_state(void)
     todo_wine ok(state == State_Paused, "Got state %u.\n", state);
 
     hr = IBaseFilter_Stop(filter);
-    todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
+    ok(hr == S_OK, "Got hr %#x.\n", hr);
 
     hr = IBaseFilter_GetState(filter, 0, &state);
     ok(hr == S_OK, "Got hr %#x.\n", hr);
@@ -715,7 +715,7 @@ static void test_unconnected_filter_state(void)
     todo_wine ok(state == State_Running, "Got state %u.\n", state);
 
     hr = IBaseFilter_Stop(filter);
-    todo_wine ok(hr == S_OK, "Got hr %#x.\n", hr);
+    ok(hr == S_OK, "Got hr %#x.\n", hr);
 
     hr = IBaseFilter_GetState(filter, 0, &state);
     ok(hr == S_OK, "Got hr %#x.\n", hr);
-- 
2.25.1




More information about the wine-devel mailing list