diff --git a/dlls/quartz/Makefile.in b/dlls/quartz/Makefile.in index 389415ed537..4e0dda5d285 100644 --- a/dlls/quartz/Makefile.in +++ b/dlls/quartz/Makefile.in @@ -3,10 +3,11 @@ TOPOBJDIR = ../.. SRCDIR = @srcdir@ VPATH = @srcdir@ MODULE = quartz.dll -IMPORTS = dsound ddraw msvfw32 ole32 oleaut32 user32 advapi32 kernel32 +IMPORTS = dsound ddraw msacm32 msvfw32 ole32 oleaut32 user32 advapi32 kernel32 EXTRALIBS = -lstrmiids -luuid $(LIBUNICODE) C_SRCS = \ + acmwrapper.c \ avidec.c \ avisplit.c \ control.c \ diff --git a/dlls/quartz/acmwrapper.c b/dlls/quartz/acmwrapper.c new file mode 100644 index 00000000000..62c23461ea4 --- /dev/null +++ b/dlls/quartz/acmwrapper.c @@ -0,0 +1,259 @@ +/* + * ACM Wrapper + * + * 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 "config.h" + +#include "quartz_private.h" +#include "control_private.h" +#include "pin.h" + +#include "uuids.h" +#include "mmreg.h" +#include "windef.h" +#include "winbase.h" +#include "dshow.h" +#include "strmif.h" +#include "vfwmsgs.h" +#include "evcode.h" +#include "msacm.h" + +#include + +#include "wine/unicode.h" +#include "wine/debug.h" + +#include "transform.h" + +WINE_DEFAULT_DEBUG_CHANNEL(quartz); + +/* FIXME: Improve buffers management */ +#define OUTPUT_BUFFER_SIZE 15000 +#define INPUT_BUFFER_SIZE 4096 + +typedef struct ACMWrapperImpl +{ + TransformFilterImpl tf; + HACMSTREAM has; + LPWAVEFORMATEX pWfIn; + LPWAVEFORMATEX pWfOut; + BYTE buffer[INPUT_BUFFER_SIZE]; + DWORD max_size; + DWORD current_size; + BOOL reinit_codec; /* FIXME: Should use sync points instead */ +} ACMWrapperImpl; + +static DWORD ACMWrapper_SendSampleData(TransformFilterImpl* pTransformFilter, LPBYTE data, DWORD size) +{ + ACMWrapperImpl* This = (ACMWrapperImpl*)pTransformFilter; + AM_MEDIA_TYPE amt; + HRESULT hr; + IMediaSample* pSample = NULL; + DWORD cbDstStream; + LPBYTE pbDstStream; + ACMSTREAMHEADER ash; + DWORD offset = 0; + BOOL stop = FALSE; + BOOL unprepare_header = FALSE; + MMRESULT res; + + TRACE("(%p)->(%p,%ld)\n", This, data, size); + + hr = IPin_ConnectionMediaType(This->tf.ppPins[0], &amt); + if (FAILED(hr)) { + ERR("Unable to retrieve media type\n"); + goto error; + } + + while(!stop) + { + DWORD rem_buf = This->max_size - This->current_size; + DWORD rem_smp = size - offset; + DWORD copy_size = min(rem_buf, rem_smp); + + memcpy(This->buffer + This->current_size, data + offset, copy_size); + This->current_size += copy_size; + offset += copy_size; + + if (offset == size) + stop = TRUE; + if (This->current_size < This->max_size) + break; + + hr = OutputPin_GetDeliveryBuffer((OutputPin*)This->tf.ppPins[1], &pSample, NULL, NULL, 0); + if (FAILED(hr)) { + ERR("Unable to get delivery buffer (%lx)\n", hr); + goto error; + } + + hr = IMediaSample_SetActualDataLength(pSample, 0); + assert(hr == S_OK); + + hr = IMediaSample_GetPointer(pSample, &pbDstStream); + if (FAILED(hr)) { + ERR("Unable to get pointer to buffer (%lx)\n", hr); + goto error; + } + cbDstStream = IMediaSample_GetSize(pSample); + + ash.cbStruct = sizeof(ash); + ash.fdwStatus = 0; + ash.dwUser = 0; + ash.pbSrc = This->buffer; + ash.cbSrcLength = This->current_size; + ash.pbDst = pbDstStream; + ash.cbDstLength = cbDstStream; + + if ((res = acmStreamPrepareHeader(This->has, &ash, 0))) { + ERR("Cannot prepare header %d\n", res); + goto error; + } + + unprepare_header = TRUE; + + if ((res = acmStreamConvert(This->has, &ash, This->reinit_codec ? ACM_STREAMCONVERTF_START : 0))) { + ERR("Cannot convert data header %d\n", res); + goto error; + } + This->reinit_codec = FALSE; + + TRACE("used in %lu, used out %lu\n", ash.cbSrcLengthUsed, ash.cbDstLengthUsed); + + hr = IMediaSample_SetActualDataLength(pSample, ash.cbDstLengthUsed); + assert(hr == S_OK); + + if (ash.cbSrcLengthUsed < ash.cbSrcLength) { + This->current_size = ash.cbSrcLength - ash.cbSrcLengthUsed; + memmove(This->buffer, This->buffer + ash.cbSrcLengthUsed, This->current_size); + } + else + This->current_size = 0; + + hr = OutputPin_SendSample((OutputPin*)This->tf.ppPins[1], pSample); + if (hr != S_OK && hr != VFW_E_NOT_CONNECTED) { + ERR("Error sending sample (%lx)\n", hr); + goto error; + } + +error: + if (unprepare_header && (res = acmStreamUnprepareHeader(This->has, &ash, 0))) + ERR("Cannot unprepare header %d\n", res); + + if (pSample) + IMediaSample_Release(pSample); + } + + return hr; +} + +static HRESULT ACMWrapper_ConnectInput(TransformFilterImpl* pTransformFilter, const AM_MEDIA_TYPE * pmt) +{ + ACMWrapperImpl* This = (ACMWrapperImpl*)pTransformFilter; + MMRESULT res; + + TRACE("(%p)->(%p)\n", This, pmt); + + if ((IsEqualIID(&pmt->majortype, &MEDIATYPE_Audio)) && + (!memcmp(((char*)&pmt->subtype)+4, ((char*)&MEDIATYPE_Audio)+4, sizeof(GUID)-4)) && /* Check root (GUID w/o FOURCC) */ + (IsEqualIID(&pmt->formattype, &FORMAT_WaveFormatEx))) + { + HACMSTREAM drv; + AM_MEDIA_TYPE* outpmt = &((OutputPin*)This->tf.ppPins[1])->pin.mtCurrent; + This->pWfIn = (LPWAVEFORMATEX)pmt->pbFormat; + + /* HACK */ + /* TRACE("ALIGN = %d\n", pACMWrapper->pWfIn->nBlockAlign); */ + /* pACMWrapper->pWfIn->nBlockAlign = 1; */ + + /* Set output audio data to PCM */ + CopyMediaType(outpmt, pmt); + outpmt->subtype.Data1 = WAVE_FORMAT_PCM; + This->pWfOut = (WAVEFORMATEX*)outpmt->pbFormat; + This->pWfOut->wFormatTag = WAVE_FORMAT_PCM; + This->pWfOut->wBitsPerSample = 16; + This->pWfOut->nBlockAlign = 1; + This->pWfOut->cbSize = 0; + This->pWfOut->nAvgBytesPerSec = This->pWfOut->nChannels * This->pWfOut->nSamplesPerSec + * (This->pWfOut->wBitsPerSample/8); + + if (!(res = acmStreamOpen(&drv, NULL, This->pWfIn, This->pWfOut, NULL, 0, 0, 0))) + { + This->has = drv; + + if ((res = acmStreamSize(drv, OUTPUT_BUFFER_SIZE, &This->max_size, ACM_STREAMSIZEF_DESTINATION))) { + ERR("Cannot retreive input buffer size error %d!\n", res); + This->max_size = INPUT_BUFFER_SIZE; + } + + TRACE("input buffer size %ld\n", This->max_size); + + TRACE("Connection accepted\n"); + return S_OK; + } + else + FIXME("acmStreamOpen returned %d\n", res); + DeleteMediaType(outpmt); + TRACE("Unable to find a suitable ACM decompressor\n"); + } + + TRACE("Connection refused\n"); + return S_FALSE; +} + +static HRESULT ACMWrapper_Cleanup(TransformFilterImpl* pTransformFilter) +{ + ACMWrapperImpl* This = (ACMWrapperImpl*)pTransformFilter; + + TRACE("(%p)->()\n", This); + + if (This->has) + acmStreamClose(This->has, 0); + + This->has = 0; + + return S_OK; +} + +HRESULT ACMWrapper_create(IUnknown * pUnkOuter, LPVOID * ppv) +{ + HRESULT hr; + ACMWrapperImpl* 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(ACMWrapperImpl)); + + This->has = 0; + This->reinit_codec = TRUE; + + hr = TransformFilter_Create(&(This->tf), &CLSID_ACMWrapper, ACMWrapper_SendSampleData, ACMWrapper_ConnectInput, ACMWrapper_Cleanup); + + if (FAILED(hr)) + return hr; + + *ppv = (LPVOID)This; + + return hr; +} diff --git a/dlls/quartz/avidec.c b/dlls/quartz/avidec.c index 470040a4ce0..b62e1323715 100644 --- a/dlls/quartz/avidec.c +++ b/dlls/quartz/avidec.c @@ -54,9 +54,9 @@ typedef struct AVIDecImpl BITMAPINFOHEADER* pBihOut; } AVIDecImpl; -static DWORD AVIDec_SendSampleData(TransformFilterImpl* sub, LPBYTE data, DWORD size) +static DWORD AVIDec_SendSampleData(TransformFilterImpl* pTransformFilter, LPBYTE data, DWORD size) { - AVIDecImpl* This = (AVIDecImpl*)sub; + AVIDecImpl* This = (AVIDecImpl*)pTransformFilter; VIDEOINFOHEADER* format; AM_MEDIA_TYPE amt; HRESULT hr; @@ -65,7 +65,7 @@ static DWORD AVIDec_SendSampleData(TransformFilterImpl* sub, LPBYTE data, DWORD DWORD cbDstStream; LPBYTE pbDstStream; - TRACE("%p %p %ld\n", sub, data, size); + TRACE("(%p)->(%p,%ld)\n", This, data, size); hr = IPin_ConnectionMediaType(This->tf.ppPins[0], &amt); if (FAILED(hr)) { @@ -115,11 +115,11 @@ error: return hr; } -static HRESULT AVIDec_ConnectInput(TransformFilterImpl* iface, const AM_MEDIA_TYPE * pmt) +static HRESULT AVIDec_ConnectInput(TransformFilterImpl* pTransformFilter, const AM_MEDIA_TYPE * pmt) { - AVIDecImpl* pAVIDec = (AVIDecImpl*)iface; - TRACE("%p\n", iface); - dump_AM_MEDIA_TYPE(pmt); + AVIDecImpl* This = (AVIDecImpl*)pTransformFilter; + + TRACE("(%p)->(%p)\n", This, pmt); if ((IsEqualIID(&pmt->majortype, &MEDIATYPE_Video)) && (!memcmp(((char*)&pmt->subtype)+4, ((char*)&MEDIATYPE_Video)+4, sizeof(GUID)-4)) && /* Check root (GUID w/o FOURCC) */ @@ -131,7 +131,7 @@ static HRESULT AVIDec_ConnectInput(TransformFilterImpl* iface, const AM_MEDIA_TY drv = ICLocate(pmt->majortype.Data1, pmt->subtype.Data1, &format->bmiHeader, NULL, ICMODE_DECOMPRESS); if (drv) { - AM_MEDIA_TYPE* outpmt = &((OutputPin*)pAVIDec->tf.ppPins[1])->pin.mtCurrent; + AM_MEDIA_TYPE* outpmt = &((OutputPin*)This->tf.ppPins[1])->pin.mtCurrent; const CLSID* outsubtype; DWORD bih_size; @@ -148,37 +148,37 @@ static HRESULT AVIDec_ConnectInput(TransformFilterImpl* iface, const AM_MEDIA_TY } CopyMediaType(outpmt, pmt); outpmt->subtype = *outsubtype; - pAVIDec->hvid = drv; + This->hvid = drv; /* Copy bitmap header from media type to 1 for input and 1 for output */ - if (pAVIDec->pBihIn) { - CoTaskMemFree(pAVIDec->pBihIn); - CoTaskMemFree(pAVIDec->pBihOut); + if (This->pBihIn) { + CoTaskMemFree(This->pBihIn); + CoTaskMemFree(This->pBihOut); } bih_size = format->bmiHeader.biSize + format->bmiHeader.biClrUsed * 4; - pAVIDec->pBihIn = (BITMAPINFOHEADER*)CoTaskMemAlloc(bih_size); - if (!pAVIDec->pBihIn) + This->pBihIn = (BITMAPINFOHEADER*)CoTaskMemAlloc(bih_size); + if (!This->pBihIn) { ICClose(drv); return E_OUTOFMEMORY; } - pAVIDec->pBihOut = (BITMAPINFOHEADER*)CoTaskMemAlloc(bih_size); - if (!pAVIDec->pBihOut) + This->pBihOut = (BITMAPINFOHEADER*)CoTaskMemAlloc(bih_size); + if (!This->pBihOut) { - CoTaskMemFree(pAVIDec->pBihIn); - pAVIDec->pBihIn = NULL; + CoTaskMemFree(This->pBihIn); + This->pBihIn = NULL; ICClose(drv); return E_OUTOFMEMORY; } - memcpy(pAVIDec->pBihIn, &format->bmiHeader, bih_size); - memcpy(pAVIDec->pBihOut, &format->bmiHeader, bih_size); + memcpy(This->pBihIn, &format->bmiHeader, bih_size); + memcpy(This->pBihOut, &format->bmiHeader, bih_size); /* Update output format as non compressed bitmap */ - pAVIDec->pBihOut->biCompression = 0; - pAVIDec->pBihOut->biSizeImage = pAVIDec->pBihOut->biWidth * pAVIDec->pBihOut->biHeight * pAVIDec->pBihOut->biBitCount / 8; + This->pBihOut->biCompression = 0; + This->pBihOut->biSizeImage = This->pBihOut->biWidth * This->pBihOut->biHeight * This->pBihOut->biBitCount / 8; /* Update buffer size of media samples in output */ - ((OutputPin*)pAVIDec->tf.ppPins[1])->allocProps.cbBuffer = pAVIDec->pBihOut->biSizeImage; + ((OutputPin*)This->tf.ppPins[1])->allocProps.cbBuffer = This->pBihOut->biSizeImage; TRACE("Connection accepted\n"); return S_OK; @@ -190,20 +190,22 @@ static HRESULT AVIDec_ConnectInput(TransformFilterImpl* iface, const AM_MEDIA_TY return S_FALSE; } -static HRESULT AVIDec_Cleanup(TransformFilterImpl* This) +static HRESULT AVIDec_Cleanup(TransformFilterImpl* pTransformFilter) { - AVIDecImpl* pAVIDec = (AVIDecImpl*)This; - - if (pAVIDec->hvid) - ICClose(pAVIDec->hvid); + AVIDecImpl* This = (AVIDecImpl*)pTransformFilter; - if (pAVIDec->pBihIn) { - CoTaskMemFree(pAVIDec->pBihIn); - CoTaskMemFree(pAVIDec->pBihOut); + TRACE("(%p)->()\n", This); + + if (This->hvid) + ICClose(This->hvid); + + if (This->pBihIn) { + CoTaskMemFree(This->pBihIn); + CoTaskMemFree(This->pBihOut); } - pAVIDec->hvid = NULL; - pAVIDec->pBihIn = NULL; + This->hvid = NULL; + This->pBihIn = NULL; return S_OK; } @@ -211,7 +213,7 @@ static HRESULT AVIDec_Cleanup(TransformFilterImpl* This) HRESULT AVIDec_create(IUnknown * pUnkOuter, LPVOID * ppv) { HRESULT hr; - AVIDecImpl * pAVIDec; + AVIDecImpl * This; TRACE("(%p, %p)\n", pUnkOuter, ppv); @@ -221,17 +223,17 @@ HRESULT AVIDec_create(IUnknown * pUnkOuter, LPVOID * ppv) return CLASS_E_NOAGGREGATION; /* Note: This memory is managed by the transform filter once created */ - pAVIDec = CoTaskMemAlloc(sizeof(AVIDecImpl)); + This = CoTaskMemAlloc(sizeof(AVIDecImpl)); - pAVIDec->hvid = NULL; - pAVIDec->pBihIn = NULL; + This->hvid = NULL; + This->pBihIn = NULL; - hr = TransformFilter_Create(&(pAVIDec->tf), &CLSID_AVIDec, AVIDec_SendSampleData, AVIDec_ConnectInput, AVIDec_Cleanup); + hr = TransformFilter_Create(&(This->tf), &CLSID_AVIDec, AVIDec_SendSampleData, AVIDec_ConnectInput, AVIDec_Cleanup); if (FAILED(hr)) return hr; - *ppv = (LPVOID)pAVIDec; + *ppv = (LPVOID)This; return hr; } diff --git a/dlls/quartz/main.c b/dlls/quartz/main.c index 3f85366f280..dd919836dbf 100644 --- a/dlls/quartz/main.c +++ b/dlls/quartz/main.c @@ -69,7 +69,8 @@ static const struct object_creation_info object_creation[] = { &CLSID_VideoRenderer, VideoRenderer_create }, { &CLSID_DSoundRender, DSoundRender_create }, { &CLSID_AVIDec, AVIDec_create }, - { &CLSID_SystemClock, &QUARTZ_CreateSystemClock } + { &CLSID_SystemClock, &QUARTZ_CreateSystemClock }, + { &CLSID_ACMWrapper, &ACMWrapper_create } }; static HRESULT WINAPI diff --git a/dlls/quartz/quartz_private.h b/dlls/quartz/quartz_private.h index 37443fdf5e4..c239865e334 100644 --- a/dlls/quartz/quartz_private.h +++ b/dlls/quartz/quartz_private.h @@ -50,6 +50,7 @@ HRESULT AVIDec_create(IUnknown * pUnkOuter, LPVOID * ppv); 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 EnumMonikerImpl_Create(IMoniker ** ppMoniker, ULONG nMonikerCount, IEnumMoniker ** ppEnum); diff --git a/dlls/quartz/regsvr.c b/dlls/quartz/regsvr.c index 554b85055ca..cfea669ab75 100644 --- a/dlls/quartz/regsvr.c +++ b/dlls/quartz/regsvr.c @@ -874,6 +874,12 @@ static struct regsvr_coclass const coclass_list[] = { "quartz.dll", "Both" }, + { &CLSID_SeekingPassThru, + "Seeking", + NULL, + "quartz.dll", + "Both" + }, { &CLSID_AsyncReader, "File Source Filter", NULL, @@ -904,11 +910,11 @@ static struct regsvr_coclass const coclass_list[] = { "quartz.dll", "Both" }, - { &CLSID_SeekingPassThru, - "Seeking", - NULL, - "quartz.dll", - "Both" + { &CLSID_ACMWrapper, + "ACM wrapper", + NULL, + "quartz.dll", + "Both" }, { NULL } /* list terminator */ }; @@ -1064,6 +1070,23 @@ static struct regsvr_filter const filter_list[] = { { 0xFFFFFFFF }, } }, + { &CLSID_ACMWrapper, + &CLSID_LegacyAmFilterCategory, + {'A','C','M',' ','W','r','a','p','p','e','r',0}, + 0x600000, + { { 0, + { { &MEDIATYPE_Audio, &GUID_NULL }, + { NULL } + }, + }, + { REG_PINFLAG_B_OUTPUT, + { { &MEDIATYPE_Audio, &GUID_NULL }, + { NULL } + }, + }, + { 0xFFFFFFFF }, + } + }, { NULL } /* list terminator */ };