From ad13ce161a8c60ecf9bd95b6604c7578a1160d06 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Tue, 10 Mar 2020 22:03:29 -0500 Subject: [PATCH] quartz/avidec: Reimplement the AVI decoder directly on top of strmbase_filter. Signed-off-by: Zebediah Figura Signed-off-by: Alexandre Julliard --- dlls/quartz/avidec.c | 390 ++++++++++++++++++++++++++++--------- dlls/quartz/tests/avidec.c | 4 +- 2 files changed, 295 insertions(+), 99 deletions(-) diff --git a/dlls/quartz/avidec.c b/dlls/quartz/avidec.c index abed957fa6d..4abc715be12 100644 --- a/dlls/quartz/avidec.c +++ b/dlls/quartz/avidec.c @@ -38,51 +38,51 @@ WINE_DEFAULT_DEBUG_CHANNEL(quartz); typedef struct AVIDecImpl { - 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; } AVIDecImpl; -static inline AVIDecImpl *impl_from_TransformFilter( TransformFilter *iface ) +static AVIDecImpl *impl_from_strmbase_filter(struct strmbase_filter *iface) { - return CONTAINING_RECORD(iface, AVIDecImpl, tf); + return CONTAINING_RECORD(iface, AVIDecImpl, filter); } -static HRESULT WINAPI AVIDec_StartStreaming(TransformFilter* pTransformFilter) +static HRESULT avi_decompressor_sink_query_interface(struct strmbase_pin *iface, REFIID iid, void **out) { - AVIDecImpl* This = impl_from_TransformFilter(pTransformFilter); - DWORD result; + AVIDecImpl *filter = impl_from_strmbase_filter(iface->filter); - TRACE("(%p)->()\n", This); - This->late = -1; - - result = ICDecompressBegin(This->hvid, This->pBihIn, This->pBihOut); - if (result != ICERR_OK) - { - ERR("Cannot start processing (%d)\n", result); - return E_FAIL; - } - return S_OK; -} - -static HRESULT WINAPI AVIDec_EndFlush(TransformFilter *pTransformFilter) { - AVIDecImpl* This = impl_from_TransformFilter(pTransformFilter); - This->late = -1; - return S_OK; -} - -static HRESULT WINAPI AVIDec_NotifyDrop(TransformFilter *pTransformFilter, IBaseFilter *sender, Quality qm) { - AVIDecImpl *This = impl_from_TransformFilter(pTransformFilter); - - EnterCriticalSection(&This->tf.filter.csFilter); - if (qm.Late > 0) - This->late = qm.Late + qm.TimeStamp; + if (IsEqualGUID(iid, &IID_IMemInputPin)) + *out = &filter->sink.IMemInputPin_iface; else - This->late = -1; - LeaveCriticalSection(&This->tf.filter.csFilter); + return E_NOINTERFACE; + + IUnknown_AddRef((IUnknown *)*out); + return S_OK; +} + +static HRESULT avi_decompressor_sink_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE *mt) +{ + return S_OK; +} + +static HRESULT avi_decompressor_sink_end_flush(struct strmbase_sink *iface) +{ + AVIDecImpl *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(AVIDecImpl *This, REFERENCE_TIME tStart) { return 0; } -static HRESULT WINAPI AVIDec_Receive(TransformFilter *tf, IMediaSample *pSample) +static HRESULT WINAPI avi_decompressor_sink_Receive(struct strmbase_sink *iface, IMediaSample *pSample) { - AVIDecImpl* This = impl_from_TransformFilter(tf); + AVIDecImpl *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) { - AVIDecImpl* 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) -{ - AVIDecImpl* This = impl_from_TransformFilter(tf); + AVIDecImpl *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) { - AVIDecImpl *This = impl_from_TransformFilter(tf); + AVIDecImpl *filter = impl_from_strmbase_filter(iface->pin.filter); - TRACE("(%p)->()\n", This); + if (filter->hvid) + ICClose(filter->hvid); + CoTaskMemFree(filter->pBihIn); + CoTaskMemFree(filter->pBihOut); + filter->hvid = NULL; + filter->pBihIn = NULL; + filter->pBihOut = NULL; +} - 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 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, +}; +static HRESULT avi_decompressor_source_query_interface(struct strmbase_pin *iface, REFIID iid, void **out) +{ + AVIDecImpl *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) { - AVIDecImpl *pAVI = impl_from_TransformFilter(tf); + AVIDecImpl *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) +{ + AVIDecImpl *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) +{ + AVIDecImpl *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 AVIDecImpl *impl_from_source_IQualityControl(IQualityControl *iface) +{ + return CONTAINING_RECORD(iface, AVIDecImpl, source_IQualityControl_iface); +} + +static HRESULT WINAPI acm_wrapper_source_qc_QueryInterface(IQualityControl *iface, + REFIID iid, void **out) +{ + AVIDecImpl *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) +{ + AVIDecImpl *filter = impl_from_source_IQualityControl(iface); + return IPin_AddRef(&filter->source.pin.IPin_iface); +} + +static ULONG WINAPI acm_wrapper_source_qc_Release(IQualityControl *iface) +{ + AVIDecImpl *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) +{ + AVIDecImpl *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) +{ + AVIDecImpl *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) +{ + AVIDecImpl *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) +{ + AVIDecImpl *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) +{ + AVIDecImpl *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) +{ + AVIDecImpl *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 AVIDec_create(IUnknown *outer, void **out) { + AVIDecImpl *object; + ISeekingPassThru *passthrough; HRESULT hr; - AVIDecImpl * This; - *out = NULL; + if (!(object = calloc(1, sizeof(*object)))) + return E_OUTOFMEMORY; - hr = strmbase_transform_create(sizeof(AVIDecImpl), 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 b406a4861b9..1eafb9b3293 100644 --- a/dlls/quartz/tests/avidec.c +++ b/dlls/quartz/tests/avidec.c @@ -644,7 +644,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); @@ -658,7 +658,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);