quartz: Make wave parser and mpeg splitter zero copy by getting rid of the seperate allocator for the output pin.

oldstable
Maarten Lankhorst 2008-04-22 13:57:11 -07:00 committed by Alexandre Julliard
parent ec124be823
commit 3a39805ed8
7 changed files with 407 additions and 417 deletions

View File

@ -813,7 +813,7 @@ static HRESULT AVISplitter_InitializeStreams(AVISplitterImpl *This)
static HRESULT AVISplitter_Disconnect(LPVOID iface);
/* FIXME: fix leaks on failure here */
static HRESULT AVISplitter_InputPin_PreConnect(IPin * iface, IPin * pConnectPin)
static HRESULT AVISplitter_InputPin_PreConnect(IPin * iface, IPin * pConnectPin, ALLOCATOR_PROPERTIES *props)
{
PullPin *This = (PullPin *)iface;
HRESULT hr;
@ -918,7 +918,8 @@ static HRESULT AVISplitter_InputPin_PreConnect(IPin * iface, IPin * pConnectPin)
IAsyncReader_Length(This->pReader, &total, &avail);
/* FIXME: AVIX files are added ("eXtended") beyond the "AVI" length, and thus won't be played here */
/* FIXME: AVIX files are extended beyond the FOURCC chunk "AVI ", and thus won't be played here,
* once I get one of the files I'll try to fix it */
if (hr == S_OK)
{
This->rtStart = pAviSplit->CurrentChunkOffset = MEDIATIME_FROM_BYTES(pos + sizeof(RIFFLIST));
@ -1060,7 +1061,7 @@ HRESULT AVISplitter_create(IUnknown * pUnkOuter, LPVOID * ppv)
This->streams = NULL;
This->oldindex = NULL;
hr = Parser_Create(&(This->Parser), &CLSID_AviSplitter, AVISplitter_Sample, AVISplitter_QueryAccept, AVISplitter_InputPin_PreConnect, AVISplitter_Cleanup, AVISplitter_Disconnect, NULL, NULL, NULL);
hr = Parser_Create(&(This->Parser), &CLSID_AviSplitter, AVISplitter_Sample, AVISplitter_QueryAccept, AVISplitter_InputPin_PreConnect, AVISplitter_Cleanup, AVISplitter_Disconnect, NULL, NULL, NULL, NULL);
if (FAILED(hr))
return hr;

View File

@ -67,10 +67,13 @@ typedef struct MPEGSplitterImpl
LONGLONG EndOfFile;
LONGLONG duration;
LONGLONG position;
DWORD skipbytes;
DWORD header_bytes;
DWORD remaining_bytes;
DWORD begin_offset;
BYTE header[4];
/* Whether we just seeked (or started playing) */
BOOL seek;
/* Seeking cache */
ULONG seek_entries;
struct seek_entry *seektable;
} MPEGSplitterImpl;
@ -118,7 +121,7 @@ static const DWORD tabsel_123[2][3][16] = {
static HRESULT parse_header(BYTE *header, LONGLONG *plen, LONGLONG *pduration)
{
LONGLONG duration = *pduration;
LONGLONG duration;
int bitrate_index, freq_index, mode_ext, emphasis, lsf = 1, mpeg1, layer, mode, padding, bitrate, length;
@ -157,192 +160,76 @@ static HRESULT parse_header(BYTE *header, LONGLONG *plen, LONGLONG *pduration)
duration = (ULONGLONG)10000000 * (ULONGLONG)(length) / (ULONGLONG)(bitrate/8);
*plen = length;
*pduration += duration;
if (pduration)
*pduration += duration;
return S_OK;
}
static void skip_data(BYTE** from, DWORD *flen, DWORD amount)
{
*flen -= amount;
if (!*flen)
*from = NULL;
else
*from += amount;
}
static HRESULT copy_data(IMediaSample *to, BYTE** from, DWORD *flen, DWORD amount)
{
HRESULT hr = S_OK;
BYTE *ptr = NULL;
DWORD oldlength = IMediaSample_GetActualDataLength(to);
hr = IMediaSample_SetActualDataLength(to, oldlength + amount);
if (FAILED(hr))
{
if (!oldlength || oldlength <= 4)
WARN("Could not set require length\n");
return hr;
}
IMediaSample_GetPointer(to, &ptr);
memcpy(ptr + oldlength, *from, amount);
skip_data(from, flen, amount);
return hr;
}
static HRESULT FillBuffer(MPEGSplitterImpl *This, BYTE** fbuf, DWORD *flen, IMediaSample *pCurrentSample)
static HRESULT FillBuffer(MPEGSplitterImpl *This, IMediaSample *pCurrentSample)
{
Parser_OutputPin * pOutputPin = (Parser_OutputPin*)This->Parser.ppPins[1];
LONGLONG length = 0;
HRESULT hr = S_OK;
DWORD dlen;
LONGLONG time = This->position, sampleduration = 0;
DWORD extrasamples = 2;
LONGLONG pos = BYTES_FROM_MEDIATIME(This->Parser.pInputPin->rtNext);
LONGLONG time = This->position;
HRESULT hr;
BYTE *fbuf = NULL;
DWORD len = IMediaSample_GetActualDataLength(pCurrentSample);
TRACE("Source length: %u, skip length: %u, remaining: %u\n", *flen, This->skipbytes, This->remaining_bytes);
/* Case where bytes are skipped */
if (This->skipbytes)
{
DWORD skip = min(This->skipbytes, *flen);
skip_data(fbuf, flen, skip);
This->skipbytes -= skip;
return S_OK;
}
/* Case where there is already an output sample being held */
if (This->remaining_bytes)
{
DWORD towrite = min(This->remaining_bytes, *flen);
LONGLONG foo;
hr = copy_data(pCurrentSample, fbuf, flen, towrite);
if (FAILED(hr))
{
WARN("Could not resize sample: %08x\n", hr);
return hr;
}
This->remaining_bytes -= towrite;
if (This->remaining_bytes)
return hr;
/* Restore the time in the time variable. This->position now points
* to the NEW timestamp which is slightly off
*/
IMediaSample_GetTime(pCurrentSample, &time, &foo);
/* Optimize: Try appending more samples to the stream */
goto out_append;
}
/* Special case, last source sample might (or might not have) had a header, and now we want to retrieve it */
dlen = IMediaSample_GetActualDataLength(pCurrentSample);
if (dlen > 0 && dlen < 4)
{
BYTE *header = NULL;
DWORD attempts = 0;
/* Shoot anyone with a small sample! */
assert(*flen >= 6);
hr = IMediaSample_GetPointer(pCurrentSample, &header);
if (SUCCEEDED(hr))
hr = IMediaSample_SetActualDataLength(pCurrentSample, 7);
if (FAILED(hr))
{
WARN("Could not resize sample: %08x\n", hr);
return hr;
}
memcpy(header + dlen, *fbuf, 6 - dlen);
while (FAILED(parse_header(header+attempts, &length, &This->position)) && attempts < dlen)
{
attempts++;
}
/* No header found */
if (attempts == dlen)
{
hr = IMediaSample_SetActualDataLength(pCurrentSample, 0);
return hr;
}
IMediaSample_SetActualDataLength(pCurrentSample, 4);
IMediaSample_SetTime(pCurrentSample, &time, &This->position);
/* Move header back to beginning */
if (attempts)
memmove(header, header+attempts, 4);
This->remaining_bytes = length - 4;
*flen -= (4 - dlen + attempts);
*fbuf += (4 - dlen + attempts);
return hr;
}
/* Destination sample should contain no data! But the source sample should */
assert(!dlen);
assert(*flen);
TRACE("Source length: %u\n", len);
IMediaSample_GetPointer(pCurrentSample, &fbuf);
/* Find the next valid header.. it <SHOULD> be right here */
while (*flen > 3 && FAILED(parse_header(*fbuf, &length, &This->position)))
{
skip_data(fbuf, flen, 1);
}
assert(parse_header(fbuf, &length, &This->position) == S_OK);
assert(length == len || length + 4 == len);
IMediaSample_SetActualDataLength(pCurrentSample, length);
/* Uh oh, no header found! */
if (*flen < 4)
if (length + 4 == len)
{
assert(!length);
hr = copy_data(pCurrentSample, fbuf, flen, *flen);
return hr;
PullPin *pin = This->Parser.pInputPin;
LONGLONG stop = BYTES_FROM_MEDIATIME(pin->rtStop);
hr = S_OK;
memcpy(This->header, fbuf + length, 4);
while (FAILED(hr = parse_header(This->header, &length, NULL)))
{
memmove(This->header, This->header+1, 3);
if (pos + 4 >= stop)
break;
IAsyncReader_SyncRead(pin->pReader, ++pos, 1, This->header + 3);
}
pin->rtNext = MEDIATIME_FROM_BYTES(pos);
if (SUCCEEDED(hr))
{
/* Remove 4 for the last header, which should hopefully work */
IMediaSample *sample = NULL;
LONGLONG rtSampleStart = pin->rtNext - MEDIATIME_FROM_BYTES(4);
LONGLONG rtSampleStop = rtSampleStart + MEDIATIME_FROM_BYTES(length + 4);
hr = IMemAllocator_GetBuffer(pin->pAlloc, &sample, NULL, NULL, 0);
if (rtSampleStop > pin->rtStop)
rtSampleStop = MEDIATIME_FROM_BYTES(ALIGNUP(BYTES_FROM_MEDIATIME(pin->rtStop), pin->cbAlign));
IMediaSample_SetTime(sample, &rtSampleStart, &rtSampleStop);
IMediaSample_SetPreroll(sample, 0);
IMediaSample_SetDiscontinuity(sample, 0);
IMediaSample_SetSyncPoint(sample, 1);
pin->rtCurrent = rtSampleStart;
pin->rtNext = rtSampleStop;
if (SUCCEEDED(hr))
hr = IAsyncReader_Request(pin->pReader, sample, 0);
if (FAILED(hr))
FIXME("o_Ox%08x\n", hr);
}
}
/* If not, we're presumably at the end of file */
TRACE("Media time : %u.%03u\n", (DWORD)(This->position/10000000), (DWORD)((This->position/10000)%1000));
IMediaSample_SetTime(pCurrentSample, &time, &This->position);
if (*flen < length)
{
/* Partial copy: Copy 4 bytes, the rest will be copied by the logic for This->remaining_bytes */
This->remaining_bytes = length - 4;
copy_data(pCurrentSample, fbuf, flen, 4);
return hr;
}
hr = copy_data(pCurrentSample, fbuf, flen, length);
if (FAILED(hr))
{
WARN("Couldn't set data size to %x%08x\n", (DWORD)(length >> 32), (DWORD)length);
This->skipbytes = length;
return hr;
}
out_append:
/* Optimize: Send multiple samples! */
while (extrasamples--)
{
if (*flen < 4)
break;
if (FAILED(parse_header(*fbuf, &length, &sampleduration)))
break;
if (length > *flen)
break;
if (FAILED(copy_data(pCurrentSample, fbuf, flen, length)))
break;
This->position += sampleduration;
sampleduration = 0;
IMediaSample_SetTime(pCurrentSample, &time, &This->position);
}
TRACE("Media time: %u.%03u\n", (DWORD)(This->position/10000000), (DWORD)((This->position/10000)%1000));
hr = OutputPin_SendSample(&pOutputPin->pin, pCurrentSample);
if (hr != S_OK)
@ -350,12 +237,9 @@ out_append:
if (hr != S_FALSE)
TRACE("Error sending sample (%x)\n", hr);
else
TRACE("S_FALSE (%d), holding\n", IMediaSample_GetActualDataLength(This->pCurrentSample));
return hr;
TRACE("S_FALSE (%d), holding\n", IMediaSample_GetActualDataLength(pCurrentSample));
}
IMediaSample_Release(pCurrentSample);
This->pCurrentSample = NULL;
return hr;
}
@ -378,74 +262,56 @@ static HRESULT MPEGSplitter_process_sample(LPVOID iface, IMediaSample * pSample,
hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
}
/* Flush occuring */
if (cbSrcStream == 0)
{
FIXME(".. Why do I need you?\n");
return S_OK;
}
/* trace removed for performance reasons */
/* TRACE("(%p), %llu -> %llu\n", pSample, tStart, tStop); */
/* Try to get rid of current sample, if any */
if (This->pCurrentSample && !This->skipbytes && !This->remaining_bytes && IMediaSample_GetActualDataLength(This->pCurrentSample) > 4)
if (This->pCurrentSample)
{
Parser_OutputPin * pOutputPin = (Parser_OutputPin*)This->Parser.ppPins[1];
IMediaSample *pCurrentSample = This->pCurrentSample;
HRESULT hr;
/* Unset advancement */
This->Parser.pInputPin->rtCurrent -= MEDIATIME_FROM_BYTES(cbSrcStream);
/* Requeue buffer */
hr = OutputPin_SendSample(&pOutputPin->pin, pCurrentSample);
if (hr != S_OK)
{
Sleep(10);
TRACE("Yuck!\n");
IMediaSample_AddRef(pSample);
IAsyncReader_Request(This->Parser.pInputPin->pReader, pSample, 0);
return hr;
}
IMediaSample_Release(This->pCurrentSample);
This->pCurrentSample = NULL;
This->Parser.pInputPin->rtCurrent += MEDIATIME_FROM_BYTES(cbSrcStream);
}
/* Now, try to find a new header */
while (cbSrcStream > 0)
hr = FillBuffer(This, pSample);
if (hr != S_OK)
{
if (!This->pCurrentSample)
{
if (FAILED(hr = OutputPin_GetDeliveryBuffer(&pOutputPin->pin, &This->pCurrentSample, NULL, NULL, 0)))
{
TRACE("Failed with hres: %08x!\n", hr);
break;
}
IMediaSample_SetTime(This->pCurrentSample, NULL, NULL);
if (FAILED(hr = IMediaSample_SetActualDataLength(This->pCurrentSample, 0)))
goto fail;
IMediaSample_SetSyncPoint(This->pCurrentSample, TRUE);
IMediaSample_SetDiscontinuity(This->pCurrentSample, This->seek);
IMediaSample_SetPreroll(This->pCurrentSample, (This->seek && This->position > 0));
This->seek = FALSE;
}
hr = FillBuffer(This, &pbSrcStream, &cbSrcStream, This->pCurrentSample);
if (hr == S_OK)
continue;
/* We still have our sample! Do damage control and send it next round */
fail:
if (hr != S_FALSE)
WARN("Failed with hres: %08x!\n", hr);
This->skipbytes += This->remaining_bytes;
This->remaining_bytes = 0;
This->Parser.pInputPin->rtCurrent = MEDIATIME_FROM_BYTES(BYTES_FROM_MEDIATIME(tStop) - cbSrcStream);
WARN("Failed with hres: %08x!\n", hr);
/* If set to S_FALSE we keep the sample, to transmit it next time */
if (hr != S_FALSE && This->pCurrentSample)
if (hr == S_FALSE)
{
IMediaSample_SetActualDataLength(This->pCurrentSample, 0);
IMediaSample_Release(This->pCurrentSample);
This->pCurrentSample = NULL;
This->pCurrentSample = pSample;
IMediaSample_AddRef(This->pCurrentSample);
}
/* Sample was rejected because of whatever reason (paused/flushing/etc), no need to terminate the processing */
if (hr == S_FALSE)
hr = S_OK;
break;
}
if (BYTES_FROM_MEDIATIME(tStop) >= This->EndOfFile || This->position >= This->Parser.mediaSeeking.llStop)
@ -615,11 +481,10 @@ static HRESULT MPEGSplitter_init_audio(MPEGSplitterImpl *This, const BYTE *heade
}
static HRESULT MPEGSplitter_pre_connect(IPin *iface, IPin *pConnectPin)
static HRESULT MPEGSplitter_pre_connect(IPin *iface, IPin *pConnectPin, ALLOCATOR_PROPERTIES *props)
{
PullPin *pPin = (PullPin *)iface;
MPEGSplitterImpl *This = (MPEGSplitterImpl*)pPin->pin.pinInfo.pFilter;
ALLOCATOR_PROPERTIES props;
HRESULT hr;
LONGLONG pos = 0; /* in bytes */
BYTE header[10];
@ -668,8 +533,8 @@ static HRESULT MPEGSplitter_pre_connect(IPin *iface, IPin *pConnectPin)
if (FAILED(hr))
return hr;
pos -= 4;
This->header_bytes = pos;
This->skipbytes = 0;
This->begin_offset = pos;
memcpy(This->header, header, 4);
This->seektable[0].bytepos = pos;
This->seektable[0].timepos = 0;
@ -688,13 +553,13 @@ static HRESULT MPEGSplitter_pre_connect(IPin *iface, IPin *pConnectPin)
{
WAVEFORMATEX *format = (WAVEFORMATEX*)amt.pbFormat;
props.cbAlign = 1;
props.cbPrefix = 0;
props->cbAlign = 1;
props->cbPrefix = 0;
/* Make the output buffer a multiple of the frame size */
props.cbBuffer = 0x4000 / format->nBlockAlign *
props->cbBuffer = 0x4000 / format->nBlockAlign *
format->nBlockAlign;
props.cBuffers = 1;
hr = Parser_AddPin(&(This->Parser), &piOutput, &props, &amt);
props->cBuffers = 2;
hr = Parser_AddPin(&(This->Parser), &piOutput, props, &amt);
}
if (FAILED(hr))
@ -712,7 +577,7 @@ static HRESULT MPEGSplitter_pre_connect(IPin *iface, IPin *pConnectPin)
if (!strncmp((char*)header+4, "TAG", 3))
This->EndOfFile -= 128;
This->Parser.pInputPin->rtStop = MEDIATIME_FROM_BYTES(This->EndOfFile);
This->Parser.pInputPin->rtStart = This->Parser.pInputPin->rtCurrent = MEDIATIME_FROM_BYTES(This->header_bytes);
This->Parser.pInputPin->rtStart = This->Parser.pInputPin->rtCurrent = MEDIATIME_FROM_BYTES(This->begin_offset);
/* http://mpgedit.org/mpgedit/mpeg_format/mpeghdr.htm has a whole read up on audio headers */
while (pos + 3 < This->EndOfFile)
@ -773,7 +638,6 @@ static HRESULT MPEGSplitter_pre_connect(IPin *iface, IPin *pConnectPin)
default:
break;
}
This->remaining_bytes = 0;
This->position = 0;
return hr;
@ -789,7 +653,6 @@ static HRESULT MPEGSplitter_cleanup(LPVOID iface)
IMediaSample_Release(This->pCurrentSample);
This->pCurrentSample = NULL;
This->remaining_bytes = This->skipbytes = 0;
return S_OK;
}
@ -820,11 +683,11 @@ static HRESULT MPEGSplitter_seek(IBaseFilter *iface)
timepos = This->seektable[newpos / SEEK_INTERVAL].timepos;
hr = IAsyncReader_SyncRead(pPin->pReader, bytepos, 4, header);
while (timepos < newpos && bytepos + 3 < This->EndOfFile)
while (bytepos + 3 < This->EndOfFile)
{
LONGLONG length = 0;
hr = IAsyncReader_SyncRead(pPin->pReader, bytepos, 4, header);
if (hr != S_OK)
if (hr != S_OK || timepos >= newpos)
break;
while (parse_header(header, &length, &timepos) && bytepos + 3 < This->EndOfFile)
@ -851,6 +714,7 @@ static HRESULT MPEGSplitter_seek(IBaseFilter *iface)
/* Make sure this is done while stopped, BeginFlush takes care of this */
EnterCriticalSection(&This->Parser.csFilter);
memcpy(This->header, header, 4);
IPin_ConnectedTo(This->Parser.ppPins[1], &victim);
if (victim)
{
@ -871,12 +735,60 @@ static HRESULT MPEGSplitter_seek(IBaseFilter *iface)
return hr;
}
static HRESULT MPEGSplitter_destroy(LPVOID iface)
static HRESULT MPEGSplitter_disconnect(LPVOID iface)
{
/* TODO: Find memory leaks etc */
return S_OK;
}
static HRESULT MPEGSplitter_first_request(LPVOID iface)
{
MPEGSplitterImpl *This = (MPEGSplitterImpl*)iface;
PullPin *pin = This->Parser.pInputPin;
HRESULT hr;
LONGLONG length;
IMediaSample *sample;
TRACE("Seeking? %d\n", This->seek);
assert(parse_header(This->header, &length, NULL) == S_OK);
if (pin->rtCurrent >= pin->rtStop)
{
/* Last sample has already been queued, request nothing more */
FIXME("Done!\n");
return S_OK;
}
hr = IMemAllocator_GetBuffer(pin->pAlloc, &sample, NULL, NULL, 0);
pin->rtNext = pin->rtCurrent;
if (SUCCEEDED(hr))
{
LONGLONG rtSampleStart = pin->rtNext;
/* Add 4 for the next header, which should hopefully work */
LONGLONG rtSampleStop = rtSampleStart + MEDIATIME_FROM_BYTES(length + 4);
if (rtSampleStop > pin->rtStop)
rtSampleStop = MEDIATIME_FROM_BYTES(ALIGNUP(BYTES_FROM_MEDIATIME(pin->rtStop), pin->cbAlign));
hr = IMediaSample_SetTime(sample, &rtSampleStart, &rtSampleStop);
pin->rtCurrent = pin->rtNext;
pin->rtNext = rtSampleStop;
IMediaSample_SetPreroll(sample, FALSE);
IMediaSample_SetDiscontinuity(sample, This->seek);
IMediaSample_SetSyncPoint(sample, 1);
This->seek = 0;
hr = IAsyncReader_Request(pin->pReader, sample, 0);
}
if (FAILED(hr))
ERR("Horsemen of the apocalypse came to bring error 0x%08x\n", hr);
return hr;
}
HRESULT MPEGSplitter_create(IUnknown * pUnkOuter, LPVOID * ppv)
{
MPEGSplitterImpl *This;
@ -902,13 +814,13 @@ HRESULT MPEGSplitter_create(IUnknown * pUnkOuter, LPVOID * ppv)
}
This->seek_entries = 64;
hr = Parser_Create(&(This->Parser), &CLSID_MPEG1Splitter, MPEGSplitter_process_sample, MPEGSplitter_query_accept, MPEGSplitter_pre_connect, MPEGSplitter_cleanup, MPEGSplitter_destroy, NULL, MPEGSplitter_seek, NULL);
hr = Parser_Create(&(This->Parser), &CLSID_MPEG1Splitter, MPEGSplitter_process_sample, MPEGSplitter_query_accept, MPEGSplitter_pre_connect, MPEGSplitter_cleanup, MPEGSplitter_disconnect, MPEGSplitter_first_request, NULL, MPEGSplitter_seek, NULL);
if (FAILED(hr))
{
CoTaskMemFree(This);
return hr;
}
This->seek = TRUE;
This->seek = 1;
/* Note: This memory is managed by the parser filter once created */
*ppv = (LPVOID)This;

View File

@ -53,7 +53,7 @@ static inline ParserImpl *impl_from_IMediaSeeking( IMediaSeeking *iface )
}
HRESULT Parser_Create(ParserImpl* pParser, const CLSID* pClsid, PFN_PROCESS_SAMPLE fnProcessSample, PFN_QUERY_ACCEPT fnQueryAccept, PFN_PRE_CONNECT fnPreConnect, PFN_CLEANUP fnCleanup, PFN_DISCONNECT fnDisconnect, CHANGEPROC stop, CHANGEPROC current, CHANGEPROC rate)
HRESULT Parser_Create(ParserImpl* pParser, const CLSID* pClsid, PFN_PROCESS_SAMPLE fnProcessSample, PFN_QUERY_ACCEPT fnQueryAccept, PFN_PRE_CONNECT fnPreConnect, PFN_CLEANUP fnCleanup, PFN_DISCONNECT fnDisconnect, REQUESTPROC fnRequest, CHANGEPROC stop, CHANGEPROC current, CHANGEPROC rate)
{
HRESULT hr;
PIN_INFO piInput;
@ -91,7 +91,7 @@ HRESULT Parser_Create(ParserImpl* pParser, const CLSID* pClsid, PFN_PROCESS_SAMP
MediaSeekingImpl_Init((IBaseFilter*)pParser, stop, current, rate, &pParser->mediaSeeking, &pParser->csFilter);
pParser->mediaSeeking.lpVtbl = &Parser_Seeking_Vtbl;
hr = PullPin_Construct(&Parser_InputPin_Vtbl, &piInput, fnProcessSample, (LPVOID)pParser, fnQueryAccept, fnCleanup, NULL, &pParser->csFilter, (IPin **)&pParser->pInputPin);
hr = PullPin_Construct(&Parser_InputPin_Vtbl, &piInput, fnProcessSample, (LPVOID)pParser, fnQueryAccept, fnCleanup, fnRequest, &pParser->csFilter, (IPin **)&pParser->pInputPin);
if (SUCCEEDED(hr))
{
@ -489,6 +489,8 @@ HRESULT Parser_AddPin(ParserImpl * This, const PIN_INFO * piOutput, ALLOCATOR_PR
pin->dwSamplesProcessed = 0;
pin->pin.pin.pUserData = (LPVOID)This->ppPins[This->cStreams + 1];
pin->pin.pin.pinInfo.pFilter = (LPVOID)This;
pin->pin.custom_allocator = 1;
This->cStreams++;
CoTaskMemFree(ppOldPins);
}
@ -649,6 +651,19 @@ static HRESULT WINAPI Parser_OutputPin_EnumMediaTypes(IPin * iface, IEnumMediaTy
return IEnumMediaTypesImpl_Construct(&emd, ppEnum);
}
static HRESULT WINAPI Parser_OutputPin_Connect(IPin * iface, IPin * pReceivePin, const AM_MEDIA_TYPE * pmt)
{
Parser_OutputPin *This = (Parser_OutputPin *)iface;
ParserImpl *parser = (ParserImpl *)This->pin.pin.pinInfo.pFilter;
/* Set the allocator to our input pin's */
EnterCriticalSection(This->pin.pin.pCritSec);
This->pin.alloc = parser->pInputPin->pAlloc;
LeaveCriticalSection(This->pin.pin.pCritSec);
return OutputPin_Connect(iface, pReceivePin, pmt);
}
static HRESULT Parser_OutputPin_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt)
{
Parser_OutputPin *This = (Parser_OutputPin *)iface;
@ -664,7 +679,7 @@ static const IPinVtbl Parser_OutputPin_Vtbl =
Parser_OutputPin_QueryInterface,
IPinImpl_AddRef,
Parser_OutputPin_Release,
OutputPin_Connect,
Parser_OutputPin_Connect,
OutputPin_ReceiveConnection,
OutputPin_Disconnect,
IPinImpl_ConnectedTo,
@ -692,6 +707,7 @@ static HRESULT WINAPI Parser_PullPin_Disconnect(IPin * iface)
{
if (This->pConnectedTo)
{
PullPin *ppin = (PullPin *)This;
FILTER_STATE state;
ParserImpl *Parser = (ParserImpl *)This->pinInfo.pFilter;
@ -701,6 +717,8 @@ static HRESULT WINAPI Parser_PullPin_Disconnect(IPin * iface)
{
IPin_Release(This->pConnectedTo);
This->pConnectedTo = NULL;
if (FAILED(hr = IMemAllocator_Decommit(ppin->pAlloc)))
ERR("Allocator decommit failed with error %x. Possible memory leak\n", hr);
hr = Parser_RemoveOutputPins((ParserImpl *)This->pinInfo.pFilter);
}
else
@ -738,7 +756,7 @@ static const IPinVtbl Parser_InputPin_Vtbl =
PullPin_QueryInterface,
IPinImpl_AddRef,
PullPin_Release,
OutputPin_Connect,
InputPin_Connect,
Parser_PullPin_ReceiveConnection,
Parser_PullPin_Disconnect,
IPinImpl_ConnectedTo,

View File

@ -22,7 +22,7 @@ typedef struct ParserImpl ParserImpl;
typedef HRESULT (*PFN_PROCESS_SAMPLE) (LPVOID iface, IMediaSample * pSample, DWORD_PTR cookie);
typedef HRESULT (*PFN_QUERY_ACCEPT) (LPVOID iface, const AM_MEDIA_TYPE * pmt);
typedef HRESULT (*PFN_PRE_CONNECT) (IPin * iface, IPin * pConnectPin);
typedef HRESULT (*PFN_PRE_CONNECT) (IPin * iface, IPin * pConnectPin, ALLOCATOR_PROPERTIES *prop);
typedef HRESULT (*PFN_CLEANUP) (LPVOID iface);
typedef HRESULT (*PFN_DISCONNECT) (LPVOID iface);
@ -57,4 +57,4 @@ typedef struct Parser_OutputPin
HRESULT Parser_AddPin(ParserImpl * This, const PIN_INFO * piOutput, ALLOCATOR_PROPERTIES * props, const AM_MEDIA_TYPE * amt);
HRESULT Parser_Create(ParserImpl*, const CLSID*, PFN_PROCESS_SAMPLE, PFN_QUERY_ACCEPT, PFN_PRE_CONNECT,
PFN_CLEANUP, PFN_DISCONNECT, CHANGEPROC stop, CHANGEPROC current, CHANGEPROC rate);
PFN_CLEANUP, PFN_DISCONNECT, REQUESTPROC, CHANGEPROC stop, CHANGEPROC current, CHANGEPROC rate);

View File

@ -182,7 +182,7 @@ static HRESULT OutputPin_ConnectSpecific(IPin * iface, IPin * pReceivePin, const
This->pMemInputPin = NULL;
hr = IPin_QueryInterface(pReceivePin, &IID_IMemInputPin, (LPVOID)&This->pMemInputPin);
if (SUCCEEDED(hr))
if (SUCCEEDED(hr) && !This->custom_allocator)
{
hr = IMemInputPin_GetAllocator(This->pMemInputPin, &pMemAlloc);
@ -193,7 +193,7 @@ static HRESULT OutputPin_ConnectSpecific(IPin * iface, IPin * pReceivePin, const
if (SUCCEEDED(hr))
{
hr = IMemInputPin_NotifyAllocator(This->pMemInputPin, pMemAlloc, FALSE);
hr = IMemInputPin_NotifyAllocator(This->pMemInputPin, pMemAlloc, This->readonly);
}
}
@ -203,6 +203,15 @@ static HRESULT OutputPin_ConnectSpecific(IPin * iface, IPin * pReceivePin, const
if (pMemAlloc)
IMemAllocator_Release(pMemAlloc);
}
else if (SUCCEEDED(hr))
{
if (This->alloc)
{
hr = IMemInputPin_NotifyAllocator(This->pMemInputPin, This->alloc, This->readonly);
}
else
hr = VFW_E_NO_ALLOCATOR;
}
/* break connection if we couldn't get the allocator */
if (FAILED(hr))
@ -272,6 +281,12 @@ static HRESULT OutputPin_Init(const IPinVtbl *OutputPin_Vtbl, const PIN_INFO * p
/* Output pin attributes */
pPinImpl->pMemInputPin = NULL;
pPinImpl->pConnectSpecific = OutputPin_ConnectSpecific;
/* If custom_allocator is set, you will need to specify an allocator
* in the alloc member of the struct before an output pin can connect
*/
pPinImpl->custom_allocator = 0;
pPinImpl->alloc = NULL;
pPinImpl->readonly = FALSE;
if (props)
{
pPinImpl->allocProps = *props;
@ -741,6 +756,9 @@ HRESULT WINAPI MemInputPin_NotifyAllocator(IMemInputPin * iface, IMemAllocator *
TRACE("(%p/%p)->(%p, %d)\n", This, iface, pAllocator, bReadOnly);
if (bReadOnly)
FIXME("Read only flag not handled yet!\n");
if (This->pAllocator)
IMemAllocator_Release(This->pAllocator);
This->pAllocator = pAllocator;
@ -1115,11 +1133,12 @@ HRESULT OutputPin_DeliverNewSegment(OutputPin * This, REFERENCE_TIME tStart, REF
HRESULT OutputPin_CommitAllocator(OutputPin * This)
{
HRESULT hr;
HRESULT hr = S_OK;
TRACE("(%p)->()\n", This);
EnterCriticalSection(This->pin.pCritSec);
if (!This->custom_allocator)
{
if (!This->pin.pConnectedTo || !This->pMemInputPin)
hr = VFW_E_NOT_CONNECTED;
@ -1137,7 +1156,7 @@ HRESULT OutputPin_CommitAllocator(OutputPin * This)
}
}
LeaveCriticalSection(This->pin.pCritSec);
return hr;
}
@ -1151,7 +1170,7 @@ HRESULT OutputPin_DeliverDisconnect(OutputPin * This)
{
if (!This->pin.pConnectedTo || !This->pMemInputPin)
hr = VFW_E_NOT_CONNECTED;
else
else if (!This->custom_allocator)
{
IMemAllocator * pAlloc = NULL;
@ -1166,6 +1185,13 @@ HRESULT OutputPin_DeliverDisconnect(OutputPin * This)
if (SUCCEEDED(hr))
hr = IPin_Disconnect(This->pin.pConnectedTo);
}
else /* Kill the allocator! */
{
hr = IMemInputPin_NotifyAllocator(This->pMemInputPin, NULL, 0);
if (SUCCEEDED(hr))
hr = IPin_Disconnect(This->pin.pConnectedTo);
}
}
LeaveCriticalSection(This->pin.pCritSec);
@ -1248,6 +1274,13 @@ HRESULT WINAPI PullPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const
EnterCriticalSection(This->pin.pCritSec);
{
ALLOCATOR_PROPERTIES props;
props.cBuffers = 3;
props.cbBuffer = 64 * 1024; /* 64k bytes */
props.cbAlign = 1;
props.cbPrefix = 0;
if (This->pin.pConnectedTo)
hr = VFW_E_ALREADY_CONNECTED;
@ -1273,19 +1306,14 @@ HRESULT WINAPI PullPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const
hr = IPin_QueryInterface(pReceivePin, &IID_IAsyncReader, (LPVOID *)&This->pReader);
}
if (SUCCEEDED(hr))
{
ALLOCATOR_PROPERTIES props;
props.cBuffers = 3;
props.cbBuffer = 64 * 1024; /* 64k bytes */
props.cbAlign = 1;
props.cbPrefix = 0;
hr = IAsyncReader_RequestAllocator(This->pReader, NULL, &props, &This->pAlloc);
}
if (SUCCEEDED(hr) && This->fnPreConnect)
{
hr = This->fnPreConnect(iface, pReceivePin);
hr = This->fnPreConnect(iface, pReceivePin, &props);
}
if (SUCCEEDED(hr))
{
hr = IAsyncReader_RequestAllocator(This->pReader, NULL, &props, &This->pAlloc);
}
if (SUCCEEDED(hr))
@ -1293,8 +1321,11 @@ HRESULT WINAPI PullPin_ReceiveConnection(IPin * iface, IPin * pReceivePin, const
CopyMediaType(&This->pin.mtCurrent, pmt);
This->pin.pConnectedTo = pReceivePin;
IPin_AddRef(pReceivePin);
hr = IMemAllocator_Commit(This->pAlloc);
}
else
if (FAILED(hr))
{
if (This->pReader)
IAsyncReader_Release(This->pReader);
@ -1387,10 +1418,12 @@ static HRESULT PullPin_Standard_Request(PullPin *This, BOOL start)
This->rtCurrent = This->rtNext;
This->rtNext = rtSampleStop;
}
if (SUCCEEDED(hr))
hr = IAsyncReader_Request(This->pReader, sample, 0);
if (SUCCEEDED(hr))
hr = IAsyncReader_Request(This->pReader, sample, 0);
}
if (FAILED(hr))
FIXME("Failed to queue sample : %08x\n", hr);
return hr;
}
@ -1398,6 +1431,7 @@ static HRESULT PullPin_Standard_Request(PullPin *This, BOOL start)
static void CALLBACK PullPin_Flush(PullPin *This)
{
IMediaSample *pSample;
TRACE("Flushing!\n");
EnterCriticalSection(This->pin.pCritSec);
if (This->pReader)
@ -1413,6 +1447,7 @@ static void CALLBACK PullPin_Flush(PullPin *This)
if (!pSample)
break;
assert(!IMediaSample_GetActualDataLength(pSample));
if (This->fnCustomRequest)
This->fnSampleProc(This->pin.pUserData, pSample, dwUser);
@ -1427,7 +1462,6 @@ static void CALLBACK PullPin_Flush(PullPin *This)
static void CALLBACK PullPin_Thread_Process(PullPin *This)
{
HRESULT hr;
BOOL rejected = FALSE;
IMediaSample * pSample = NULL;
ALLOCATOR_PROPERTIES allocProps;
@ -1456,6 +1490,9 @@ static void CALLBACK PullPin_Thread_Process(PullPin *This)
else
hr = This->fnCustomRequest(This->pin.pUserData);
if (FAILED(hr))
ERR("Request error: %x\n", hr);
do
{
DWORD_PTR dwUser;
@ -1464,45 +1501,39 @@ static void CALLBACK PullPin_Thread_Process(PullPin *This)
hr = IAsyncReader_WaitForNext(This->pReader, 10000, &pSample, &dwUser);
if (FAILED(hr))
ERR("Queueing error: %x\n", hr);
if (pSample)
{
if (!This->fnCustomRequest)
hr = PullPin_Standard_Request(This, FALSE);
else
hr = This->fnCustomRequest(This->pin.pUserData);
}
/* Calling fnCustomRequest is not specifically useful here: It can be handled inside fnSampleProc */
if (pSample && !This->fnCustomRequest)
hr = PullPin_Standard_Request(This, FALSE);
/* Return an empty sample on error to the implementation in case it does custom parsing, so it knows it's gone */
if (SUCCEEDED(hr) || This->fnCustomRequest)
if (SUCCEEDED(hr) || (This->fnCustomRequest && pSample))
{
REFERENCE_TIME rtStart, rtStop;
BOOL rejected;
IMediaSample_GetTime(pSample, &rtStart, &rtStop);
do
{
hr = This->fnSampleProc(This->pin.pUserData, pSample, dwUser);
rejected = FALSE;
if (This->fnCustomRequest)
break;
if (!This->fnCustomRequest)
rejected = FALSE;
if (This->rtCurrent == rtStart)
{
if (This->rtCurrent == rtStart)
{
rejected = TRUE;
TRACE("DENIED!\n");
Sleep(10);
/* Maybe it's transient? */
}
/* rtNext = rtCurrent, because the next sample is already queued */
else if (rtStop != This->rtCurrent && rtStop < This->rtStop)
{
WARN("Position changed! rtStop: %u, rtCurrent: %u\n", (DWORD)BYTES_FROM_MEDIATIME(rtStop), (DWORD)BYTES_FROM_MEDIATIME(This->rtCurrent));
PullPin_Flush(This);
hr = PullPin_Standard_Request(This, TRUE);
}
rejected = TRUE;
TRACE("DENIED!\n");
Sleep(10);
/* Maybe it's transient? */
}
/* rtNext = rtCurrent, because the next sample is already queued */
else if (rtStop != This->rtCurrent && rtStop < This->rtStop)
{
WARN("Position changed! rtStop: %u, rtCurrent: %u\n", (DWORD)BYTES_FROM_MEDIATIME(rtStop), (DWORD)BYTES_FROM_MEDIATIME(This->rtCurrent));
PullPin_Flush(This);
hr = PullPin_Standard_Request(This, TRUE);
}
} while (rejected && (This->rtCurrent < This->rtStop && hr == S_OK && !This->stop_playback));
}
@ -1551,13 +1582,8 @@ static void CALLBACK PullPin_Thread_Stop(PullPin *This)
EnterCriticalSection(This->pin.pCritSec);
{
HRESULT hr;
CloseHandle(This->hThread);
This->hThread = NULL;
if (FAILED(hr = IMemAllocator_Decommit(This->pAlloc)))
ERR("Allocator decommit failed with error %x. Possible memory leak\n", hr);
SetEvent(This->hEventStateChanged);
}
LeaveCriticalSection(This->pin.pCritSec);
@ -1624,8 +1650,6 @@ HRESULT PullPin_InitProcessing(PullPin * This)
if (SUCCEEDED(hr))
{
hr = IMemAllocator_Commit(This->pAlloc);
SetEvent(This->hEventStateChanged);
/* If assert fails, that means a command was not processed before the thread previously terminated */
}
@ -1777,6 +1801,32 @@ HRESULT WINAPI PullPin_EndFlush(IPin * iface)
return S_OK;
}
HRESULT WINAPI PullPin_Disconnect(IPin *iface)
{
HRESULT hr;
PullPin *This = (PullPin *)iface;
TRACE("()\n");
EnterCriticalSection(This->pin.pCritSec);
{
if (FAILED(hr = IMemAllocator_Decommit(This->pAlloc)))
ERR("Allocator decommit failed with error %x. Possible memory leak\n", hr);
if (This->pin.pConnectedTo)
{
IPin_Release(This->pin.pConnectedTo);
This->pin.pConnectedTo = NULL;
hr = S_OK;
}
else
hr = S_FALSE;
}
LeaveCriticalSection(This->pin.pCritSec);
return hr;
}
HRESULT WINAPI PullPin_NewSegment(IPin * iface, REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
{
newsegmentargs args;
@ -1796,7 +1846,7 @@ static const IPinVtbl PullPin_Vtbl =
PullPin_Release,
InputPin_Connect,
PullPin_ReceiveConnection,
IPinImpl_Disconnect,
PullPin_Disconnect,
IPinImpl_ConnectedTo,
IPinImpl_ConnectionMediaType,
IPinImpl_QueryPinInfo,

View File

@ -36,8 +36,10 @@ typedef HRESULT (* QUERYACCEPTPROC)(LPVOID userdata, const AM_MEDIA_TYPE * pmt);
/* This function is called prior to finalizing a connection with
* another pin and can be used to get things from the other pin
* like IMemInput interfaces.
*
* props contains some defaults, but you can safely override them to your liking
*/
typedef HRESULT (* PRECONNECTPROC)(IPin * iface, IPin * pConnectPin);
typedef HRESULT (* PRECONNECTPROC)(IPin * iface, IPin * pConnectPin, ALLOCATOR_PROPERTIES *props);
/* This function is called whenever a cleanup operation has to occur,
* this is usually after a flush, seek, or end of stream notification.
@ -57,6 +59,9 @@ typedef HRESULT (* CLEANUPPROC) (LPVOID userdata);
*/
typedef HRESULT (* REQUESTPROC) (LPVOID userdata);
#define ALIGNDOWN(value,boundary) ((value)/(boundary)*(boundary))
#define ALIGNUP(value,boundary) (ALIGNDOWN((value)+(boundary)-1, (boundary)))
typedef struct IPinImpl
{
const struct IPinVtbl * lpVtbl;
@ -92,6 +97,9 @@ typedef struct OutputPin
IMemInputPin * pMemInputPin;
HRESULT (* pConnectSpecific)(IPin * iface, IPin * pReceiver, const AM_MEDIA_TYPE * pmt);
BOOL custom_allocator;
IMemAllocator *alloc;
BOOL readonly;
ALLOCATOR_PROPERTIES allocProps;
} OutputPin;

View File

@ -76,139 +76,95 @@ static HRESULT WAVEParser_Sample(LPVOID iface, IMediaSample * pSample, DWORD_PTR
{
WAVEParserImpl *This = (WAVEParserImpl *)iface;
LPBYTE pbSrcStream = NULL;
long cbSrcStream = 0;
ULONG cbSrcStream = 0;
REFERENCE_TIME tStart, tStop;
HRESULT hr;
BOOL bMoreData = TRUE;
Parser_OutputPin * pOutputPin;
BYTE * pbDstStream;
long cbDstStream;
long chunk_remaining_bytes = 0;
long offset_src = 0;
hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
IMediaSample *newsample = NULL;
Parser_OutputPin *pOutputPin;
PullPin *pin = This->Parser.pInputPin;
IMediaSample_GetPointer(pSample, &pbSrcStream);
hr = IMediaSample_GetTime(pSample, &tStart, &tStop);
cbSrcStream = IMediaSample_GetActualDataLength(pSample);
assert(BYTES_FROM_MEDIATIME(tStop - tStart) == cbSrcStream);
/* Flush occuring */
if (cbSrcStream == 0)
{
TRACE(".. Why do I need you?\n");
return S_OK;
}
pOutputPin = (Parser_OutputPin *)This->Parser.ppPins[1];
/* Try to get rid of the current sample in case we had a S_FALSE last time */
if (This->pCurrentSample && (IMediaSample_GetActualDataLength(This->pCurrentSample) == IMediaSample_GetSize(This->pCurrentSample)))
if (This->pCurrentSample)
{
HRESULT hr;
Parser_OutputPin * pOutputPin = (Parser_OutputPin*)This->Parser.ppPins[1];
IMediaSample *pCurrentSample = This->pCurrentSample;
/* Unset advancement */
This->Parser.pInputPin->rtCurrent -= MEDIATIME_FROM_BYTES(cbSrcStream);
hr = OutputPin_SendSample(&pOutputPin->pin, This->pCurrentSample);
/* Requeue buffer */
hr = OutputPin_SendSample(&pOutputPin->pin, pCurrentSample);
if (hr != S_OK)
{
Sleep(10);
TRACE("Requeueing!\n");
IMediaSample_AddRef(pSample);
IAsyncReader_Request(This->Parser.pInputPin->pReader, pSample, 0);
return hr;
}
IMediaSample_Release(This->pCurrentSample);
This->pCurrentSample = NULL;
This->Parser.pInputPin->rtCurrent += MEDIATIME_FROM_BYTES(cbSrcStream);
}
if (tStop < This->StartOfFile)
return S_OK;
if (SUCCEEDED(hr))
hr = IMemAllocator_GetBuffer(pin->pAlloc, &newsample, NULL, NULL, 0);
if (tStart < This->StartOfFile)
offset_src = BYTES_FROM_MEDIATIME(This->StartOfFile - tStart);
while (bMoreData)
if (SUCCEEDED(hr))
{
if (!This->pCurrentSample)
LONGLONG rtSampleStart = pin->rtNext;
/* Add 4 for the next header, which should hopefully work */
LONGLONG rtSampleStop = rtSampleStart + MEDIATIME_FROM_BYTES(IMediaSample_GetSize(newsample));
if (rtSampleStop > pin->rtStop)
rtSampleStop = MEDIATIME_FROM_BYTES(ALIGNUP(BYTES_FROM_MEDIATIME(pin->rtStop), pin->cbAlign));
hr = IMediaSample_SetTime(newsample, &rtSampleStart, &rtSampleStop);
pin->rtCurrent = pin->rtNext;
pin->rtNext = rtSampleStop;
IMediaSample_SetPreroll(newsample, 0);
IMediaSample_SetDiscontinuity(newsample, 0);
IMediaSample_SetSyncPoint(newsample, 1);
hr = IAsyncReader_Request(pin->pReader, newsample, 0);
}
if (SUCCEEDED(hr))
{
REFERENCE_TIME tAviStart, tAviStop;
IMediaSample_SetSyncPoint(pSample, TRUE);
pOutputPin->dwSamplesProcessed++;
tAviStart = bytepos_to_duration(This, tStart);
tAviStop = bytepos_to_duration(This, tStart + MEDIATIME_FROM_BYTES(IMediaSample_GetActualDataLength(pSample)));
IMediaSample_SetTime(pSample, &tAviStart, &tAviStop);
hr = OutputPin_SendSample(&pOutputPin->pin, pSample);
if (hr != S_OK && hr != VFW_E_NOT_CONNECTED && hr != S_FALSE)
ERR("Error sending sample (%x)\n", hr);
if (hr == S_FALSE)
{
/* cache media sample until it is ready to be dispatched
* (i.e. we reach the end of the chunk) */
hr = OutputPin_GetDeliveryBuffer(&pOutputPin->pin, &This->pCurrentSample, NULL, NULL, 0);
if (SUCCEEDED(hr))
{
hr = IMediaSample_SetActualDataLength(This->pCurrentSample, 0);
assert(hr == S_OK);
}
else
{
TRACE("Skipping sending sample due to error (%x)\n", hr);
This->pCurrentSample = NULL;
break;
}
This->pCurrentSample = pSample;
IMediaSample_AddRef(pSample);
hr = S_OK;
}
hr = IMediaSample_GetPointer(This->pCurrentSample, &pbDstStream);
if (SUCCEEDED(hr))
{
cbDstStream = IMediaSample_GetSize(This->pCurrentSample);
chunk_remaining_bytes = cbDstStream - IMediaSample_GetActualDataLength(This->pCurrentSample);
assert(chunk_remaining_bytes >= 0);
assert(chunk_remaining_bytes <= cbDstStream - IMediaSample_GetActualDataLength(This->pCurrentSample));
}
if (chunk_remaining_bytes <= cbSrcStream - offset_src)
{
if (SUCCEEDED(hr))
{
memcpy(pbDstStream + IMediaSample_GetActualDataLength(This->pCurrentSample), pbSrcStream + offset_src, chunk_remaining_bytes);
hr = IMediaSample_SetActualDataLength(This->pCurrentSample, chunk_remaining_bytes + IMediaSample_GetActualDataLength(This->pCurrentSample));
assert(hr == S_OK);
}
if (SUCCEEDED(hr))
{
REFERENCE_TIME tAviStart, tAviStop, tOffset;
IMediaSample_SetDiscontinuity(This->pCurrentSample, pOutputPin->dwSamplesProcessed == 0);
IMediaSample_SetSyncPoint(This->pCurrentSample, TRUE);
pOutputPin->dwSamplesProcessed++;
tOffset = MEDIATIME_FROM_BYTES(offset_src + chunk_remaining_bytes - IMediaSample_GetActualDataLength(This->pCurrentSample));
tAviStart = bytepos_to_duration(This, tStart + tOffset);
tOffset += MEDIATIME_FROM_BYTES(IMediaSample_GetActualDataLength(This->pCurrentSample));
tAviStop = bytepos_to_duration(This, tStart + tOffset);
IMediaSample_SetTime(This->pCurrentSample, &tAviStart, &tAviStop);
hr = OutputPin_SendSample(&pOutputPin->pin, This->pCurrentSample);
if (hr != S_OK && hr != VFW_E_NOT_CONNECTED && hr != S_FALSE)
ERR("Error sending sample (%x)\n", hr);
}
if (This->pCurrentSample && hr != S_FALSE)
{
IMediaSample_Release(This->pCurrentSample);
This->pCurrentSample = NULL;
}
if (hr == S_FALSE)
{
/* Break out */
This->Parser.pInputPin->rtCurrent -= MEDIATIME_FROM_BYTES(cbSrcStream - offset_src - chunk_remaining_bytes);
hr = S_OK;
break;
}
}
else
{
if (SUCCEEDED(hr))
{
memcpy(pbDstStream + IMediaSample_GetActualDataLength(This->pCurrentSample), pbSrcStream + offset_src, cbSrcStream - offset_src);
IMediaSample_SetActualDataLength(This->pCurrentSample, cbSrcStream - offset_src + IMediaSample_GetActualDataLength(This->pCurrentSample));
}
bMoreData = FALSE;
}
offset_src += chunk_remaining_bytes;
}
if (tStop >= This->EndOfFile || (bytepos_to_duration(This, tStop) >= This->Parser.mediaSeeking.llStop))
@ -304,7 +260,7 @@ static HRESULT WAVEParserImpl_seek(IBaseFilter *iface)
return S_OK;
}
static HRESULT WAVEParser_InputPin_PreConnect(IPin * iface, IPin * pConnectPin)
static HRESULT WAVEParser_InputPin_PreConnect(IPin * iface, IPin * pConnectPin, ALLOCATOR_PROPERTIES *props)
{
PullPin *This = (PullPin *)iface;
HRESULT hr;
@ -312,7 +268,6 @@ static HRESULT WAVEParser_InputPin_PreConnect(IPin * iface, IPin * pConnectPin)
RIFFCHUNK chunk;
LONGLONG pos = 0; /* in bytes */
PIN_INFO piOutput;
ALLOCATOR_PROPERTIES props;
AM_MEDIA_TYPE amt;
WAVEParserImpl * pWAVEParser = (WAVEParserImpl *)This->pin.pinInfo.pFilter;
LONGLONG length, avail;
@ -380,15 +335,15 @@ static HRESULT WAVEParser_InputPin_PreConnect(IPin * iface, IPin * pConnectPin)
if (hr != S_OK)
return E_FAIL;
props.cbAlign = ((WAVEFORMATEX*)amt.pbFormat)->nBlockAlign;
props.cbPrefix = 0;
props.cbBuffer = 4096;
props.cBuffers = 2;
props->cbAlign = ((WAVEFORMATEX*)amt.pbFormat)->nBlockAlign;
props->cbPrefix = 0;
props->cbBuffer = 4096;
props->cBuffers = 2;
pWAVEParser->dwSampleSize = ((WAVEFORMATEX*)amt.pbFormat)->nBlockAlign;
IAsyncReader_Length(This->pReader, &length, &avail);
pWAVEParser->dwLength = length / (ULONGLONG)pWAVEParser->dwSampleSize;
pWAVEParser->nSamplesPerSec = ((WAVEFORMATEX*)amt.pbFormat)->nSamplesPerSec;
hr = Parser_AddPin(&(pWAVEParser->Parser), &piOutput, &props, &amt);
hr = Parser_AddPin(&(pWAVEParser->Parser), &piOutput, props, &amt);
CoTaskMemFree(amt.pbFormat);
pWAVEParser->Parser.mediaSeeking.llCurrent = 0;
@ -416,6 +371,52 @@ static HRESULT WAVEParser_Cleanup(LPVOID iface)
return S_OK;
}
static HRESULT WAVEParser_first_request(LPVOID iface)
{
WAVEParserImpl *This = (WAVEParserImpl *)iface;
PullPin *pin = This->Parser.pInputPin;
HRESULT hr;
IMediaSample *sample;
if (pin->rtCurrent >= pin->rtStop)
{
/* Last sample has already been queued, request nothing more */
TRACE("Done!\n");
return S_OK;
}
hr = IMemAllocator_GetBuffer(pin->pAlloc, &sample, NULL, NULL, 0);
pin->rtNext = pin->rtCurrent;
if (SUCCEEDED(hr))
{
LONGLONG rtSampleStart = pin->rtNext;
/* Add 4 for the next header, which should hopefully work */
LONGLONG rtSampleStop = rtSampleStart + MEDIATIME_FROM_BYTES(IMediaSample_GetSize(sample));
Parser_OutputPin *outpin = (Parser_OutputPin *)This->Parser.ppPins[1];
if (rtSampleStop > pin->rtStop)
rtSampleStop = MEDIATIME_FROM_BYTES(ALIGNUP(BYTES_FROM_MEDIATIME(pin->rtStop), pin->cbAlign));
hr = IMediaSample_SetTime(sample, &rtSampleStart, &rtSampleStop);
pin->rtCurrent = pin->rtNext;
pin->rtNext = rtSampleStop;
IMediaSample_SetPreroll(sample, FALSE);
if (!outpin->dwSamplesProcessed++)
IMediaSample_SetDiscontinuity(sample, TRUE);
else
IMediaSample_SetDiscontinuity(sample, FALSE);
hr = IAsyncReader_Request(pin->pReader, sample, 0);
}
if (FAILED(hr))
ERR("Horsemen of the apocalypse came to bring error 0x%08x\n", hr);
return hr;
}
static HRESULT WAVEParser_disconnect(LPVOID iface)
{
/* TODO: Find and plug memory leaks */
@ -439,7 +440,7 @@ HRESULT WAVEParser_create(IUnknown * pUnkOuter, LPVOID * ppv)
This->pCurrentSample = NULL;
hr = Parser_Create(&(This->Parser), &CLSID_WAVEParser, WAVEParser_Sample, WAVEParser_QueryAccept, WAVEParser_InputPin_PreConnect, WAVEParser_Cleanup, WAVEParser_disconnect, NULL, WAVEParserImpl_seek, NULL);
hr = Parser_Create(&(This->Parser), &CLSID_WAVEParser, WAVEParser_Sample, WAVEParser_QueryAccept, WAVEParser_InputPin_PreConnect, WAVEParser_Cleanup, WAVEParser_disconnect, WAVEParser_first_request, NULL, WAVEParserImpl_seek, NULL);
if (FAILED(hr))
return hr;