From 7dea79c487401243a900f112c35f9b7038f64f4a Mon Sep 17 00:00:00 2001 From: Christian Costa Date: Wed, 2 Mar 2005 10:12:12 +0000 Subject: [PATCH] Handle End Of Stream notifications. Some AVI Splitter fixes. --- dlls/quartz/avisplit.c | 67 +++++++++++++++++++++++++++++++++-------- dlls/quartz/dsoundrender.c | 23 ++++++++------ dlls/quartz/filtergraph.c | 45 +++++++++++++++------------ dlls/quartz/pin.c | 2 +- dlls/quartz/tests/filtergraph.c | 14 ++++++++- dlls/quartz/transform.c | 28 ++++++++++++++++- dlls/quartz/videorenderer.c | 21 ++++++++++++- dlls/quartz/waveparser.c | 32 +++++++++++++++++++- 8 files changed, 185 insertions(+), 47 deletions(-) diff --git a/dlls/quartz/avisplit.c b/dlls/quartz/avisplit.c index a7acadd2245..f282dd1c39f 100644 --- a/dlls/quartz/avisplit.c +++ b/dlls/quartz/avisplit.c @@ -55,11 +55,14 @@ typedef struct AVISplitterImpl AVIMAINHEADER AviHeader; } AVISplitterImpl; -static HRESULT AVISplitter_NextChunk(LONGLONG * pllCurrentChunkOffset, RIFFCHUNK * pCurrentChunk, const REFERENCE_TIME * tStart, const REFERENCE_TIME * tStop, const BYTE * pbSrcStream) +static HRESULT AVISplitter_NextChunk(LONGLONG * pllCurrentChunkOffset, RIFFCHUNK * pCurrentChunk, const REFERENCE_TIME * tStart, const REFERENCE_TIME * tStop, const BYTE * pbSrcStream, int inner) { - *pllCurrentChunkOffset += MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK) + RIFFROUND(pCurrentChunk->cb)); - - if (*pllCurrentChunkOffset > *tStop) + if (inner) + *pllCurrentChunkOffset += MEDIATIME_FROM_BYTES(sizeof(RIFFLIST)); + else + *pllCurrentChunkOffset += MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK) + RIFFROUND(pCurrentChunk->cb)); + + if (*pllCurrentChunkOffset >= *tStop) return S_FALSE; /* no more data - we couldn't even get the next chunk header! */ else if (*pllCurrentChunkOffset + MEDIATIME_FROM_BYTES(sizeof(RIFFCHUNK)) >= *tStop) { @@ -88,7 +91,7 @@ static HRESULT AVISplitter_Sample(LPVOID iface, IMediaSample * pSample) cbSrcStream = IMediaSample_GetActualDataLength(pSample); /* trace removed for performance reasons */ -/* TRACE("(%p)\n", pSample); */ + /* TRACE("(%p)\n", pSample); */ assert(BYTES_FROM_MEDIATIME(tStop - tStart) == cbSrcStream); @@ -104,7 +107,8 @@ static HRESULT AVISplitter_Sample(LPVOID iface, IMediaSample * pSample) if (offset >= (DWORD)cbSrcStream) { FIXME("large offset\n"); - return S_OK; + hr = S_OK; + goto skip; } memcpy(&This->CurrentChunk, pbSrcStream + offset, sizeof(RIFFCHUNK)); @@ -132,7 +136,7 @@ static HRESULT AVISplitter_Sample(LPVOID iface, IMediaSample * pSample) case ckidJUNK: case aviFCC('i','d','x','1'): /* Index is not handled */ /* silently ignore */ - if (S_FALSE == AVISplitter_NextChunk(&This->CurrentChunkOffset, &This->CurrentChunk, &tStart, &tStop, pbSrcStream)) + if (S_FALSE == AVISplitter_NextChunk(&This->CurrentChunkOffset, &This->CurrentChunk, &tStart, &tStop, pbSrcStream, FALSE)) bMoreData = FALSE; continue; case ckidLIST: @@ -141,12 +145,16 @@ static HRESULT AVISplitter_Sample(LPVOID iface, IMediaSample * pSample) { /* FIXME: We only advanced to the first chunk inside the list without keeping track that we are in it. * This is not clean and the parser should be improved for that but it is enough for most AVI files. */ - This->CurrentChunkOffset = MEDIATIME_FROM_BYTES(BYTES_FROM_MEDIATIME(This->CurrentChunkOffset) + sizeof(RIFFLIST)); + if (S_FALSE == AVISplitter_NextChunk(&This->CurrentChunkOffset, &This->CurrentChunk, &tStart, &tStop, pbSrcStream, TRUE)) + { + bMoreData = FALSE; + continue; + } This->CurrentChunk = *(RIFFCHUNK*) (pbSrcStream + BYTES_FROM_MEDIATIME(This->CurrentChunkOffset-tStart)); offset_src = (long)BYTES_FROM_MEDIATIME(This->CurrentChunkOffset - tStart) + sizeof(RIFFCHUNK); break; } - else if (S_FALSE == AVISplitter_NextChunk(&This->CurrentChunkOffset, &This->CurrentChunk, &tStart, &tStop, pbSrcStream)) + else if (S_FALSE == AVISplitter_NextChunk(&This->CurrentChunkOffset, &This->CurrentChunk, &tStart, &tStop, pbSrcStream, FALSE)) bMoreData = FALSE; continue; default: @@ -168,7 +176,7 @@ static HRESULT AVISplitter_Sample(LPVOID iface, IMediaSample * pSample) break; default: FIXME("Skipping unknown chunk type: %s at file offset 0x%lx\n", debugstr_an((LPSTR)&This->CurrentChunk.fcc, 4), (DWORD)BYTES_FROM_MEDIATIME(This->CurrentChunkOffset)); - if (S_FALSE == AVISplitter_NextChunk(&This->CurrentChunkOffset, &This->CurrentChunk, &tStart, &tStop, pbSrcStream)) + if (S_FALSE == AVISplitter_NextChunk(&This->CurrentChunkOffset, &This->CurrentChunk, &tStart, &tStop, pbSrcStream, FALSE)) bMoreData = FALSE; continue; } @@ -180,7 +188,8 @@ static HRESULT AVISplitter_Sample(LPVOID iface, IMediaSample * pSample) if (streamId > This->Parser.cStreams) { ERR("Corrupted AVI file (contains stream id %d, but supposed to only have %ld streams)\n", streamId, This->Parser.cStreams); - return E_FAIL; + hr = E_FAIL; + break; } pOutputPin = (Parser_OutputPin *)This->Parser.ppPins[streamId + 1]; @@ -200,7 +209,7 @@ static HRESULT AVISplitter_Sample(LPVOID iface, IMediaSample * pSample) { TRACE("Skipping sending sample for stream %02d due to error (%lx)\n", streamId, hr); This->pCurrentSample = NULL; - if (S_FALSE == AVISplitter_NextChunk(&This->CurrentChunkOffset, &This->CurrentChunk, &tStart, &tStop, pbSrcStream)) + if (S_FALSE == AVISplitter_NextChunk(&This->CurrentChunkOffset, &This->CurrentChunk, &tStart, &tStop, pbSrcStream, FALSE)) bMoreData = FALSE; continue; } @@ -263,7 +272,7 @@ static HRESULT AVISplitter_Sample(LPVOID iface, IMediaSample * pSample) This->pCurrentSample = NULL; - if (S_FALSE == AVISplitter_NextChunk(&This->CurrentChunkOffset, &This->CurrentChunk, &tStart, &tStop, pbSrcStream)) + if (S_FALSE == AVISplitter_NextChunk(&This->CurrentChunkOffset, &This->CurrentChunk, &tStart, &tStop, pbSrcStream, FALSE)) bMoreData = FALSE; } else @@ -276,6 +285,38 @@ static HRESULT AVISplitter_Sample(LPVOID iface, IMediaSample * pSample) bMoreData = FALSE; } } + +skip: + if (tStop >= This->EndOfFile) + { + int i; + + TRACE("End of file reached\n"); + + for (i = 0; i < This->Parser.cStreams; i++) + { + IPin* ppin; + HRESULT hr; + + TRACE("Send End Of Stream to output pin %d\n", i); + + hr = IPin_ConnectedTo(This->Parser.ppPins[i+1], &ppin); + if (SUCCEEDED(hr)) + { + hr = IPin_EndOfStream(ppin); + IPin_Release(ppin); + } + if (FAILED(hr)) + { + ERR("%lx\n", hr); + break; + } + } + + /* Force the pullpin thread to stop */ + hr = S_FALSE; + } + return hr; } diff --git a/dlls/quartz/dsoundrender.c b/dlls/quartz/dsoundrender.c index 78e95b9ed3c..847b989f14b 100644 --- a/dlls/quartz/dsoundrender.c +++ b/dlls/quartz/dsoundrender.c @@ -58,7 +58,6 @@ typedef struct DSoundRenderImpl REFERENCE_TIME rtStreamStart; IReferenceClock * pClock; FILTER_INFO filterInfo; - IMediaEventSink * pEventSink; InputPin * pInputPin; IPin ** ppPins; @@ -550,7 +549,6 @@ static HRESULT WINAPI DSoundRender_QueryFilterInfo(IBaseFilter * iface, FILTER_I static HRESULT WINAPI DSoundRender_JoinFilterGraph(IBaseFilter * iface, IFilterGraph *pGraph, LPCWSTR pName) { - HRESULT hr; DSoundRenderImpl *This = (DSoundRenderImpl *)iface; TRACE("(%p/%p)->(%p, %s)\n", This, iface, pGraph, debugstr_w(pName)); @@ -562,12 +560,10 @@ static HRESULT WINAPI DSoundRender_JoinFilterGraph(IBaseFilter * iface, IFilterG else *This->filterInfo.achName = '\0'; This->filterInfo.pGraph = pGraph; /* NOTE: do NOT increase ref. count */ - - hr = IFilterGraph_QueryInterface(pGraph, &IID_IMediaEventSink, (LPVOID*)&This->pEventSink); } LeaveCriticalSection(&This->csFilter); - return hr; + return S_OK; } static HRESULT WINAPI DSoundRender_QueryVendorInfo(IBaseFilter * iface, LPWSTR *pVendorInfo) @@ -598,12 +594,21 @@ static const IBaseFilterVtbl DSoundRender_Vtbl = static HRESULT WINAPI DSoundRender_InputPin_EndOfStream(IPin * iface) { - /* FIXME: critical section */ InputPin* This = (InputPin*)iface; - + IMediaEventSink* pEventSink; + HRESULT hr; + TRACE("(%p/%p)->()\n", This, iface); - - return IMediaEventSink_Notify(((DSoundRenderImpl*)This->pin.pinInfo.pFilter)->pEventSink, EC_COMPLETE, S_OK, 0); + + hr = IFilterGraph_QueryInterface(((DSoundRenderImpl*)This->pin.pinInfo.pFilter)->filterInfo.pGraph, &IID_IMediaEventSink, (LPVOID*)&pEventSink); + if (SUCCEEDED(hr)) + { + /* FIXME: We should wait that all audio data has been played */ + hr = IMediaEventSink_Notify(pEventSink, EC_COMPLETE, S_OK, 0); + IMediaEventSink_Release(pEventSink); + } + + return hr; } static const IPinVtbl DSoundRender_InputPin_Vtbl = diff --git a/dlls/quartz/filtergraph.c b/dlls/quartz/filtergraph.c index 6d555929541..34c384e3b36 100644 --- a/dlls/quartz/filtergraph.c +++ b/dlls/quartz/filtergraph.c @@ -2995,30 +2995,35 @@ static HRESULT WINAPI MediaEventSink_Notify(IMediaEventSink *iface, long EventCo if ((EventCode == EC_COMPLETE) && This->HandleEcComplete) { - if (++This->EcCompleteCount == This->nRenderers) - { - evt.lEventCode = EC_COMPLETE; - evt.lParam1 = S_OK; - evt.lParam2 = 0; - EventsQueue_PutEvent(&This->evqueue, &evt); - if (!This->notif.disabled && This->notif.hWnd) - PostMessageW(This->notif.hWnd, This->notif.msg, 0, This->notif.instance); - This->CompletionStatus = EC_COMPLETE; - SetEvent(This->hEventCompletion); - } + TRACE("Process EC_COMPLETE notification\n"); + if (++This->EcCompleteCount == This->nRenderers) + { + evt.lEventCode = EC_COMPLETE; + evt.lParam1 = S_OK; + evt.lParam2 = 0; + TRACE("Send EC_COMPLETE to app\n"); + EventsQueue_PutEvent(&This->evqueue, &evt); + if (!This->notif.disabled && This->notif.hWnd) + { + TRACE("Send Window message\n"); + PostMessageW(This->notif.hWnd, This->notif.msg, 0, This->notif.instance); + } + This->CompletionStatus = EC_COMPLETE; + SetEvent(This->hEventCompletion); + } } else if ((EventCode == EC_REPAINT) && This->HandleEcRepaint) { - /* FIXME: Not handled yet */ + /* FIXME: Not handled yet */ } else { - evt.lEventCode = EventCode; - evt.lParam1 = EventParam1; - evt.lParam2 = EventParam2; - EventsQueue_PutEvent(&This->evqueue, &evt); - if (!This->notif.disabled && This->notif.hWnd) - PostMessageW(This->notif.hWnd, This->notif.msg, 0, This->notif.instance); + evt.lEventCode = EventCode; + evt.lParam1 = EventParam1; + evt.lParam2 = EventParam2; + EventsQueue_PutEvent(&This->evqueue, &evt); + if (!This->notif.disabled && This->notif.hWnd) + PostMessageW(This->notif.hWnd, This->notif.msg, 0, This->notif.instance); } LeaveCriticalSection(&This->evqueue.msg_crst); @@ -3059,11 +3064,11 @@ HRESULT FILTERGRAPH_create(IUnknown *pUnkOuter, LPVOID *ppObj) { fimpl->nFilters = 0; fimpl->filterCapacity = 0; fimpl->nameIndex = 1; - fimpl->hEventCompletion = CreateEventW(0, TRUE, FALSE,0); + fimpl->hEventCompletion = CreateEventW(0, TRUE, FALSE, 0); fimpl->HandleEcComplete = TRUE; fimpl->HandleEcRepaint = TRUE; fimpl->notif.hWnd = 0; - fimpl->notif.disabled = TRUE; + fimpl->notif.disabled = FALSE; fimpl->nRenderers = 0; fimpl->EcCompleteCount = 0; fimpl->state = State_Stopped; diff --git a/dlls/quartz/pin.c b/dlls/quartz/pin.c index 3d903e54e95..a53343f4782 100644 --- a/dlls/quartz/pin.c +++ b/dlls/quartz/pin.c @@ -1158,7 +1158,7 @@ static void CALLBACK PullPin_Thread_Process(ULONG_PTR iface) TRACE("Start\n"); - while (rtCurrent < This->rtStop) + while (rtCurrent < This->rtStop && hr == S_OK) { /* FIXME: to improve performance by quite a bit this should be changed * so that one sample is processed while one sample is fetched. However, diff --git a/dlls/quartz/tests/filtergraph.c b/dlls/quartz/tests/filtergraph.c index 969fa7d0851..b309343d7ff 100644 --- a/dlls/quartz/tests/filtergraph.c +++ b/dlls/quartz/tests/filtergraph.c @@ -51,6 +51,8 @@ static void rungraph() { HRESULT hr; IMediaControl* pmc; + IMediaEvent* pme; + HANDLE hEvent; hr = IGraphBuilder_QueryInterface(pgraph, &IID_IMediaControl, (LPVOID*)&pmc); ok(hr==S_OK, "Cannot get IMediaControl interface returned: %lx\n", hr); @@ -58,13 +60,23 @@ static void rungraph() hr = IMediaControl_Run(pmc); ok(hr==S_FALSE, "Cannot run the graph returned: %lx\n", hr); + hr = IGraphBuilder_QueryInterface(pgraph, &IID_IMediaEvent, (LPVOID*)&pme); + ok(hr==S_OK, "Cannot get IMediaEvent interface returned: %lx\n", hr); + + hr = IMediaEvent_GetEventHandle(pme, (OAEVENT*)&hEvent); + ok(hr==S_OK, "Cannot get event handle returned: %lx\n", hr); + + /* WaitForSingleObject(hEvent, INFINITE); */ Sleep(20000); + hr = IMediaControl_Release(pme); + ok(hr==2, "Releasing mediaevent returned: %lx\n", hr); + hr = IMediaControl_Stop(pmc); ok(hr==S_OK, "Cannot stop the graph returned: %lx\n", hr); hr = IMediaControl_Release(pmc); - ok(hr==1, "Releasing mediacontrol returned: %lx\n", hr); + ok(hr==1, "Releasing mediacontrol returned: %lx\n", hr); } static void releasefiltergraph() diff --git a/dlls/quartz/transform.c b/dlls/quartz/transform.c index e05be42e07a..ca857a1e0d4 100644 --- a/dlls/quartz/transform.c +++ b/dlls/quartz/transform.c @@ -495,6 +495,32 @@ static const IBaseFilterVtbl TransformFilter_Vtbl = TransformFilter_QueryVendorInfo }; +HRESULT WINAPI TransformFilter_InputPin_EndOfStream(IPin * iface) +{ + InputPin* This = (InputPin*) iface; + TransformFilterImpl* pTransform; + IPin* ppin; + HRESULT hr; + + TRACE("(%p)->()\n", iface); + + /* Since we process samples synchronously, just forward notification downstream */ + pTransform = (TransformFilterImpl*)This->pin.pinInfo.pFilter; + if (!pTransform) + hr = E_FAIL; + else + hr = IPin_ConnectedTo(pTransform->ppPins[1], &ppin); + if (SUCCEEDED(hr)) + { + hr = IPin_EndOfStream(ppin); + IPin_Release(ppin); + } + + if (FAILED(hr)) + ERR("%lx\n", hr); + return hr; +} + static const IPinVtbl TransformFilter_InputPin_Vtbl = { InputPin_QueryInterface, @@ -511,7 +537,7 @@ static const IPinVtbl TransformFilter_InputPin_Vtbl = IPinImpl_QueryAccept, IPinImpl_EnumMediaTypes, IPinImpl_QueryInternalConnections, - InputPin_EndOfStream, + TransformFilter_InputPin_EndOfStream, InputPin_BeginFlush, InputPin_EndFlush, InputPin_NewSegment diff --git a/dlls/quartz/videorenderer.c b/dlls/quartz/videorenderer.c index 65e476799e5..3c98af0b4cb 100644 --- a/dlls/quartz/videorenderer.c +++ b/dlls/quartz/videorenderer.c @@ -34,6 +34,7 @@ #include "windef.h" #include "winbase.h" #include "dshow.h" +#include "evcode.h" #include "strmif.h" #include "ddraw.h" @@ -635,6 +636,24 @@ static const IBaseFilterVtbl VideoRenderer_Vtbl = VideoRenderer_QueryVendorInfo }; +static HRESULT WINAPI VideoRenderer_InputPin_EndOfStream(IPin * iface) +{ + InputPin* This = (InputPin*)iface; + IMediaEventSink* pEventSink; + HRESULT hr; + + TRACE("(%p/%p)->()\n", This, iface); + + hr = IFilterGraph_QueryInterface(((VideoRendererImpl*)This->pin.pinInfo.pFilter)->filterInfo.pGraph, &IID_IMediaEventSink, (LPVOID*)&pEventSink); + if (SUCCEEDED(hr)) + { + hr = IMediaEventSink_Notify(pEventSink, EC_COMPLETE, S_OK, 0); + IMediaEventSink_Release(pEventSink); + } + + return hr; +} + static const IPinVtbl VideoRenderer_InputPin_Vtbl = { InputPin_QueryInterface, @@ -651,7 +670,7 @@ static const IPinVtbl VideoRenderer_InputPin_Vtbl = IPinImpl_QueryAccept, IPinImpl_EnumMediaTypes, IPinImpl_QueryInternalConnections, - InputPin_EndOfStream, + VideoRenderer_InputPin_EndOfStream, InputPin_BeginFlush, InputPin_EndFlush, InputPin_NewSegment diff --git a/dlls/quartz/waveparser.c b/dlls/quartz/waveparser.c index b508d905c22..eb885b1905c 100644 --- a/dlls/quartz/waveparser.c +++ b/dlls/quartz/waveparser.c @@ -97,7 +97,7 @@ static HRESULT WAVEParser_Sample(LPVOID iface, IMediaSample * pSample) { TRACE("Skipping sending sample due to error (%lx)\n", hr); This->pCurrentSample = NULL; - return hr; + break; } } @@ -167,6 +167,36 @@ static HRESULT WAVEParser_Sample(LPVOID iface, IMediaSample * pSample) offset_src += chunk_remaining_bytes; } + if (tStop >= This->EndOfFile) + { + int i; + + TRACE("End of file reached\n"); + + for (i = 0; i < This->Parser.cStreams; i++) + { + IPin* ppin; + HRESULT hr; + + TRACE("Send End Of Stream to output pin %d\n", i); + + hr = IPin_ConnectedTo(This->Parser.ppPins[i+1], &ppin); + if (SUCCEEDED(hr)) + { + hr = IPin_EndOfStream(ppin); + IPin_Release(ppin); + } + if (FAILED(hr)) + { + ERR("%lx\n", hr); + break; + } + } + + /* Force the pullpin thread to stop */ + hr = S_FALSE; + } + return hr; } -- 2.11.4.GIT