mirror of
https://github.com/reactos/reactos.git
synced 2024-08-22 03:12:51 +00:00
725 lines
20 KiB
C
725 lines
20 KiB
C
/*
|
|
* 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);
|
|
|
|
/***********************************************************************/
|
|
|
|
typedef struct _IAVIStreamImpl {
|
|
/* IUnknown stuff */
|
|
IAVIStream IAVIStream_iface;
|
|
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 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;
|
|
}
|
|
|
|
static inline IAVIStreamImpl *impl_from_IAVIStream(IAVIStream *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, IAVIStreamImpl, IAVIStream_iface);
|
|
}
|
|
|
|
static HRESULT WINAPI ACMStream_fnQueryInterface(IAVIStream *iface,
|
|
REFIID refiid, LPVOID *obj)
|
|
{
|
|
IAVIStreamImpl *This = impl_from_IAVIStream(iface);
|
|
|
|
TRACE("(%p,%s,%p)\n", iface, debugstr_guid(refiid), obj);
|
|
|
|
if (IsEqualGUID(&IID_IUnknown, refiid) ||
|
|
IsEqualGUID(&IID_IAVIStream, refiid)) {
|
|
*obj = &This->IAVIStream_iface;
|
|
IAVIStream_AddRef(iface);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
return OLE_E_ENUM_NOMORE;
|
|
}
|
|
|
|
static ULONG WINAPI ACMStream_fnAddRef(IAVIStream *iface)
|
|
{
|
|
IAVIStreamImpl *This = impl_from_IAVIStream(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 = impl_from_IAVIStream(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 = impl_from_IAVIStream(iface);
|
|
|
|
TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2);
|
|
|
|
/* check for swapped parameters */
|
|
if ((LPVOID)lParam1 != NULL &&
|
|
((LPAVICOMPRESSOPTIONS)lParam1)->fccType == streamtypeAUDIO) {
|
|
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 = impl_from_IAVIStream(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 = impl_from_IAVIStream(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 = impl_from_IAVIStream(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 = impl_from_IAVIStream(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 = impl_from_IAVIStream(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 = impl_from_IAVIStream(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 = impl_from_IAVIStream(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 = impl_from_IAVIStream(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 = impl_from_IAVIStream(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 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
|
|
};
|
|
|
|
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->IAVIStream_iface.lpVtbl = &iacmst;
|
|
|
|
hr = IAVIStream_QueryInterface(&pstream->IAVIStream_iface, riid, ppv);
|
|
if (FAILED(hr))
|
|
HeapFree(GetProcessHeap(), 0, pstream);
|
|
|
|
return hr;
|
|
}
|