quartz: Make wave parser and mpeg splitter zero copy by getting rid of the seperate allocator for the output pin.
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 @@
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 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 @@
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 @@
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 @@
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_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 @@
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 @@
{
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 @@
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 @@
default:
break;
}
- This->remaining_bytes = 0;
This->position = 0;
return hr;
@@ -789,7 +653,6 @@
IMediaSample_Release(This->pCurrentSample);
This->pCurrentSample = NULL;
- This->remaining_bytes = This->skipbytes = 0;
return S_OK;
}
@@ -820,11 +683,11 @@
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 @@
/* 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 @@
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 @@
}
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;