Handle End Of Stream notifications.
Some AVI Splitter fixes.
This commit is contained in:
parent
46ebd66796
commit
7dea79c487
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue