|  | /* | 
|  | * Copyright 2002 Michael Günnewig | 
|  | * | 
|  | * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
|  | */ | 
|  |  | 
|  | #include <assert.h> | 
|  | #include <stdarg.h> | 
|  |  | 
|  | #include "windef.h" | 
|  | #include "winbase.h" | 
|  | #include "wingdi.h" | 
|  | #include "winuser.h" | 
|  | #include "winerror.h" | 
|  | #include "mmsystem.h" | 
|  | #include "vfw.h" | 
|  | #include "msacm.h" | 
|  |  | 
|  | #include "avifile_private.h" | 
|  |  | 
|  | #include "wine/debug.h" | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(avifile); | 
|  |  | 
|  | /***********************************************************************/ | 
|  |  | 
|  | static HRESULT WINAPI ACMStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID *obj); | 
|  | static ULONG   WINAPI ACMStream_fnAddRef(IAVIStream*iface); | 
|  | static ULONG   WINAPI ACMStream_fnRelease(IAVIStream* iface); | 
|  | static HRESULT WINAPI ACMStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2); | 
|  | static HRESULT WINAPI ACMStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size); | 
|  | static LONG    WINAPI ACMStream_fnFindSample(IAVIStream*iface,LONG pos,LONG flags); | 
|  | static HRESULT WINAPI ACMStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG *formatsize); | 
|  | static HRESULT WINAPI ACMStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize); | 
|  | static HRESULT WINAPI ACMStream_fnRead(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG *bytesread,LONG *samplesread); | 
|  | static HRESULT WINAPI ACMStream_fnWrite(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG *sampwritten,LONG *byteswritten); | 
|  | static HRESULT WINAPI ACMStream_fnDelete(IAVIStream*iface,LONG start,LONG samples); | 
|  | static HRESULT WINAPI ACMStream_fnReadData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG *lpread); | 
|  | static HRESULT WINAPI ACMStream_fnWriteData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG size); | 
|  | static HRESULT WINAPI ACMStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen); | 
|  |  | 
|  | static const struct IAVIStreamVtbl iacmst = { | 
|  | ACMStream_fnQueryInterface, | 
|  | ACMStream_fnAddRef, | 
|  | ACMStream_fnRelease, | 
|  | ACMStream_fnCreate, | 
|  | ACMStream_fnInfo, | 
|  | ACMStream_fnFindSample, | 
|  | ACMStream_fnReadFormat, | 
|  | ACMStream_fnSetFormat, | 
|  | ACMStream_fnRead, | 
|  | ACMStream_fnWrite, | 
|  | ACMStream_fnDelete, | 
|  | ACMStream_fnReadData, | 
|  | ACMStream_fnWriteData, | 
|  | ACMStream_fnSetInfo | 
|  | }; | 
|  |  | 
|  | typedef struct _IAVIStreamImpl { | 
|  | /* IUnknown stuff */ | 
|  | const IAVIStreamVtbl *lpVtbl; | 
|  | LONG		  ref; | 
|  |  | 
|  | /* IAVIStream stuff */ | 
|  | PAVISTREAM      pStream; | 
|  | AVISTREAMINFOW  sInfo; | 
|  |  | 
|  | HACMSTREAM      has; | 
|  |  | 
|  | LPWAVEFORMATEX  lpInFormat; | 
|  | LONG            cbInFormat; | 
|  |  | 
|  | LPWAVEFORMATEX  lpOutFormat; | 
|  | LONG            cbOutFormat; | 
|  |  | 
|  | ACMSTREAMHEADER acmStreamHdr; | 
|  | } IAVIStreamImpl; | 
|  |  | 
|  | /***********************************************************************/ | 
|  |  | 
|  | #define CONVERT_STREAM_to_THIS(a) do { \ | 
|  | DWORD __bytes; \ | 
|  | acmStreamSize(This->has,*(a) * This->lpInFormat->nBlockAlign,\ | 
|  | &__bytes, ACM_STREAMSIZEF_SOURCE); \ | 
|  | *(a) = __bytes / This->lpOutFormat->nBlockAlign; } while(0) | 
|  |  | 
|  | #define CONVERT_THIS_to_STREAM(a) do { \ | 
|  | DWORD __bytes; \ | 
|  | acmStreamSize(This->has,*(a) * This->lpOutFormat->nBlockAlign,\ | 
|  | &__bytes, ACM_STREAMSIZEF_DESTINATION); \ | 
|  | *(a) = __bytes / This->lpInFormat->nBlockAlign; } while(0) | 
|  |  | 
|  | static HRESULT AVIFILE_OpenCompressor(IAVIStreamImpl *This); | 
|  |  | 
|  | HRESULT AVIFILE_CreateACMStream(REFIID riid, LPVOID *ppv) | 
|  | { | 
|  | IAVIStreamImpl *pstream; | 
|  | HRESULT         hr; | 
|  |  | 
|  | assert(riid != NULL && ppv != NULL); | 
|  |  | 
|  | *ppv = NULL; | 
|  |  | 
|  | pstream = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIStreamImpl)); | 
|  | if (pstream == NULL) | 
|  | return AVIERR_MEMORY; | 
|  |  | 
|  | pstream->lpVtbl = &iacmst; | 
|  |  | 
|  | hr = IAVIStream_QueryInterface((IAVIStream*)pstream, riid, ppv); | 
|  | if (FAILED(hr)) | 
|  | HeapFree(GetProcessHeap(), 0, pstream); | 
|  |  | 
|  | return hr; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI ACMStream_fnQueryInterface(IAVIStream *iface, | 
|  | REFIID refiid, LPVOID *obj) | 
|  | { | 
|  | IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | 
|  |  | 
|  | TRACE("(%p,%s,%p)\n", iface, debugstr_guid(refiid), obj); | 
|  |  | 
|  | if (IsEqualGUID(&IID_IUnknown, refiid) || | 
|  | IsEqualGUID(&IID_IAVIStream, refiid)) { | 
|  | *obj = This; | 
|  | IAVIStream_AddRef(iface); | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | return OLE_E_ENUM_NOMORE; | 
|  | } | 
|  |  | 
|  | static ULONG WINAPI ACMStream_fnAddRef(IAVIStream *iface) | 
|  | { | 
|  | IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | 
|  | ULONG ref = InterlockedIncrement(&This->ref); | 
|  |  | 
|  | TRACE("(%p) -> %d\n", iface, ref); | 
|  |  | 
|  | /* also add reference to the nested stream */ | 
|  | if (This->pStream != NULL) | 
|  | IAVIStream_AddRef(This->pStream); | 
|  |  | 
|  | return ref; | 
|  | } | 
|  |  | 
|  | static ULONG WINAPI ACMStream_fnRelease(IAVIStream* iface) | 
|  | { | 
|  | IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | 
|  | ULONG ref = InterlockedDecrement(&This->ref); | 
|  |  | 
|  | TRACE("(%p) -> %d\n", iface, ref); | 
|  |  | 
|  | if (ref == 0) { | 
|  | /* destruct */ | 
|  | if (This->has != NULL) { | 
|  | if (This->acmStreamHdr.fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED) | 
|  | acmStreamUnprepareHeader(This->has, &This->acmStreamHdr, 0); | 
|  | acmStreamClose(This->has, 0); | 
|  | This->has = NULL; | 
|  | } | 
|  | HeapFree(GetProcessHeap(), 0, This->acmStreamHdr.pbSrc); | 
|  | This->acmStreamHdr.pbSrc = NULL; | 
|  | HeapFree(GetProcessHeap(), 0, This->acmStreamHdr.pbDst); | 
|  | This->acmStreamHdr.pbDst = NULL; | 
|  | if (This->lpInFormat != NULL) { | 
|  | HeapFree(GetProcessHeap(), 0, This->lpInFormat); | 
|  | This->lpInFormat = NULL; | 
|  | This->cbInFormat = 0; | 
|  | } | 
|  | if (This->lpOutFormat != NULL) { | 
|  | HeapFree(GetProcessHeap(), 0, This->lpOutFormat); | 
|  | This->lpOutFormat = NULL; | 
|  | This->cbOutFormat = 0; | 
|  | } | 
|  | if (This->pStream != NULL) { | 
|  | IAVIStream_Release(This->pStream); | 
|  | This->pStream = NULL; | 
|  | } | 
|  | HeapFree(GetProcessHeap(), 0, This); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* also release reference to the nested stream */ | 
|  | if (This->pStream != NULL) | 
|  | IAVIStream_Release(This->pStream); | 
|  |  | 
|  | return ref; | 
|  | } | 
|  |  | 
|  | /* lParam1: PAVISTREAM | 
|  | * lParam2: LPAVICOMPRESSOPTIONS -- even if doc's say LPWAVEFORMAT | 
|  | */ | 
|  | static HRESULT WINAPI ACMStream_fnCreate(IAVIStream *iface, LPARAM lParam1, | 
|  | LPARAM lParam2) | 
|  | { | 
|  | IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | 
|  |  | 
|  | TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2); | 
|  |  | 
|  | /* check for swapped parameters */ | 
|  | if ((LPVOID)lParam1 != NULL && | 
|  | ((LPAVICOMPRESSOPTIONS)lParam1)->fccType == streamtypeAUDIO) { | 
|  | register LPARAM tmp = lParam1; | 
|  |  | 
|  | lParam1 = lParam2; | 
|  | lParam2 = tmp; | 
|  | } | 
|  |  | 
|  | if ((LPVOID)lParam1 == NULL) | 
|  | return AVIERR_BADPARAM; | 
|  |  | 
|  | IAVIStream_Info((PAVISTREAM)lParam1, &This->sInfo, sizeof(This->sInfo)); | 
|  | if (This->sInfo.fccType != streamtypeAUDIO) | 
|  | return AVIERR_ERROR; /* error in registry or AVIMakeCompressedStream */ | 
|  |  | 
|  | This->sInfo.fccHandler = 0; /* be paranoid */ | 
|  |  | 
|  | /* FIXME: check ACM version? Which version does we need? */ | 
|  |  | 
|  | if ((LPVOID)lParam2 != NULL) { | 
|  | /* We only need the format from the compress-options */ | 
|  | if (((LPAVICOMPRESSOPTIONS)lParam2)->fccType == streamtypeAUDIO) | 
|  | lParam2 = (LPARAM)((LPAVICOMPRESSOPTIONS)lParam2)->lpFormat; | 
|  |  | 
|  | if (((LPWAVEFORMATEX)lParam2)->wFormatTag != WAVE_FORMAT_PCM) | 
|  | This->cbOutFormat = sizeof(WAVEFORMATEX) + ((LPWAVEFORMATEX)lParam2)->cbSize; | 
|  | else | 
|  | This->cbOutFormat = sizeof(PCMWAVEFORMAT); | 
|  |  | 
|  | This->lpOutFormat = HeapAlloc(GetProcessHeap(), 0, This->cbOutFormat); | 
|  | if (This->lpOutFormat == NULL) | 
|  | return AVIERR_MEMORY; | 
|  |  | 
|  | memcpy(This->lpOutFormat, (LPVOID)lParam2, This->cbOutFormat); | 
|  | } else { | 
|  | This->lpOutFormat = NULL; | 
|  | This->cbOutFormat = 0; | 
|  | } | 
|  |  | 
|  | This->pStream = (PAVISTREAM)lParam1; | 
|  | IAVIStream_AddRef(This->pStream); | 
|  |  | 
|  | return AVIERR_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI ACMStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi, | 
|  | LONG size) | 
|  | { | 
|  | IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | 
|  |  | 
|  | TRACE("(%p,%p,%d)\n", iface, psi, size); | 
|  |  | 
|  | if (psi == NULL) | 
|  | return AVIERR_BADPARAM; | 
|  | if (size < 0) | 
|  | return AVIERR_BADSIZE; | 
|  |  | 
|  | /* Need codec to correct some values in structure */ | 
|  | if (This->has == NULL) { | 
|  | HRESULT hr = AVIFILE_OpenCompressor(This); | 
|  |  | 
|  | if (FAILED(hr)) | 
|  | return hr; | 
|  | } | 
|  |  | 
|  | memcpy(psi, &This->sInfo, min(size, (LONG)sizeof(This->sInfo))); | 
|  |  | 
|  | if (size < (LONG)sizeof(This->sInfo)) | 
|  | return AVIERR_BUFFERTOOSMALL; | 
|  | return AVIERR_OK; | 
|  | } | 
|  |  | 
|  | static LONG WINAPI ACMStream_fnFindSample(IAVIStream *iface, LONG pos, | 
|  | LONG flags) | 
|  | { | 
|  | IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | 
|  |  | 
|  | TRACE("(%p,%d,0x%08X)\n",iface,pos,flags); | 
|  |  | 
|  | if (flags & FIND_FROM_START) { | 
|  | pos = This->sInfo.dwStart; | 
|  | flags &= ~(FIND_FROM_START|FIND_PREV); | 
|  | flags |= FIND_NEXT; | 
|  | } | 
|  |  | 
|  | /* convert pos from our 'space' to This->pStream's one */ | 
|  | CONVERT_THIS_to_STREAM(&pos); | 
|  |  | 
|  | /* ask stream */ | 
|  | pos = IAVIStream_FindSample(This->pStream, pos, flags); | 
|  |  | 
|  | if (pos != -1) { | 
|  | /* convert pos back to our 'space' if it's no size or physical pos */ | 
|  | if ((flags & FIND_RET) == 0) | 
|  | CONVERT_STREAM_to_THIS(&pos); | 
|  | } | 
|  |  | 
|  | return pos; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI ACMStream_fnReadFormat(IAVIStream *iface, LONG pos, | 
|  | LPVOID format, LONG *formatsize) | 
|  | { | 
|  | IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | 
|  |  | 
|  | TRACE("(%p,%d,%p,%p)\n", iface, pos, format, formatsize); | 
|  |  | 
|  | if (formatsize == NULL) | 
|  | return AVIERR_BADPARAM; | 
|  |  | 
|  | if (This->has == NULL) { | 
|  | HRESULT hr = AVIFILE_OpenCompressor(This); | 
|  |  | 
|  | if (FAILED(hr)) | 
|  | return hr; | 
|  | } | 
|  |  | 
|  | /* only interested in needed buffersize? */ | 
|  | if (format == NULL || *formatsize <= 0) { | 
|  | *formatsize = This->cbOutFormat; | 
|  |  | 
|  | return AVIERR_OK; | 
|  | } | 
|  |  | 
|  | /* copy initial format (only as much as will fit) */ | 
|  | memcpy(format, This->lpOutFormat, min(*formatsize, This->cbOutFormat)); | 
|  | if (*formatsize < This->cbOutFormat) { | 
|  | *formatsize = This->cbOutFormat; | 
|  | return AVIERR_BUFFERTOOSMALL; | 
|  | } | 
|  |  | 
|  | *formatsize = This->cbOutFormat; | 
|  | return AVIERR_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI ACMStream_fnSetFormat(IAVIStream *iface, LONG pos, | 
|  | LPVOID format, LONG formatsize) | 
|  | { | 
|  | IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | 
|  |  | 
|  | HRESULT hr; | 
|  |  | 
|  | TRACE("(%p,%d,%p,%d)\n", iface, pos, format, formatsize); | 
|  |  | 
|  | /* check parameters */ | 
|  | if (format == NULL || formatsize <= 0) | 
|  | return AVIERR_BADPARAM; | 
|  |  | 
|  | /* Input format already known? | 
|  | * Changing is unsupported, but be quiet if it's the same */ | 
|  | if (This->lpInFormat != NULL) { | 
|  | if (This->cbInFormat != formatsize || | 
|  | memcmp(format, This->lpInFormat, formatsize) != 0) | 
|  | return AVIERR_UNSUPPORTED; | 
|  |  | 
|  | return AVIERR_OK; | 
|  | } | 
|  |  | 
|  | /* Does the nested stream support writing? */ | 
|  | if ((This->sInfo.dwCaps & AVIFILECAPS_CANWRITE) == 0) | 
|  | return AVIERR_READONLY; | 
|  |  | 
|  | This->lpInFormat = HeapAlloc(GetProcessHeap(), 0, formatsize); | 
|  | if (This->lpInFormat == NULL) | 
|  | return AVIERR_MEMORY; | 
|  | This->cbInFormat = formatsize; | 
|  | memcpy(This->lpInFormat, format, formatsize); | 
|  |  | 
|  | /* initialize formats and get compressor */ | 
|  | hr = AVIFILE_OpenCompressor(This); | 
|  | if (FAILED(hr)) | 
|  | return hr; | 
|  |  | 
|  | CONVERT_THIS_to_STREAM(&pos); | 
|  |  | 
|  | /* tell the nested stream the new format */ | 
|  | return IAVIStream_SetFormat(This->pStream, pos, This->lpOutFormat, | 
|  | This->cbOutFormat); | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI ACMStream_fnRead(IAVIStream *iface, LONG start, | 
|  | LONG samples, LPVOID buffer, | 
|  | LONG buffersize, LPLONG bytesread, | 
|  | LPLONG samplesread) | 
|  | { | 
|  | IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | 
|  |  | 
|  | HRESULT hr; | 
|  | DWORD   size; | 
|  |  | 
|  | TRACE("(%p,%d,%d,%p,%d,%p,%p)\n", iface, start, samples, buffer, | 
|  | buffersize, bytesread, samplesread); | 
|  |  | 
|  | /* clear return parameters if given */ | 
|  | if (bytesread != NULL) | 
|  | *bytesread = 0; | 
|  | if (samplesread != NULL) | 
|  | *samplesread = 0; | 
|  |  | 
|  | /* Do we have our compressor? */ | 
|  | if (This->has == NULL) { | 
|  | hr = AVIFILE_OpenCompressor(This); | 
|  |  | 
|  | if (FAILED(hr)) | 
|  | return hr; | 
|  | } | 
|  |  | 
|  | /* only need to pass through? */ | 
|  | if (This->cbInFormat == This->cbOutFormat && | 
|  | memcmp(This->lpInFormat, This->lpOutFormat, This->cbInFormat) == 0) { | 
|  | return IAVIStream_Read(This->pStream, start, samples, buffer, buffersize, | 
|  | bytesread, samplesread); | 
|  | } | 
|  |  | 
|  | /* read as much as fit? */ | 
|  | if (samples == -1) | 
|  | samples = buffersize / This->lpOutFormat->nBlockAlign; | 
|  | /* limit to buffersize */ | 
|  | if (samples * This->lpOutFormat->nBlockAlign > buffersize) | 
|  | samples = buffersize / This->lpOutFormat->nBlockAlign; | 
|  |  | 
|  | /* only return needed size? */ | 
|  | if (buffer == NULL || buffersize <= 0 || samples == 0) { | 
|  | if (bytesread == NULL && samplesread == NULL) | 
|  | return AVIERR_BADPARAM; | 
|  |  | 
|  | if (bytesread != NULL) | 
|  | *bytesread = samples * This->lpOutFormat->nBlockAlign; | 
|  | if (samplesread != NULL) | 
|  | *samplesread = samples; | 
|  |  | 
|  | return AVIERR_OK; | 
|  | } | 
|  |  | 
|  | /* map our positions to pStream positions */ | 
|  | CONVERT_THIS_to_STREAM(&start); | 
|  |  | 
|  | /* our needed internal buffersize */ | 
|  | size = samples * This->lpInFormat->nBlockAlign; | 
|  |  | 
|  | /* Need to free destination buffer used for writing? */ | 
|  | if (This->acmStreamHdr.pbDst != NULL) { | 
|  | HeapFree(GetProcessHeap(), 0, This->acmStreamHdr.pbDst); | 
|  | This->acmStreamHdr.pbDst     = NULL; | 
|  | This->acmStreamHdr.dwDstUser = 0; | 
|  | } | 
|  |  | 
|  | /* need bigger source buffer? */ | 
|  | if (This->acmStreamHdr.pbSrc == NULL || | 
|  | This->acmStreamHdr.dwSrcUser < size) { | 
|  | if (This->acmStreamHdr.pbSrc == NULL) | 
|  | This->acmStreamHdr.pbSrc = HeapAlloc(GetProcessHeap(), 0, size); | 
|  | else | 
|  | This->acmStreamHdr.pbSrc = HeapReAlloc(GetProcessHeap(), 0, This->acmStreamHdr.pbSrc, size); | 
|  | if (This->acmStreamHdr.pbSrc == NULL) | 
|  | return AVIERR_MEMORY; | 
|  | This->acmStreamHdr.dwSrcUser = size; | 
|  | } | 
|  |  | 
|  | This->acmStreamHdr.cbStruct = sizeof(This->acmStreamHdr); | 
|  | This->acmStreamHdr.cbSrcLengthUsed = 0; | 
|  | This->acmStreamHdr.cbDstLengthUsed = 0; | 
|  | This->acmStreamHdr.cbSrcLength     = size; | 
|  |  | 
|  | /* read source data */ | 
|  | hr = IAVIStream_Read(This->pStream, start, -1, This->acmStreamHdr.pbSrc, | 
|  | This->acmStreamHdr.cbSrcLength, | 
|  | (LONG *)&This->acmStreamHdr.cbSrcLength, NULL); | 
|  | if (FAILED(hr) || This->acmStreamHdr.cbSrcLength == 0) | 
|  | return hr; | 
|  |  | 
|  | /* need to prepare stream? */ | 
|  | This->acmStreamHdr.pbDst       = buffer; | 
|  | This->acmStreamHdr.cbDstLength = buffersize; | 
|  | if ((This->acmStreamHdr.fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED) == 0) { | 
|  | if (acmStreamPrepareHeader(This->has, &This->acmStreamHdr, 0) != S_OK) { | 
|  | This->acmStreamHdr.pbDst       = NULL; | 
|  | This->acmStreamHdr.cbDstLength = 0; | 
|  | return AVIERR_COMPRESSOR; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* now do the conversion */ | 
|  | /* FIXME: use ACM_CONVERTF_* flags */ | 
|  | if (acmStreamConvert(This->has, &This->acmStreamHdr, 0) != S_OK) | 
|  | hr = AVIERR_COMPRESSOR; | 
|  |  | 
|  | This->acmStreamHdr.pbDst       = NULL; | 
|  | This->acmStreamHdr.cbDstLength = 0; | 
|  |  | 
|  | /* fill out return parameters if given */ | 
|  | if (bytesread != NULL) | 
|  | *bytesread = This->acmStreamHdr.cbDstLengthUsed; | 
|  | if (samplesread != NULL) | 
|  | *samplesread = | 
|  | This->acmStreamHdr.cbDstLengthUsed / This->lpOutFormat->nBlockAlign; | 
|  |  | 
|  | return hr; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI ACMStream_fnWrite(IAVIStream *iface, LONG start, | 
|  | LONG samples, LPVOID buffer, | 
|  | LONG buffersize, DWORD flags, | 
|  | LPLONG sampwritten, | 
|  | LPLONG byteswritten) | 
|  | { | 
|  | IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | 
|  |  | 
|  | HRESULT hr; | 
|  | ULONG   size; | 
|  |  | 
|  | TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n", iface, start, samples, | 
|  | buffer, buffersize, flags, sampwritten, byteswritten); | 
|  |  | 
|  | /* clear return parameters if given */ | 
|  | if (sampwritten != NULL) | 
|  | *sampwritten = 0; | 
|  | if (byteswritten != NULL) | 
|  | *byteswritten = 0; | 
|  |  | 
|  | /* check parameters */ | 
|  | if (buffer == NULL && (buffersize > 0 || samples > 0)) | 
|  | return AVIERR_BADPARAM; | 
|  |  | 
|  | /* Have we write capability? */ | 
|  | if ((This->sInfo.dwCaps & AVIFILECAPS_CANWRITE) == 0) | 
|  | return AVIERR_READONLY; | 
|  |  | 
|  | /* also need a compressor */ | 
|  | if (This->has == NULL) | 
|  | return AVIERR_NOCOMPRESSOR; | 
|  |  | 
|  | /* map our sizes to pStream sizes */ | 
|  | size = buffersize; | 
|  | CONVERT_THIS_to_STREAM(&size); | 
|  | CONVERT_THIS_to_STREAM(&start); | 
|  |  | 
|  | /* no bytes to write? -- short circuit */ | 
|  | if (size == 0) { | 
|  | return IAVIStream_Write(This->pStream, -1, samples, buffer, size, | 
|  | flags, sampwritten, byteswritten); | 
|  | } | 
|  |  | 
|  | /* Need to free source buffer used for reading? */ | 
|  | if (This->acmStreamHdr.pbSrc != NULL) { | 
|  | HeapFree(GetProcessHeap(), 0, This->acmStreamHdr.pbSrc); | 
|  | This->acmStreamHdr.pbSrc     = NULL; | 
|  | This->acmStreamHdr.dwSrcUser = 0; | 
|  | } | 
|  |  | 
|  | /* Need bigger destination buffer? */ | 
|  | if (This->acmStreamHdr.pbDst == NULL || | 
|  | This->acmStreamHdr.dwDstUser < size) { | 
|  | if (This->acmStreamHdr.pbDst == NULL) | 
|  | This->acmStreamHdr.pbDst = HeapAlloc(GetProcessHeap(), 0, size); | 
|  | else | 
|  | This->acmStreamHdr.pbDst = HeapReAlloc(GetProcessHeap(), 0, This->acmStreamHdr.pbDst, size); | 
|  | if (This->acmStreamHdr.pbDst == NULL) | 
|  | return AVIERR_MEMORY; | 
|  | This->acmStreamHdr.dwDstUser = size; | 
|  | } | 
|  | This->acmStreamHdr.cbStruct        = sizeof(This->acmStreamHdr); | 
|  | This->acmStreamHdr.cbSrcLengthUsed = 0; | 
|  | This->acmStreamHdr.cbDstLengthUsed = 0; | 
|  | This->acmStreamHdr.cbDstLength     = This->acmStreamHdr.dwDstUser; | 
|  |  | 
|  | /* need to prepare stream? */ | 
|  | This->acmStreamHdr.pbSrc       = buffer; | 
|  | This->acmStreamHdr.cbSrcLength = buffersize; | 
|  | if ((This->acmStreamHdr.fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED) == 0) { | 
|  | if (acmStreamPrepareHeader(This->has, &This->acmStreamHdr, 0) != S_OK) { | 
|  | This->acmStreamHdr.pbSrc       = NULL; | 
|  | This->acmStreamHdr.cbSrcLength = 0; | 
|  | return AVIERR_COMPRESSOR; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* now do the conversion */ | 
|  | /* FIXME: use ACM_CONVERTF_* flags */ | 
|  | if (acmStreamConvert(This->has, &This->acmStreamHdr, 0) != S_OK) | 
|  | hr = AVIERR_COMPRESSOR; | 
|  | else | 
|  | hr = AVIERR_OK; | 
|  |  | 
|  | This->acmStreamHdr.pbSrc       = NULL; | 
|  | This->acmStreamHdr.cbSrcLength = 0; | 
|  |  | 
|  | if (FAILED(hr)) | 
|  | return hr; | 
|  |  | 
|  | return IAVIStream_Write(This->pStream,-1,This->acmStreamHdr.cbDstLengthUsed / | 
|  | This->lpOutFormat->nBlockAlign,This->acmStreamHdr.pbDst, | 
|  | This->acmStreamHdr.cbDstLengthUsed,flags,sampwritten, | 
|  | byteswritten); | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI ACMStream_fnDelete(IAVIStream *iface, LONG start, | 
|  | LONG samples) | 
|  | { | 
|  | IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | 
|  |  | 
|  | TRACE("(%p,%d,%d)\n", iface, start, samples); | 
|  |  | 
|  | /* check parameters */ | 
|  | if (start < 0 || samples < 0) | 
|  | return AVIERR_BADPARAM; | 
|  |  | 
|  | /* Delete before start of stream? */ | 
|  | if ((DWORD)(start + samples) < This->sInfo.dwStart) | 
|  | return AVIERR_OK; | 
|  |  | 
|  | /* Delete after end of stream? */ | 
|  | if ((DWORD)start > This->sInfo.dwLength) | 
|  | return AVIERR_OK; | 
|  |  | 
|  | /* For the rest we need write capability */ | 
|  | if ((This->sInfo.dwCaps & AVIFILECAPS_CANWRITE) == 0) | 
|  | return AVIERR_READONLY; | 
|  |  | 
|  | /* A compressor is also necessary */ | 
|  | if (This->has == NULL) | 
|  | return AVIERR_NOCOMPRESSOR; | 
|  |  | 
|  | /* map our positions to pStream positions */ | 
|  | CONVERT_THIS_to_STREAM(&start); | 
|  | CONVERT_THIS_to_STREAM(&samples); | 
|  |  | 
|  | return IAVIStream_Delete(This->pStream, start, samples); | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI ACMStream_fnReadData(IAVIStream *iface, DWORD fcc, | 
|  | LPVOID lp, LPLONG lpread) | 
|  | { | 
|  | IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | 
|  |  | 
|  | TRACE("(%p,0x%08X,%p,%p)\n", iface, fcc, lp, lpread); | 
|  |  | 
|  | assert(This->pStream != NULL); | 
|  |  | 
|  | return IAVIStream_ReadData(This->pStream, fcc, lp, lpread); | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI ACMStream_fnWriteData(IAVIStream *iface, DWORD fcc, | 
|  | LPVOID lp, LONG size) | 
|  | { | 
|  | IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | 
|  |  | 
|  | TRACE("(%p,0x%08x,%p,%d)\n", iface, fcc, lp, size); | 
|  |  | 
|  | assert(This->pStream != NULL); | 
|  |  | 
|  | return IAVIStream_WriteData(This->pStream, fcc, lp, size); | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI ACMStream_fnSetInfo(IAVIStream *iface, | 
|  | LPAVISTREAMINFOW info, LONG infolen) | 
|  | { | 
|  | FIXME("(%p,%p,%d): stub\n", iface, info, infolen); | 
|  |  | 
|  | return E_FAIL; | 
|  | } | 
|  |  | 
|  | /***********************************************************************/ | 
|  |  | 
|  | static HRESULT AVIFILE_OpenCompressor(IAVIStreamImpl *This) | 
|  | { | 
|  | HRESULT hr; | 
|  |  | 
|  | /* pre-conditions */ | 
|  | assert(This != NULL); | 
|  | assert(This->pStream != NULL); | 
|  |  | 
|  | if (This->has != NULL) | 
|  | return AVIERR_OK; | 
|  |  | 
|  | if (This->lpInFormat == NULL) { | 
|  | /* decode or encode the data from pStream */ | 
|  | hr = AVIStreamFormatSize(This->pStream, This->sInfo.dwStart, &This->cbInFormat); | 
|  | if (FAILED(hr)) | 
|  | return hr; | 
|  | This->lpInFormat = HeapAlloc(GetProcessHeap(), 0, This->cbInFormat); | 
|  | if (This->lpInFormat == NULL) | 
|  | return AVIERR_MEMORY; | 
|  |  | 
|  | hr = IAVIStream_ReadFormat(This->pStream, This->sInfo.dwStart, | 
|  | This->lpInFormat, &This->cbInFormat); | 
|  | if (FAILED(hr)) | 
|  | return hr; | 
|  |  | 
|  | if (This->lpOutFormat == NULL) { | 
|  | /* we must decode to default format */ | 
|  | This->cbOutFormat = sizeof(PCMWAVEFORMAT); | 
|  | This->lpOutFormat = HeapAlloc(GetProcessHeap(), 0, This->cbOutFormat); | 
|  | if (This->lpOutFormat == NULL) | 
|  | return AVIERR_MEMORY; | 
|  |  | 
|  | This->lpOutFormat->wFormatTag = WAVE_FORMAT_PCM; | 
|  | if (acmFormatSuggest(NULL, This->lpInFormat, This->lpOutFormat, | 
|  | This->cbOutFormat, ACM_FORMATSUGGESTF_WFORMATTAG) != S_OK) | 
|  | return AVIERR_NOCOMPRESSOR; | 
|  | } | 
|  | } else if (This->lpOutFormat == NULL) | 
|  | return AVIERR_ERROR; /* To what should I encode? */ | 
|  |  | 
|  | if (acmStreamOpen(&This->has, NULL, This->lpInFormat, This->lpOutFormat, | 
|  | NULL, 0, 0, ACM_STREAMOPENF_NONREALTIME) != S_OK) | 
|  | return AVIERR_NOCOMPRESSOR; | 
|  |  | 
|  | /* update AVISTREAMINFO structure */ | 
|  | This->sInfo.dwSampleSize = This->lpOutFormat->nBlockAlign; | 
|  | This->sInfo.dwScale      = This->lpOutFormat->nBlockAlign; | 
|  | This->sInfo.dwRate       = This->lpOutFormat->nAvgBytesPerSec; | 
|  | This->sInfo.dwQuality    = (DWORD)ICQUALITY_DEFAULT; | 
|  | SetRectEmpty(&This->sInfo.rcFrame); | 
|  |  | 
|  | /* convert positions and sizes to output format */ | 
|  | CONVERT_STREAM_to_THIS(&This->sInfo.dwStart); | 
|  | CONVERT_STREAM_to_THIS(&This->sInfo.dwLength); | 
|  | CONVERT_STREAM_to_THIS(&This->sInfo.dwSuggestedBufferSize); | 
|  |  | 
|  | return AVIERR_OK; | 
|  | } |