From b63eab8003babcebf5ceaa931e1aa29b6b01dba6 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 22 Apr 2008 13:57:11 -0700 Subject: [PATCH] quartz: Make wave parser and mpeg splitter zero copy by getting rid of the seperate allocator for the output pin --- dlls/quartz/avisplit.c | 7 +- dlls/quartz/mpegsplit.c | 362 +++++++++++++++++---------------------------- dlls/quartz/parser.c | 26 +++- dlls/quartz/parser.h | 4 +- dlls/quartz/pin.c | 158 +++++++++++++------- dlls/quartz/pin.h | 10 +- dlls/quartz/waveparser.c | 211 ++++++++++++++------------- 7 files changed, 384 insertions(+), 394 deletions(-) diff --git a/dlls/quartz/avisplit.c b/dlls/quartz/avisplit.c index 1235aac..77ee335 100644 --- a/dlls/quartz/avisplit.c +++ b/dlls/quartz/avisplit.c @@ -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; diff --git a/dlls/quartz/mpegsplit.c b/dlls/quartz/mpegsplit.c index 6f8c502..395ce71 100644 --- a/dlls/quartz/mpegsplit.c +++ b/dlls/quartz/mpegsplit.c @@ -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); + TRACE("Source length: %u\n", len); + IMediaSample_GetPointer(pCurrentSample, &fbuf); - /* 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; - } + /* Find the next valid header.. it be right here */ + assert(parse_header(fbuf, &length, &This->position) == S_OK); + assert(length == len || length + 4 == len); + IMediaSample_SetActualDataLength(pCurrentSample, length); - /* Case where there is already an output sample being held */ - if (This->remaining_bytes) + if (length + 4 == len) { - DWORD towrite = min(This->remaining_bytes, *flen); - LONGLONG foo; + PullPin *pin = This->Parser.pInputPin; + LONGLONG stop = BYTES_FROM_MEDIATIME(pin->rtStop); - hr = copy_data(pCurrentSample, fbuf, flen, towrite); - if (FAILED(hr)) + hr = S_OK; + memcpy(This->header, fbuf + length, 4); + while (FAILED(hr = parse_header(This->header, &length, NULL))) { - WARN("Could not resize sample: %08x\n", hr); - return hr; + memmove(This->header, This->header+1, 3); + if (pos + 4 >= stop) + break; + IAsyncReader_SyncRead(pin->pReader, ++pos, 1, This->header + 3); } - - 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); + pin->rtNext = MEDIATIME_FROM_BYTES(pos); 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; - } + /* 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); - IMediaSample_SetActualDataLength(pCurrentSample, 4); - IMediaSample_SetTime(pCurrentSample, &time, &This->position); + hr = IMemAllocator_GetBuffer(pin->pAlloc, &sample, NULL, NULL, 0); - /* Move header back to beginning */ - if (attempts) - memmove(header, header+attempts, 4); + if (rtSampleStop > pin->rtStop) + rtSampleStop = MEDIATIME_FROM_BYTES(ALIGNUP(BYTES_FROM_MEDIATIME(pin->rtStop), pin->cbAlign)); - This->remaining_bytes = length - 4; - *flen -= (4 - dlen + attempts); - *fbuf += (4 - dlen + attempts); - return hr; - } + IMediaSample_SetTime(sample, &rtSampleStart, &rtSampleStop); + IMediaSample_SetPreroll(sample, 0); + IMediaSample_SetDiscontinuity(sample, 0); + IMediaSample_SetSyncPoint(sample, 1); + pin->rtCurrent = rtSampleStart; + pin->rtNext = rtSampleStop; - /* Destination sample should contain no data! But the source sample should */ - assert(!dlen); - assert(*flen); - - /* Find the next valid header.. it be right here */ - while (*flen > 3 && FAILED(parse_header(*fbuf, &length, &This->position))) - { - skip_data(fbuf, flen, 1); + 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 */ - /* Uh oh, no header found! */ - if (*flen < 4) - { - assert(!length); - hr = copy_data(pCurrentSample, fbuf, flen, *flen); - return hr; - } + 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; diff --git a/dlls/quartz/parser.c b/dlls/quartz/parser.c index 28ce90b..a34ae84 100644 --- a/dlls/quartz/parser.c +++ b/dlls/quartz/parser.c @@ -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, diff --git a/dlls/quartz/parser.h b/dlls/quartz/parser.h index 0c9d917..035c453 100644 --- a/dlls/quartz/parser.h +++ b/dlls/quartz/parser.h @@ -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); diff --git a/dlls/quartz/pin.c b/dlls/quartz/pin.c index a7932e4..868f73e 100644 --- a/dlls/quartz/pin.c +++ b/dlls/quartz/pin.c @@ -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)) + if (SUCCEEDED(hr) && This->fnPreConnect) { - 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); + hr = This->fnPreConnect(iface, pReceivePin, &props); } - if (SUCCEEDED(hr) && This->fnPreConnect) + if (SUCCEEDED(hr)) { - hr = This->fnPreConnect(iface, pReceivePin); + 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, diff --git a/dlls/quartz/pin.h b/dlls/quartz/pin.h index b28b52f..147a64c 100644 --- a/dlls/quartz/pin.h +++ b/dlls/quartz/pin.h @@ -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; diff --git a/dlls/quartz/waveparser.c b/dlls/quartz/waveparser.c index 867c1cc..dec0e53 100644 --- a/dlls/quartz/waveparser.c +++ b/dlls/quartz/waveparser.c @@ -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 (tStart < This->StartOfFile) - offset_src = BYTES_FROM_MEDIATIME(This->StartOfFile - tStart); + if (SUCCEEDED(hr)) + hr = IMemAllocator_GetBuffer(pin->pAlloc, &newsample, NULL, NULL, 0); - while (bMoreData) + if (SUCCEEDED(hr)) { - if (!This->pCurrentSample) - { - /* 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); + LONGLONG rtSampleStart = pin->rtNext; + /* Add 4 for the next header, which should hopefully work */ + LONGLONG rtSampleStop = rtSampleStart + MEDIATIME_FROM_BYTES(IMediaSample_GetSize(newsample)); - 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; - } - } - - hr = IMediaSample_GetPointer(This->pCurrentSample, &pbDstStream); - - if (SUCCEEDED(hr)) - { - cbDstStream = IMediaSample_GetSize(This->pCurrentSample); + if (rtSampleStop > pin->rtStop) + rtSampleStop = MEDIATIME_FROM_BYTES(ALIGNUP(BYTES_FROM_MEDIATIME(pin->rtStop), pin->cbAlign)); - chunk_remaining_bytes = cbDstStream - IMediaSample_GetActualDataLength(This->pCurrentSample); - - assert(chunk_remaining_bytes >= 0); - assert(chunk_remaining_bytes <= cbDstStream - IMediaSample_GetActualDataLength(This->pCurrentSample)); - } + hr = IMediaSample_SetTime(newsample, &rtSampleStart, &rtSampleStop); - 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); - } + pin->rtCurrent = pin->rtNext; + pin->rtNext = rtSampleStop; - if (SUCCEEDED(hr)) - { - REFERENCE_TIME tAviStart, tAviStop, tOffset; + IMediaSample_SetPreroll(newsample, 0); + IMediaSample_SetDiscontinuity(newsample, 0); + IMediaSample_SetSyncPoint(newsample, 1); - IMediaSample_SetDiscontinuity(This->pCurrentSample, pOutputPin->dwSamplesProcessed == 0); + hr = IAsyncReader_Request(pin->pReader, newsample, 0); + } - IMediaSample_SetSyncPoint(This->pCurrentSample, TRUE); - pOutputPin->dwSamplesProcessed++; + if (SUCCEEDED(hr)) + { + REFERENCE_TIME tAviStart, tAviStop; - tOffset = MEDIATIME_FROM_BYTES(offset_src + chunk_remaining_bytes - IMediaSample_GetActualDataLength(This->pCurrentSample)); - tAviStart = bytepos_to_duration(This, tStart + tOffset); + IMediaSample_SetSyncPoint(pSample, TRUE); + pOutputPin->dwSamplesProcessed++; - tOffset += MEDIATIME_FROM_BYTES(IMediaSample_GetActualDataLength(This->pCurrentSample)); - tAviStop = bytepos_to_duration(This, tStart + tOffset); + tAviStart = bytepos_to_duration(This, tStart); + tAviStop = bytepos_to_duration(This, tStart + MEDIATIME_FROM_BYTES(IMediaSample_GetActualDataLength(pSample))); - IMediaSample_SetTime(This->pCurrentSample, &tAviStart, &tAviStop); + IMediaSample_SetTime(pSample, &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); - } + 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 (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 (hr == S_FALSE) { - 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; + This->pCurrentSample = pSample; + IMediaSample_AddRef(pSample); + hr = S_OK; } - 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; -- 1.5.4.1