From 80a2e6e93b4d4786b1510c3a63e2d7c43c2df37e Mon Sep 17 00:00:00 2001 From: Christian Costa Date: Thu, 10 Feb 2005 17:13:18 +0000 Subject: [PATCH] Added WAVE parser. --- dlls/quartz/Makefile.in | 3 +- dlls/quartz/main.c | 3 +- dlls/quartz/quartz_private.h | 1 + dlls/quartz/regsvr.c | 25 +++ dlls/quartz/waveparser.c | 306 +++++++++++++++++++++++++++++++++++ include/uuids.h | 1 + 6 files changed, 337 insertions(+), 2 deletions(-) create mode 100644 dlls/quartz/waveparser.c diff --git a/dlls/quartz/Makefile.in b/dlls/quartz/Makefile.in index 621b8d703c3..221048bfabe 100644 --- a/dlls/quartz/Makefile.in +++ b/dlls/quartz/Makefile.in @@ -27,7 +27,8 @@ C_SRCS = \ regsvr.c \ systemclock.c \ transform.c \ - videorenderer.c + videorenderer.c \ + waveparser.c RC_SRCS = version.rc diff --git a/dlls/quartz/main.c b/dlls/quartz/main.c index dd919836dbf..a66bf21f278 100644 --- a/dlls/quartz/main.c +++ b/dlls/quartz/main.c @@ -70,7 +70,8 @@ static const struct object_creation_info object_creation[] = { &CLSID_DSoundRender, DSoundRender_create }, { &CLSID_AVIDec, AVIDec_create }, { &CLSID_SystemClock, &QUARTZ_CreateSystemClock }, - { &CLSID_ACMWrapper, &ACMWrapper_create } + { &CLSID_ACMWrapper, &ACMWrapper_create }, + { &CLSID_WAVEParser, &WAVEParser_create } }; static HRESULT WINAPI diff --git a/dlls/quartz/quartz_private.h b/dlls/quartz/quartz_private.h index c239865e334..63c868bf029 100644 --- a/dlls/quartz/quartz_private.h +++ b/dlls/quartz/quartz_private.h @@ -51,6 +51,7 @@ HRESULT DSoundRender_create(IUnknown * pUnkOuter, LPVOID * ppv); HRESULT VideoRenderer_create(IUnknown * pUnkOuter, LPVOID * ppv); HRESULT QUARTZ_CreateSystemClock(IUnknown * pUnkOuter, LPVOID * ppv); HRESULT ACMWrapper_create(IUnknown * pUnkOuter, LPVOID * ppv); +HRESULT WAVEParser_create(IUnknown * pUnkOuter, LPVOID * ppv); HRESULT EnumMonikerImpl_Create(IMoniker ** ppMoniker, ULONG nMonikerCount, IEnumMoniker ** ppEnum); diff --git a/dlls/quartz/regsvr.c b/dlls/quartz/regsvr.c index cfea669ab75..27a79b74f79 100644 --- a/dlls/quartz/regsvr.c +++ b/dlls/quartz/regsvr.c @@ -916,6 +916,12 @@ static struct regsvr_coclass const coclass_list[] = { "quartz.dll", "Both" }, + { &CLSID_WAVEParser, + "Wave Parser", + NULL, + "quartz.dll", + "Both" + }, { NULL } /* list terminator */ }; @@ -1087,6 +1093,25 @@ static struct regsvr_filter const filter_list[] = { { 0xFFFFFFFF }, } }, + { &CLSID_WAVEParser, + &CLSID_LegacyAmFilterCategory, + {'W','a','v','e',' ','P','a','r','s','e','r',0}, + 0x400000, + { { 0, + { { &MEDIATYPE_Stream, &MEDIASUBTYPE_WAVE }, + { &MEDIATYPE_Stream, &MEDIASUBTYPE_AU }, + { &MEDIATYPE_Stream, &MEDIASUBTYPE_AIFF }, + { NULL } + }, + }, + { REG_PINFLAG_B_OUTPUT, + { { &MEDIATYPE_Audio, &GUID_NULL }, + { NULL } + }, + }, + { 0xFFFFFFFF }, + } + }, { NULL } /* list terminator */ }; diff --git a/dlls/quartz/waveparser.c b/dlls/quartz/waveparser.c new file mode 100644 index 00000000000..b508d905c22 --- /dev/null +++ b/dlls/quartz/waveparser.c @@ -0,0 +1,306 @@ +/* + * WAVE Parser Filter + * + * Copyright 2005 Christian Costa + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "quartz_private.h" +#include "control_private.h" +#include "pin.h" + +#include "uuids.h" +#include "aviriff.h" +#include "mmreg.h" +#include "vfwmsgs.h" +#include "mmsystem.h" + +#include "fourcc.h" + +#include "wine/unicode.h" +#include "wine/debug.h" + +#include +#include + +#include "parser.h" + +WINE_DEFAULT_DEBUG_CHANNEL(quartz); + +static const WCHAR wcsOutputPinName[] = {'o','u','t','p','u','t',' ','p','i','n',0}; + +typedef struct WAVEParserImpl +{ + ParserImpl Parser; + IMediaSample * pCurrentSample; + LONGLONG StartOfFile; /* in media time */ + LONGLONG EndOfFile; +} WAVEParserImpl; + +static HRESULT WAVEParser_Sample(LPVOID iface, IMediaSample * pSample) +{ + WAVEParserImpl *This = (WAVEParserImpl *)iface; + LPBYTE pbSrcStream = NULL; + long 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); + + hr = IMediaSample_GetTime(pSample, &tStart, &tStop); + + cbSrcStream = IMediaSample_GetActualDataLength(pSample); + + assert(BYTES_FROM_MEDIATIME(tStop - tStart) == cbSrcStream); + + pOutputPin = (Parser_OutputPin *)This->Parser.ppPins[1]; + + if (tStop < This->StartOfFile) + return S_OK; + + if (tStart < This->StartOfFile) + offset_src = BYTES_FROM_MEDIATIME(This->StartOfFile - tStart); + + while (bMoreData) + { + if (!This->pCurrentSample) + { + /* cache media sample until it is ready to be despatched + * (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 (%lx)\n", hr); + This->pCurrentSample = NULL; + return hr; + } + } + + 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; + + /* FIXME: hack */ + if (pOutputPin->dwSamplesProcessed == 0) { + IMediaSample_SetDiscontinuity(This->pCurrentSample, TRUE); + IMediaSample_SetSyncPoint(This->pCurrentSample, TRUE); + } + + pOutputPin->dwSamplesProcessed++; + + if (pOutputPin->dwSampleSize) + tAviStart = (LONGLONG)ceil(10000000.0 * (float)(pOutputPin->dwSamplesProcessed - 1) * (float)IMediaSample_GetActualDataLength(This->pCurrentSample) / ((float)pOutputPin->dwSampleSize * pOutputPin->fSamplesPerSec)); + else + tAviStart = (LONGLONG)ceil(10000000.0 * (float)(pOutputPin->dwSamplesProcessed - 1) / (float)pOutputPin->fSamplesPerSec); + if (pOutputPin->dwSampleSize) + tAviStop = (LONGLONG)ceil(10000000.0 * (float)pOutputPin->dwSamplesProcessed * (float)IMediaSample_GetActualDataLength(This->pCurrentSample) / ((float)pOutputPin->dwSampleSize * pOutputPin->fSamplesPerSec)); + else + tAviStop = (LONGLONG)ceil(10000000.0 * (float)pOutputPin->dwSamplesProcessed / (float)pOutputPin->fSamplesPerSec); + + IMediaSample_SetTime(This->pCurrentSample, &tAviStart, &tAviStop); + + hr = OutputPin_SendSample(&pOutputPin->pin, This->pCurrentSample); + if (hr != S_OK && hr != VFW_E_NOT_CONNECTED) + ERR("Error sending sample (%lx)\n", hr); + } + + if (This->pCurrentSample) + IMediaSample_Release(This->pCurrentSample); + + This->pCurrentSample = NULL; + } + 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; + } + + return hr; +} + +static HRESULT WAVEParser_QueryAccept(LPVOID iface, const AM_MEDIA_TYPE * pmt) +{ + if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Stream)) + return S_FALSE; + if (IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_WAVE)) + return S_OK; + if (IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_AU) || IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_AIFF)) + FIXME("AU and AIFF files not supported yet!\n"); + return S_FALSE; +} + +static HRESULT WAVEParser_InputPin_PreConnect(IPin * iface, IPin * pConnectPin) +{ + PullPin *This = (PullPin *)iface; + HRESULT hr; + RIFFLIST list; + RIFFCHUNK chunk; + LONGLONG pos = 0; /* in bytes */ + PIN_INFO piOutput; + ALLOCATOR_PROPERTIES props; + AM_MEDIA_TYPE amt; + float fSamplesPerSec = 0.0f; + DWORD dwSampleSize = 0; + DWORD dwLength = 0; + WAVEParserImpl * pWAVEParser = (WAVEParserImpl *)This->pin.pinInfo.pFilter; + + piOutput.dir = PINDIR_OUTPUT; + piOutput.pFilter = (IBaseFilter *)This; + strncpyW(piOutput.achName, wcsOutputPinName, sizeof(piOutput.achName) / sizeof(piOutput.achName[0])); + + hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(list), (BYTE *)&list); + pos += sizeof(list); + + if (list.fcc != ckidRIFF) + { + ERR("Input stream not a RIFF file\n"); + return E_FAIL; + } + if (list.cb > 1 * 1024 * 1024 * 1024) /* cannot be more than 1Gb in size */ + { + ERR("Input stream violates RIFF spec\n"); + return E_FAIL; + } + if (list.fccListType != mmioFOURCC('W','A','V','E')) + { + ERR("Input stream not an WAVE RIFF file\n"); + return E_FAIL; + } + + hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(chunk), (BYTE *)&chunk); + pos += sizeof(chunk); + if (chunk.fcc != mmioFOURCC('f','m','t',' ')) + { + ERR("Expected 'fmt ' chunk, but got %.04s\n", (LPSTR)&chunk.fcc); + return E_FAIL; + } + + memcpy(&amt.majortype, &MEDIATYPE_Audio, sizeof(GUID)); + memcpy(&amt.formattype, &FORMAT_WaveFormatEx, sizeof(GUID)); + amt.cbFormat = chunk.cb; + amt.pbFormat = CoTaskMemAlloc(amt.cbFormat); + amt.pUnk = NULL; + hr = IAsyncReader_SyncRead(This->pReader, pos, amt.cbFormat, amt.pbFormat); + memcpy(&amt.subtype, &MEDIATYPE_Audio, sizeof(GUID)); + amt.subtype.Data1 = ((WAVEFORMATEX*)amt.pbFormat)->wFormatTag; + /* CopyMediaType(&((OutputPin*)pWAVEParser->ppPins[1])->pin.mtCurrent, &amt); */ + ((Parser_OutputPin*)pWAVEParser->Parser.ppPins[1])->pmt = (AM_MEDIA_TYPE*) CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE)); + + CopyMediaType(((Parser_OutputPin*)pWAVEParser->Parser.ppPins[1])->pmt, &amt); + + /* Update buffer alignment of media samples in output */ + ((Parser_OutputPin*)pWAVEParser->Parser.ppPins[1])->pin.allocProps.cbAlign = ((WAVEFORMATEX*)amt.pbFormat)->nBlockAlign; + + pos += chunk.cb; + hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(chunk), (BYTE *)&chunk); + if (chunk.fcc == mmioFOURCC('f','a','c','t')) + { + FIXME("'fact' chunk not supported yet\n"); + pos += sizeof(chunk) + chunk.cb; + hr = IAsyncReader_SyncRead(This->pReader, pos, sizeof(chunk), (BYTE *)&chunk); + } + if (chunk.fcc != mmioFOURCC('d','a','t','a')) + { + ERR("Expected 'data' chunk, but got %.04s\n", (LPSTR)&chunk.fcc); + return E_FAIL; + } + + if (hr == S_OK) + { + pWAVEParser->StartOfFile = MEDIATIME_FROM_BYTES(pos + sizeof(RIFFCHUNK)); + pWAVEParser->EndOfFile = MEDIATIME_FROM_BYTES(pos + chunk.cb + sizeof(RIFFCHUNK)); + } + + if (hr != S_OK) + return E_FAIL; + + props.cbAlign = 1; + props.cbPrefix = 0; + props.cbBuffer = 4096; + props.cBuffers = 2; + + hr = Parser_AddPin(&(pWAVEParser->Parser), &piOutput, &props, &amt, fSamplesPerSec, dwSampleSize, dwLength); + + TRACE("WAVE File ok\n"); + + return hr; +} + +HRESULT WAVEParser_create(IUnknown * pUnkOuter, LPVOID * ppv) +{ + HRESULT hr; + WAVEParserImpl * This; + + TRACE("(%p, %p)\n", pUnkOuter, ppv); + + *ppv = NULL; + + if (pUnkOuter) + return CLASS_E_NOAGGREGATION; + + /* Note: This memory is managed by the transform filter once created */ + This = CoTaskMemAlloc(sizeof(WAVEParserImpl)); + + This->pCurrentSample = NULL; + + hr = Parser_Create(&(This->Parser), &CLSID_WAVEParser, WAVEParser_Sample, WAVEParser_QueryAccept, WAVEParser_InputPin_PreConnect); + + if (FAILED(hr)) + return hr; + + *ppv = (LPVOID)This; + + return hr; +} diff --git a/include/uuids.h b/include/uuids.h index 62349b5ad2b..2d527453dcb 100644 --- a/include/uuids.h +++ b/include/uuids.h @@ -130,6 +130,7 @@ OUR_GUID_ENTRY(CLSID_CaptureProperties, 0x1b544c22, 0xfd0b, 0x11ce, OUR_GUID_ENTRY(CLSID_FGControl, 0xe436ebb4, 0x524f, 0x11ce, 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70) OUR_GUID_ENTRY(CLSID_MOVReader, 0x44584800, 0xf8ee, 0x11ce, 0xb2, 0xd4, 0x00, 0xdd, 0x01, 0x10, 0x1b, 0x85) OUR_GUID_ENTRY(CLSID_QuickTimeParser, 0xd51bd5a0, 0x7548, 0x11cf, 0xa5, 0x20, 0x00, 0x80, 0xc7, 0x7e, 0xf5, 0x8a) +OUR_GUID_ENTRY(CLSID_WAVEParser, 0xd51bd5a1, 0x7548, 0x11cf, 0xa5, 0x20, 0x00, 0x80, 0xc7, 0x7e, 0xf5, 0x8a) OUR_GUID_ENTRY(CLSID_QTDec, 0xfdfe9681, 0x74a3, 0x11d0, 0xaf, 0xa7, 0x00, 0xaa, 0x00, 0xb6, 0x7a, 0x42) OUR_GUID_ENTRY(CLSID_AVIDoc, 0xd3588ab0, 0x0781, 0x11ce, 0xb0, 0x3a, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70) OUR_GUID_ENTRY(CLSID_AVIDocWriter, 0xd3588ab1, 0x0781, 0x11ce, 0xb0, 0x3a, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70)