quartz: Make wave parser and mpeg splitter zero copy by getting rid of the seperate allocator for the output pin.
This commit is contained in:
parent
ec124be823
commit
3a39805ed8
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue