New AVI streamhandlers for audio and video streams.
This commit is contained in:
parent
115260c452
commit
b0719b1273
|
@ -11,11 +11,13 @@ LDDLLFLAGS = @LDDLLFLAGS@
|
|||
SYMBOLFILE = $(MODULE).tmp.o
|
||||
|
||||
C_SRCS = \
|
||||
acmstream.c \
|
||||
api.c \
|
||||
avifile.c \
|
||||
extrachunk.c \
|
||||
factory.c \
|
||||
getframe.c \
|
||||
icmstream.c \
|
||||
wavfile.c
|
||||
|
||||
RC_SRCS = \
|
||||
|
|
|
@ -0,0 +1,733 @@
|
|||
/*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "winbase.h"
|
||||
#include "winuser.h"
|
||||
#include "winnls.h"
|
||||
#include "winerror.h"
|
||||
#include "windowsx.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);
|
||||
|
||||
struct ICOM_VTABLE(IAVIStream) iacmst = {
|
||||
ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
|
||||
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 */
|
||||
ICOM_VFIELD(IAVIStream);
|
||||
DWORD 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) { \
|
||||
acmStreamSize(This->has,(a)*This->lpInFormat->nBlockAlign,\
|
||||
&(a), ACM_STREAMSIZEF_SOURCE); \
|
||||
(a) /= This->lpOutFormat->nBlockAlign; }
|
||||
#define CONVERT_THIS_to_STREAM(a) { \
|
||||
acmStreamSize(This->has,(a)*This->lpOutFormat->nBlockAlign,\
|
||||
&(a), ACM_STREAMSIZEF_DESTINATION); \
|
||||
(a) /= This->lpInFormat->nBlockAlign; }
|
||||
|
||||
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 = (IAVIStreamImpl*)LocalAlloc(LPTR, sizeof(IAVIStreamImpl));
|
||||
if (pstream == NULL)
|
||||
return AVIERR_MEMORY;
|
||||
|
||||
ICOM_VTBL(pstream) = &iacmst;
|
||||
|
||||
hr = IUnknown_QueryInterface((IUnknown*)pstream, riid, ppv);
|
||||
if (FAILED(hr))
|
||||
LocalFree((HLOCAL)pstream);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT WINAPI ACMStream_fnQueryInterface(IAVIStream *iface,
|
||||
REFIID refiid, LPVOID *obj)
|
||||
{
|
||||
ICOM_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)
|
||||
{
|
||||
ICOM_THIS(IAVIStreamImpl,iface);
|
||||
|
||||
TRACE("(%p) -> %ld\n", iface, This->ref + 1);
|
||||
|
||||
/* also add reference to the nested stream */
|
||||
if (This->pStream != NULL)
|
||||
IAVIStream_AddRef(This->pStream);
|
||||
|
||||
return ++(This->ref);
|
||||
}
|
||||
|
||||
static ULONG WINAPI ACMStream_fnRelease(IAVIStream* iface)
|
||||
{
|
||||
ICOM_THIS(IAVIStreamImpl,iface);
|
||||
|
||||
TRACE("(%p) -> %ld\n", iface, This->ref - 1);
|
||||
|
||||
if (This->ref == 0) {
|
||||
/* destruct */
|
||||
if (This->has != (HACMSTREAM)NULL) {
|
||||
if (This->acmStreamHdr.fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED)
|
||||
acmStreamUnprepareHeader(This->has, &This->acmStreamHdr, 0);
|
||||
acmStreamClose(This->has, 0);
|
||||
This->has = (HACMSTREAM)NULL;
|
||||
}
|
||||
if (This->acmStreamHdr.pbSrc != NULL) {
|
||||
GlobalFreePtr(This->acmStreamHdr.pbSrc);
|
||||
This->acmStreamHdr.pbSrc = NULL;
|
||||
}
|
||||
if (This->acmStreamHdr.pbDst != NULL) {
|
||||
GlobalFreePtr(This->acmStreamHdr.pbDst);
|
||||
This->acmStreamHdr.pbDst = NULL;
|
||||
}
|
||||
if (This->lpInFormat != NULL) {
|
||||
GlobalFreePtr(This->lpInFormat);
|
||||
This->lpInFormat = NULL;
|
||||
This->cbInFormat = 0;
|
||||
}
|
||||
if (This->lpOutFormat != NULL) {
|
||||
GlobalFreePtr(This->lpOutFormat);
|
||||
This->lpOutFormat = NULL;
|
||||
This->cbOutFormat = 0;
|
||||
}
|
||||
if (This->pStream != NULL) {
|
||||
IAVIStream_Release(This->pStream);
|
||||
This->pStream = NULL;
|
||||
}
|
||||
LocalFree((HLOCAL)This);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* also release reference to the nested stream */
|
||||
if (This->pStream != NULL)
|
||||
IAVIStream_Release(This->pStream);
|
||||
|
||||
return --This->ref;
|
||||
}
|
||||
|
||||
/* lParam1: PAVISTREAM
|
||||
* lParam2: LPAVICOMPRESSOPTIONS -- even if doc's say LPWAVEFORMAT
|
||||
*/
|
||||
static HRESULT WINAPI ACMStream_fnCreate(IAVIStream *iface, LPARAM lParam1,
|
||||
LPARAM lParam2)
|
||||
{
|
||||
ICOM_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 = (LPWAVEFORMATEX)GlobalAllocPtr(GHND, 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)
|
||||
{
|
||||
ICOM_THIS(IAVIStreamImpl,iface);
|
||||
|
||||
TRACE("(%p,%p,%ld)\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 == (HACMSTREAM)NULL) {
|
||||
HRESULT hr = AVIFILE_OpenCompressor(This);
|
||||
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
}
|
||||
|
||||
memcpy(psi, &This->sInfo, min(size, sizeof(This->sInfo)));
|
||||
|
||||
if (size < sizeof(This->sInfo))
|
||||
return AVIERR_BUFFERTOOSMALL;
|
||||
return AVIERR_OK;
|
||||
}
|
||||
|
||||
static LONG WINAPI ACMStream_fnFindSample(IAVIStream *iface, LONG pos,
|
||||
LONG flags)
|
||||
{
|
||||
ICOM_THIS(IAVIStreamImpl,iface);
|
||||
|
||||
TRACE("(%p,%ld,0x%08lX)\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)
|
||||
{
|
||||
ICOM_THIS(IAVIStreamImpl,iface);
|
||||
|
||||
TRACE("(%p,%ld,%p,%p)\n", iface, pos, format, formatsize);
|
||||
|
||||
if (formatsize == NULL)
|
||||
return AVIERR_BADPARAM;
|
||||
|
||||
if (This->has == (HACMSTREAM)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)
|
||||
{
|
||||
ICOM_THIS(IAVIStreamImpl,iface);
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
TRACE("(%p,%ld,%p,%ld)\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 = (LPWAVEFORMATEX)GlobalAllocPtr(GMEM_MOVEABLE, 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)
|
||||
{
|
||||
ICOM_THIS(IAVIStreamImpl,iface);
|
||||
|
||||
HRESULT hr;
|
||||
LONG size;
|
||||
|
||||
TRACE("(%p,%ld,%ld,%p,%ld,%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 == (HACMSTREAM)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) {
|
||||
GlobalFreePtr(This->acmStreamHdr.pbDst);
|
||||
This->acmStreamHdr.pbDst = NULL;
|
||||
This->acmStreamHdr.dwDstUser = 0;
|
||||
}
|
||||
|
||||
/* need bigger source buffer? */
|
||||
if (This->acmStreamHdr.pbSrc == NULL ||
|
||||
This->acmStreamHdr.dwSrcUser < size) {
|
||||
This->acmStreamHdr.pbSrc = GlobalReAllocPtr(This->acmStreamHdr.pbSrc,
|
||||
size, GMEM_MOVEABLE);
|
||||
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,
|
||||
&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)
|
||||
{
|
||||
ICOM_THIS(IAVIStreamImpl,iface);
|
||||
|
||||
HRESULT hr;
|
||||
LONG size;
|
||||
|
||||
TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%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 == (HACMSTREAM)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) {
|
||||
GlobalFreePtr(This->acmStreamHdr.pbSrc);
|
||||
This->acmStreamHdr.pbSrc = NULL;
|
||||
This->acmStreamHdr.dwSrcUser = 0;
|
||||
}
|
||||
|
||||
/* Need bigger destination buffer? */
|
||||
if (This->acmStreamHdr.pbDst == NULL ||
|
||||
This->acmStreamHdr.dwDstUser < size) {
|
||||
This->acmStreamHdr.pbDst = GlobalReAllocPtr(This->acmStreamHdr.pbDst,
|
||||
size, GMEM_MOVEABLE);
|
||||
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)
|
||||
{
|
||||
ICOM_THIS(IAVIStreamImpl,iface);
|
||||
|
||||
TRACE("(%p,%ld,%ld)\n", iface, start, samples);
|
||||
|
||||
/* check parameters */
|
||||
if (start < 0 || samples < 0)
|
||||
return AVIERR_BADPARAM;
|
||||
|
||||
/* Delete before start of stream? */
|
||||
if (start + samples < This->sInfo.dwStart)
|
||||
return AVIERR_OK;
|
||||
|
||||
/* Delete after end of stream? */
|
||||
if (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 neccessary */
|
||||
if (This->has == (HACMSTREAM)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)
|
||||
{
|
||||
ICOM_THIS(IAVIStreamImpl,iface);
|
||||
|
||||
TRACE("(%p,0x%08lX,%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)
|
||||
{
|
||||
ICOM_THIS(IAVIStreamImpl,iface);
|
||||
|
||||
TRACE("(%p,0x%08lx,%p,%ld)\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,%ld): 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 != (HACMSTREAM)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 = (LPWAVEFORMATEX)GlobalAllocPtr(GMEM_MOVEABLE, 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 = (LPWAVEFORMATEX)GlobalAllocPtr(GHND, 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 = ICQUALITY_DEFAULT;
|
||||
SetRectEmpty(&This->sInfo.rcFrame);
|
||||
|
||||
/* convert positions ansd 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;
|
||||
}
|
|
@ -127,7 +127,7 @@ static HRESULT WINAPI IClassFactory_fnCreateInstance(LPCLASSFACTORY iface,
|
|||
{
|
||||
ICOM_THIS(IClassFactoryImpl,iface);
|
||||
|
||||
FIXME("(%p,%p,%s,%p): partial stub!\n", iface, pOuter, debugstr_guid(riid),
|
||||
TRACE("(%p,%p,%s,%p)\n", iface, pOuter, debugstr_guid(riid),
|
||||
ppobj);
|
||||
|
||||
if (ppobj == NULL || pOuter != NULL)
|
||||
|
@ -136,12 +136,12 @@ static HRESULT WINAPI IClassFactory_fnCreateInstance(LPCLASSFACTORY iface,
|
|||
|
||||
if (IsEqualGUID(&CLSID_AVIFile, &This->clsid))
|
||||
return AVIFILE_CreateAVIFile(riid,ppobj);
|
||||
/* if (IsEqualGUID(&CLSID_ICMStream, &This->clsid)) */
|
||||
/* return AVIFILE_CreateICMStream(riid,ppobj); */
|
||||
if (IsEqualGUID(&CLSID_ICMStream, &This->clsid))
|
||||
return AVIFILE_CreateICMStream(riid,ppobj);
|
||||
if (IsEqualGUID(&CLSID_WAVFile, &This->clsid))
|
||||
return AVIFILE_CreateWAVFile(riid,ppobj);
|
||||
/* if (IsEqualGUID(&CLSID_ACMStream, &This->clsid)) */
|
||||
/* return AVIFILE_CreateACMStream(riid,ppobj); */
|
||||
if (IsEqualGUID(&CLSID_ACMStream, &This->clsid))
|
||||
return AVIFILE_CreateACMStream(riid,ppobj);
|
||||
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,971 @@
|
|||
/*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "winbase.h"
|
||||
#include "winuser.h"
|
||||
#include "winnls.h"
|
||||
#include "winerror.h"
|
||||
#include "windowsx.h"
|
||||
#include "mmsystem.h"
|
||||
#include "vfw.h"
|
||||
#include "msacm.h"
|
||||
|
||||
#include "avifile_private.h"
|
||||
|
||||
#include "wine/debug.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(avifile);
|
||||
|
||||
#define MAX_FRAMESIZE (16 * 1024 * 1024)
|
||||
#define MAX_FRAMESIZE_DIFF 512
|
||||
|
||||
/***********************************************************************/
|
||||
|
||||
static HRESULT WINAPI ICMStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID *obj);
|
||||
static ULONG WINAPI ICMStream_fnAddRef(IAVIStream*iface);
|
||||
static ULONG WINAPI ICMStream_fnRelease(IAVIStream* iface);
|
||||
static HRESULT WINAPI ICMStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2);
|
||||
static HRESULT WINAPI ICMStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size);
|
||||
static LONG WINAPI ICMStream_fnFindSample(IAVIStream*iface,LONG pos,LONG flags);
|
||||
static HRESULT WINAPI ICMStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG *formatsize);
|
||||
static HRESULT WINAPI ICMStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize);
|
||||
static HRESULT WINAPI ICMStream_fnRead(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG *bytesread,LONG *samplesread);
|
||||
static HRESULT WINAPI ICMStream_fnWrite(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG *sampwritten,LONG *byteswritten);
|
||||
static HRESULT WINAPI ICMStream_fnDelete(IAVIStream*iface,LONG start,LONG samples);
|
||||
static HRESULT WINAPI ICMStream_fnReadData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG *lpread);
|
||||
static HRESULT WINAPI ICMStream_fnWriteData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG size);
|
||||
static HRESULT WINAPI ICMStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen);
|
||||
|
||||
struct ICOM_VTABLE(IAVIStream) iicmst = {
|
||||
ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
|
||||
ICMStream_fnQueryInterface,
|
||||
ICMStream_fnAddRef,
|
||||
ICMStream_fnRelease,
|
||||
ICMStream_fnCreate,
|
||||
ICMStream_fnInfo,
|
||||
ICMStream_fnFindSample,
|
||||
ICMStream_fnReadFormat,
|
||||
ICMStream_fnSetFormat,
|
||||
ICMStream_fnRead,
|
||||
ICMStream_fnWrite,
|
||||
ICMStream_fnDelete,
|
||||
ICMStream_fnReadData,
|
||||
ICMStream_fnWriteData,
|
||||
ICMStream_fnSetInfo
|
||||
};
|
||||
|
||||
typedef struct _IAVIStreamImpl {
|
||||
/* IUnknown stuff */
|
||||
ICOM_VFIELD(IAVIStream);
|
||||
DWORD ref;
|
||||
|
||||
/* IAVIStream stuff */
|
||||
PAVISTREAM pStream;
|
||||
AVISTREAMINFOW sInfo;
|
||||
|
||||
PGETFRAME pg;
|
||||
HIC hic;
|
||||
DWORD dwICMFlags;
|
||||
|
||||
LONG lCurrent;
|
||||
LONG lLastKey;
|
||||
LONG lKeyFrameEvery;
|
||||
DWORD dwLastQuality;
|
||||
DWORD dwBytesPerFrame;
|
||||
DWORD dwUnusedBytes;
|
||||
|
||||
LPBITMAPINFOHEADER lpbiCur; /* current frame */
|
||||
LPVOID lpCur;
|
||||
LPBITMAPINFOHEADER lpbiPrev; /* previous frame */
|
||||
LPVOID lpPrev;
|
||||
|
||||
LPBITMAPINFOHEADER lpbiOutput; /* output format of codec */
|
||||
LONG cbOutput;
|
||||
LPBITMAPINFOHEADER lpbiInput; /* input format for codec */
|
||||
LONG cbInput;
|
||||
} IAVIStreamImpl;
|
||||
|
||||
/***********************************************************************/
|
||||
|
||||
static HRESULT AVIFILE_EncodeFrame(IAVIStreamImpl *This,
|
||||
LPBITMAPINFOHEADER lpbi, LPVOID lpBits);
|
||||
static HRESULT AVIFILE_OpenGetFrame(IAVIStreamImpl *This);
|
||||
|
||||
inline void AVIFILE_Reset(IAVIStreamImpl *This)
|
||||
{
|
||||
This->lCurrent = -1;
|
||||
This->lLastKey = 0;
|
||||
This->dwLastQuality = ICQUALITY_HIGH;
|
||||
This->dwUnusedBytes = 0;
|
||||
}
|
||||
|
||||
HRESULT AVIFILE_CreateICMStream(REFIID riid, LPVOID *ppv)
|
||||
{
|
||||
IAVIStreamImpl *pstream;
|
||||
HRESULT hr;
|
||||
|
||||
assert(riid != NULL && ppv != NULL);
|
||||
|
||||
*ppv = NULL;
|
||||
|
||||
pstream = (IAVIStreamImpl*)LocalAlloc(LPTR, sizeof(IAVIStreamImpl));
|
||||
if (pstream == NULL)
|
||||
return AVIERR_MEMORY;
|
||||
|
||||
ICOM_VTBL(pstream) = &iicmst;
|
||||
AVIFILE_Reset(pstream);
|
||||
|
||||
hr = IUnknown_QueryInterface((IUnknown*)pstream, riid, ppv);
|
||||
if (FAILED(hr))
|
||||
LocalFree((HLOCAL)pstream);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT WINAPI ICMStream_fnQueryInterface(IAVIStream *iface,
|
||||
REFIID refiid, LPVOID *obj)
|
||||
{
|
||||
ICOM_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 ICMStream_fnAddRef(IAVIStream *iface)
|
||||
{
|
||||
ICOM_THIS(IAVIStreamImpl,iface);
|
||||
|
||||
TRACE("(%p) -> %ld\n", iface, This->ref + 1);
|
||||
|
||||
/* also add reference to the nested stream */
|
||||
if (This->pStream != NULL)
|
||||
IAVIStream_AddRef(This->pStream);
|
||||
|
||||
return ++(This->ref);
|
||||
}
|
||||
|
||||
static ULONG WINAPI ICMStream_fnRelease(IAVIStream* iface)
|
||||
{
|
||||
ICOM_THIS(IAVIStreamImpl,iface);
|
||||
|
||||
TRACE("(%p) -> %ld\n", iface, This->ref - 1);
|
||||
|
||||
if (This->ref == 0) {
|
||||
/* destruct */
|
||||
if (This->pg != NULL) {
|
||||
AVIStreamGetFrameClose(This->pg);
|
||||
This->pg = NULL;
|
||||
}
|
||||
if (This->pStream != NULL) {
|
||||
IAVIStream_Release(This->pStream);
|
||||
This->pStream = NULL;
|
||||
}
|
||||
if (This->hic != (HIC)NULL) {
|
||||
if (This->lpbiPrev != NULL) {
|
||||
ICDecompressEnd(This->hic);
|
||||
GlobalFreePtr(This->lpbiPrev);
|
||||
This->lpbiPrev = NULL;
|
||||
This->lpPrev = NULL;
|
||||
}
|
||||
ICCompressEnd(This->hic);
|
||||
This->hic = (HIC)NULL;
|
||||
}
|
||||
if (This->lpbiCur != NULL) {
|
||||
GlobalFreePtr(This->lpbiCur);
|
||||
This->lpbiCur = NULL;
|
||||
This->lpCur = NULL;
|
||||
}
|
||||
if (This->lpbiOutput != NULL) {
|
||||
GlobalFreePtr(This->lpbiOutput);
|
||||
This->lpbiOutput = NULL;
|
||||
This->cbOutput = 0;
|
||||
}
|
||||
if (This->lpbiInput != NULL) {
|
||||
GlobalFreePtr(This->lpbiInput);
|
||||
This->lpbiInput = NULL;
|
||||
This->cbInput = 0;
|
||||
}
|
||||
|
||||
LocalFree((HLOCAL)This);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* also release reference to the nested stream */
|
||||
if (This->pStream != NULL)
|
||||
IAVIStream_Release(This->pStream);
|
||||
|
||||
return --This->ref;
|
||||
}
|
||||
|
||||
/* lParam1: PAVISTREAM
|
||||
* lParam2: LPAVICOMPRESSOPTIONS
|
||||
*/
|
||||
static HRESULT WINAPI ICMStream_fnCreate(IAVIStream *iface, LPARAM lParam1,
|
||||
LPARAM lParam2)
|
||||
{
|
||||
ICOM_THIS(IAVIStreamImpl,iface);
|
||||
|
||||
ICINFO icinfo;
|
||||
ICCOMPRESSFRAMES icFrames;
|
||||
LPAVICOMPRESSOPTIONS pco = (LPAVICOMPRESSOPTIONS)lParam2;
|
||||
|
||||
TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2);
|
||||
|
||||
/* check parameter */
|
||||
if ((LPVOID)lParam1 == NULL)
|
||||
return AVIERR_BADPARAM;
|
||||
|
||||
/* get infos from stream */
|
||||
IAVIStream_Info((PAVISTREAM)lParam1, &This->sInfo, sizeof(This->sInfo));
|
||||
if (This->sInfo.fccType != streamtypeVIDEO)
|
||||
return AVIERR_ERROR; /* error in registry or AVIMakeCompressedStream */
|
||||
|
||||
/* add reference to the stream */
|
||||
This->pStream = (PAVISTREAM)lParam1;
|
||||
IAVIStream_AddRef(This->pStream);
|
||||
|
||||
AVIFILE_Reset(This);
|
||||
|
||||
if (pco != NULL && pco->fccHandler != comptypeDIB) {
|
||||
/* we should compress */
|
||||
This->sInfo.fccHandler = pco->fccHandler;
|
||||
|
||||
This->hic = ICOpen(ICTYPE_VIDEO, pco->fccHandler, ICMODE_COMPRESS);
|
||||
if (This->hic == (HIC)NULL)
|
||||
return AVIERR_NOCOMPRESSOR;
|
||||
|
||||
/* restore saved state of codec */
|
||||
if (pco->cbParms > 0 && pco->lpParms != NULL) {
|
||||
ICSetState(This->hic, pco->lpParms, pco->cbParms);
|
||||
}
|
||||
|
||||
/* set quality -- resolve default quality */
|
||||
This->sInfo.dwQuality = pco->dwQuality;
|
||||
if (pco->dwQuality == ICQUALITY_DEFAULT)
|
||||
This->sInfo.dwQuality = ICGetDefaultQuality(This->hic);
|
||||
|
||||
/* get capabilities of codec */
|
||||
ICGetInfo(This->hic, &icinfo, sizeof(icinfo));
|
||||
This->dwICMFlags = icinfo.dwFlags;
|
||||
|
||||
/* use keyframes? */
|
||||
if ((pco->dwFlags & AVICOMPRESSF_KEYFRAMES) &&
|
||||
(icinfo.dwFlags & (VIDCF_TEMPORAL|VIDCF_FASTTEMPORALC))) {
|
||||
This->lKeyFrameEvery = pco->dwKeyFrameEvery;
|
||||
} else
|
||||
This->lKeyFrameEvery = 1;
|
||||
|
||||
/* use datarate? */
|
||||
if ((pco->dwFlags & AVICOMPRESSF_DATARATE)) {
|
||||
/* Do we have a chance to reduce size to desired one? */
|
||||
if ((icinfo.dwFlags & (VIDCF_CRUNCH|VIDCF_QUALITY)) == 0)
|
||||
return AVIERR_NOCOMPRESSOR;
|
||||
|
||||
assert(This->sInfo.dwRate != 0);
|
||||
|
||||
This->dwBytesPerFrame = MulDiv(pco->dwBytesPerSecond,
|
||||
This->sInfo.dwScale, This->sInfo.dwRate);
|
||||
} else {
|
||||
pco->dwBytesPerSecond = 0;
|
||||
This->dwBytesPerFrame = 0;
|
||||
}
|
||||
|
||||
if (icinfo.dwFlags & VIDCF_COMPRESSFRAMES) {
|
||||
memset(&icFrames, 0, sizeof(icFrames));
|
||||
icFrames.lpbiOutput = This->lpbiOutput;
|
||||
icFrames.lpbiInput = This->lpbiInput;
|
||||
icFrames.lFrameCount = This->sInfo.dwLength;
|
||||
icFrames.lQuality = This->sInfo.dwQuality;
|
||||
icFrames.lDataRate = pco->dwBytesPerSecond;
|
||||
icFrames.lKeyRate = This->lKeyFrameEvery;
|
||||
icFrames.dwRate = This->sInfo.dwRate;
|
||||
icFrames.dwScale = This->sInfo.dwScale;
|
||||
ICSendMessage(This->hic, ICM_COMPRESS_FRAMES_INFO,
|
||||
(LPARAM)&icFrames, (LPARAM)sizeof(icFrames));
|
||||
}
|
||||
} else
|
||||
This->sInfo.fccHandler = comptypeDIB;
|
||||
|
||||
return AVIERR_OK;
|
||||
}
|
||||
|
||||
static HRESULT WINAPI ICMStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi,
|
||||
LONG size)
|
||||
{
|
||||
ICOM_THIS(IAVIStreamImpl,iface);
|
||||
|
||||
TRACE("(%p,%p,%ld)\n", iface, psi, size);
|
||||
|
||||
if (psi == NULL)
|
||||
return AVIERR_BADPARAM;
|
||||
if (size < 0)
|
||||
return AVIERR_BADSIZE;
|
||||
|
||||
memcpy(psi, &This->sInfo, min(size, sizeof(This->sInfo)));
|
||||
|
||||
if (size < sizeof(This->sInfo))
|
||||
return AVIERR_BUFFERTOOSMALL;
|
||||
return AVIERR_OK;
|
||||
}
|
||||
|
||||
static LONG WINAPI ICMStream_fnFindSample(IAVIStream *iface, LONG pos,
|
||||
LONG flags)
|
||||
{
|
||||
ICOM_THIS(IAVIStreamImpl,iface);
|
||||
|
||||
TRACE("(%p,%ld,0x%08lX)\n",iface,pos,flags);
|
||||
|
||||
if (flags & FIND_FROM_START) {
|
||||
pos = This->sInfo.dwStart;
|
||||
flags &= ~(FIND_FROM_START|FIND_PREV);
|
||||
flags |= FIND_NEXT;
|
||||
}
|
||||
|
||||
if (flags & FIND_RET)
|
||||
WARN(": FIND_RET flags will be ignored!\n");
|
||||
|
||||
if (flags & FIND_KEY) {
|
||||
if (This->hic == (HIC)NULL)
|
||||
return pos; /* we decompress so every frame is a keyframe */
|
||||
|
||||
if (flags & FIND_PREV) {
|
||||
/* need to read old or new frames? */
|
||||
if (This->lLastKey <= pos || pos < This->lCurrent)
|
||||
IAVIStream_Read(iface, pos, 1, NULL, 0, NULL, NULL);
|
||||
|
||||
return This->lLastKey;
|
||||
}
|
||||
} else if (flags & FIND_ANY) {
|
||||
return pos; /* We really don't know, reread is to expensive, so guess. */
|
||||
} else if (flags & FIND_FORMAT) {
|
||||
if (flags & FIND_PREV)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static HRESULT WINAPI ICMStream_fnReadFormat(IAVIStream *iface, LONG pos,
|
||||
LPVOID format, LONG *formatsize)
|
||||
{
|
||||
ICOM_THIS(IAVIStreamImpl,iface);
|
||||
|
||||
LPBITMAPINFOHEADER lpbi;
|
||||
HRESULT hr;
|
||||
|
||||
TRACE("(%p,%ld,%p,%p)\n", iface, pos, format, formatsize);
|
||||
|
||||
if (formatsize == NULL)
|
||||
return AVIERR_BADPARAM;
|
||||
|
||||
if (This->pg == NULL) {
|
||||
hr = AVIFILE_OpenGetFrame(This);
|
||||
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
}
|
||||
|
||||
lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, pos);
|
||||
if (lpbi == NULL)
|
||||
return AVIERR_MEMORY;
|
||||
|
||||
if (This->hic == (HIC)NULL) {
|
||||
LONG size = lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD);
|
||||
|
||||
if (size > 0) {
|
||||
if (This->sInfo.dwSuggestedBufferSize < lpbi->biSizeImage)
|
||||
This->sInfo.dwSuggestedBufferSize = lpbi->biSizeImage;
|
||||
|
||||
This->cbOutput = size;
|
||||
if (format != NULL) {
|
||||
if (This->lpbiOutput != NULL)
|
||||
memcpy(format, This->lpbiOutput, min(*formatsize, This->cbOutput));
|
||||
else
|
||||
memcpy(format, lpbi, min(*formatsize, size));
|
||||
}
|
||||
}
|
||||
} else if (format != NULL)
|
||||
memcpy(format, This->lpbiOutput, min(*formatsize, This->cbOutput));
|
||||
|
||||
if (*formatsize < This->cbOutput)
|
||||
hr = AVIERR_BUFFERTOOSMALL;
|
||||
else
|
||||
hr = AVIERR_OK;
|
||||
|
||||
*formatsize = This->cbOutput;
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT WINAPI ICMStream_fnSetFormat(IAVIStream *iface, LONG pos,
|
||||
LPVOID format, LONG formatsize)
|
||||
{
|
||||
ICOM_THIS(IAVIStreamImpl,iface);
|
||||
|
||||
TRACE("(%p,%ld,%p,%ld)\n", iface, pos, format, formatsize);
|
||||
|
||||
/* check parameters */
|
||||
if (format == NULL || formatsize <= 0)
|
||||
return AVIERR_BADPARAM;
|
||||
|
||||
/* We can only accept RGB data for writing */
|
||||
if (((LPBITMAPINFOHEADER)format)->biCompression != BI_RGB) {
|
||||
WARN(": need RGB data as input\n");
|
||||
return AVIERR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
/* Input format already known?
|
||||
* Changing of palette is supported, but be quiet if it's the same */
|
||||
if (This->lpbiInput != NULL) {
|
||||
if (This->cbInput != formatsize)
|
||||
return AVIERR_UNSUPPORTED;
|
||||
|
||||
if (memcmp(format, This->lpbiInput, formatsize) == 0)
|
||||
return AVIERR_OK;
|
||||
}
|
||||
|
||||
/* Does the nested stream support writing? */
|
||||
if ((This->sInfo.dwCaps & AVIFILECAPS_CANWRITE) == 0)
|
||||
return AVIERR_READONLY;
|
||||
|
||||
/* check if frame is already written */
|
||||
if (This->sInfo.dwLength + This->sInfo.dwStart > pos)
|
||||
return AVIERR_UNSUPPORTED;
|
||||
|
||||
/* check if we should compress */
|
||||
if (This->sInfo.fccHandler == 0 ||
|
||||
This->sInfo.fccHandler == mmioFOURCC('N','O','N','E'))
|
||||
This->sInfo.fccHandler = comptypeDIB;
|
||||
|
||||
/* only pass through? */
|
||||
if (This->sInfo.fccHandler == comptypeDIB)
|
||||
return IAVIStream_SetFormat(This->pStream, pos, format, formatsize);
|
||||
|
||||
/* initial format setting? */
|
||||
if (This->lpbiInput == NULL) {
|
||||
LONG size;
|
||||
|
||||
assert(This->hic != (HIC)NULL);
|
||||
|
||||
/* get memory for input format */
|
||||
This->lpbiInput = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, formatsize);
|
||||
if (This->lpbiInput == NULL)
|
||||
return AVIERR_MEMORY;
|
||||
This->cbInput = formatsize;
|
||||
memcpy(This->lpbiInput, format, formatsize);
|
||||
|
||||
/* get output format */
|
||||
size = ICCompressGetFormatSize(This->hic, This->lpbiInput);
|
||||
if (size < sizeof(BITMAPINFOHEADER))
|
||||
return AVIERR_COMPRESSOR;
|
||||
This->lpbiOutput = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size);
|
||||
if (This->lpbiOutput == NULL)
|
||||
return AVIERR_MEMORY;
|
||||
This->cbOutput = size;
|
||||
if (ICCompressGetFormat(This->hic,This->lpbiInput,This->lpbiOutput) < S_OK)
|
||||
return AVIERR_COMPRESSOR;
|
||||
|
||||
/* update AVISTREAMINFO structure */
|
||||
This->sInfo.rcFrame.right =
|
||||
This->sInfo.rcFrame.left + This->lpbiOutput->biWidth;
|
||||
This->sInfo.rcFrame.bottom =
|
||||
This->sInfo.rcFrame.top + This->lpbiOutput->biHeight;
|
||||
|
||||
/* prepare codec for compression */
|
||||
if (ICCompressBegin(This->hic, This->lpbiInput, This->lpbiOutput) != S_OK)
|
||||
return AVIERR_COMPRESSOR;
|
||||
|
||||
/* allocate memory for compressed frame */
|
||||
size = ICCompressGetSize(This->hic, This->lpbiInput, This->lpbiOutput);
|
||||
This->lpbiCur =
|
||||
(LPBITMAPINFOHEADER)GlobalAllocPtr(GMEM_MOVEABLE, This->cbOutput + size);
|
||||
if (This->lpbiCur == NULL)
|
||||
return AVIERR_MEMORY;
|
||||
memcpy(This->lpbiCur, This->lpbiOutput, This->cbOutput);
|
||||
This->lpCur = DIBPTR(This->lpbiCur);
|
||||
|
||||
/* allocate memory for last frame if needed */
|
||||
if (This->lKeyFrameEvery != 1 &&
|
||||
(This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) {
|
||||
size = ICDecompressGetFormatSize(This->hic, This->lpbiOutput);
|
||||
This->lpbiPrev = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size);
|
||||
if (This->lpbiPrev == NULL)
|
||||
return AVIERR_MEMORY;
|
||||
if (ICDecompressGetFormat(This->hic, This->lpbiOutput, This->lpbiPrev) < S_OK)
|
||||
return AVIERR_COMPRESSOR;
|
||||
|
||||
if (This->lpbiPrev->biSizeImage == 0) {
|
||||
This->lpbiPrev->biSizeImage =
|
||||
DIBWIDTHBYTES(*This->lpbiPrev) * This->lpbiPrev->biHeight;
|
||||
}
|
||||
|
||||
/* get memory for format and picture */
|
||||
size += This->lpbiPrev->biSizeImage;
|
||||
This->lpbiPrev =
|
||||
(LPBITMAPINFOHEADER)GlobalReAllocPtr(This->lpbiPrev,size,GMEM_MOVEABLE);
|
||||
if (This->lpbiPrev == NULL)
|
||||
return AVIERR_MEMORY;
|
||||
This->lpPrev = DIBPTR(This->lpbiPrev);
|
||||
|
||||
/* prepare codec also for decompression */
|
||||
if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK)
|
||||
return AVIERR_COMPRESSOR;
|
||||
}
|
||||
} else {
|
||||
/* format change -- check that's only the palette */
|
||||
LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)format;
|
||||
|
||||
if (lpbi->biSize != This->lpbiInput->biSize ||
|
||||
lpbi->biWidth != This->lpbiInput->biWidth ||
|
||||
lpbi->biHeight != This->lpbiInput->biHeight ||
|
||||
lpbi->biBitCount != This->lpbiInput->biBitCount ||
|
||||
lpbi->biPlanes != This->lpbiInput->biPlanes ||
|
||||
lpbi->biCompression != This->lpbiInput->biCompression ||
|
||||
lpbi->biClrUsed != This->lpbiInput->biClrUsed)
|
||||
return AVIERR_UNSUPPORTED;
|
||||
|
||||
/* get new output format */
|
||||
if (ICCompressGetFormat(This->hic, lpbi, This->lpbiOutput) < S_OK)
|
||||
return AVIERR_BADFORMAT;
|
||||
|
||||
/* restart compression */
|
||||
ICCompressEnd(This->hic);
|
||||
if (ICCompressBegin(This->hic, lpbi, This->lpbiOutput) != S_OK)
|
||||
return AVIERR_COMPRESSOR;
|
||||
|
||||
/* check if we need to restart decompresion also */
|
||||
if (This->lKeyFrameEvery != 1 &&
|
||||
(This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) {
|
||||
ICDecompressEnd(This->hic);
|
||||
if (ICDecompressGetFormat(This->hic,This->lpbiOutput,This->lpbiPrev) < S_OK)
|
||||
return AVIERR_COMPRESSOR;
|
||||
if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK)
|
||||
return AVIERR_COMPRESSOR;
|
||||
}
|
||||
}
|
||||
|
||||
/* tell nested stream the new format */
|
||||
return IAVIStream_SetFormat(This->pStream, pos,
|
||||
This->lpbiOutput, This->cbOutput);
|
||||
}
|
||||
|
||||
static HRESULT WINAPI ICMStream_fnRead(IAVIStream *iface, LONG start,
|
||||
LONG samples, LPVOID buffer,
|
||||
LONG buffersize, LPLONG bytesread,
|
||||
LPLONG samplesread)
|
||||
{
|
||||
ICOM_THIS(IAVIStreamImpl,iface);
|
||||
|
||||
LPBITMAPINFOHEADER lpbi;
|
||||
|
||||
TRACE("(%p,%ld,%ld,%p,%ld,%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;
|
||||
|
||||
if (samples == 0)
|
||||
return AVIERR_OK;
|
||||
|
||||
/* check parameters */
|
||||
if (samples != 1 && (bytesread == NULL && samplesread == NULL))
|
||||
return AVIERR_BADPARAM;
|
||||
if (samples == -1) /* read as much as we could */
|
||||
samples = 1;
|
||||
|
||||
if (This->pg == NULL) {
|
||||
HRESULT hr = AVIFILE_OpenGetFrame(This);
|
||||
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* compress or decompress? */
|
||||
if (This->hic == (HIC)NULL) {
|
||||
/* decompress */
|
||||
lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, start);
|
||||
if (lpbi == NULL)
|
||||
return AVIERR_MEMORY;
|
||||
|
||||
if (buffer != NULL && buffersize > 0) {
|
||||
/* check buffersize */
|
||||
if (buffersize < lpbi->biSizeImage)
|
||||
return AVIERR_BUFFERTOOSMALL;
|
||||
|
||||
memcpy(buffer, DIBPTR(lpbi), lpbi->biSizeImage);
|
||||
}
|
||||
|
||||
/* fill out return parameters if given */
|
||||
if (bytesread != NULL)
|
||||
*bytesread = lpbi->biSizeImage;
|
||||
} else {
|
||||
/* compress */
|
||||
if (This->lCurrent > start)
|
||||
AVIFILE_Reset(This);
|
||||
|
||||
while (start > This->lCurrent) {
|
||||
HRESULT hr;
|
||||
|
||||
lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, ++This->lCurrent);
|
||||
if (lpbi == NULL) {
|
||||
AVIFILE_Reset(This);
|
||||
return AVIERR_MEMORY;
|
||||
}
|
||||
|
||||
hr = AVIFILE_EncodeFrame(This, lpbi, DIBPTR(lpbi));
|
||||
if (FAILED(hr)) {
|
||||
AVIFILE_Reset(This);
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer != NULL && buffersize > 0) {
|
||||
/* check buffersize */
|
||||
if (This->lpbiCur->biSizeImage > buffersize)
|
||||
return AVIERR_BUFFERTOOSMALL;
|
||||
|
||||
memcpy(buffer, This->lpCur, This->lpbiCur->biSizeImage);
|
||||
}
|
||||
|
||||
/* fill out return parameters if given */
|
||||
if (bytesread != NULL)
|
||||
*bytesread = This->lpbiCur->biSizeImage;
|
||||
}
|
||||
|
||||
/* fill out return parameters if given */
|
||||
if (samplesread != NULL)
|
||||
*samplesread = 1;
|
||||
|
||||
return AVIERR_OK;
|
||||
}
|
||||
|
||||
static HRESULT WINAPI ICMStream_fnWrite(IAVIStream *iface, LONG start,
|
||||
LONG samples, LPVOID buffer,
|
||||
LONG buffersize, DWORD flags,
|
||||
LPLONG sampwritten,
|
||||
LPLONG byteswritten)
|
||||
{
|
||||
ICOM_THIS(IAVIStreamImpl,iface);
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%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;
|
||||
|
||||
if (This->sInfo.fccHandler == comptypeDIB) {
|
||||
/* only pass through */
|
||||
flags |= AVIIF_KEYFRAME;
|
||||
|
||||
return IAVIStream_Write(This->pStream, start, samples, buffer, buffersize,
|
||||
flags, sampwritten, byteswritten);
|
||||
} else {
|
||||
/* compress data before writing to pStream */
|
||||
if (samples != 1 && (sampwritten == NULL && byteswritten == NULL))
|
||||
return AVIERR_UNSUPPORTED;
|
||||
|
||||
This->lCurrent = start;
|
||||
hr = AVIFILE_EncodeFrame(This, This->lpbiInput, buffer);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
|
||||
if (This->lLastKey == start)
|
||||
flags |= AVIIF_KEYFRAME;
|
||||
|
||||
return IAVIStream_Write(This->pStream, start, samples, This->lpCur,
|
||||
This->lpbiCur->biSizeImage, flags, byteswritten,
|
||||
sampwritten);
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT WINAPI ICMStream_fnDelete(IAVIStream *iface, LONG start,
|
||||
LONG samples)
|
||||
{
|
||||
ICOM_THIS(IAVIStreamImpl,iface);
|
||||
|
||||
TRACE("(%p,%ld,%ld)\n", iface, start, samples);
|
||||
|
||||
return IAVIStream_Delete(This->pStream, start, samples);
|
||||
}
|
||||
|
||||
static HRESULT WINAPI ICMStream_fnReadData(IAVIStream *iface, DWORD fcc,
|
||||
LPVOID lp, LPLONG lpread)
|
||||
{
|
||||
ICOM_THIS(IAVIStreamImpl,iface);
|
||||
|
||||
TRACE("(%p,0x%08lX,%p,%p)\n", iface, fcc, lp, lpread);
|
||||
|
||||
assert(This->pStream != NULL);
|
||||
|
||||
return IAVIStream_ReadData(This->pStream, fcc, lp, lpread);
|
||||
}
|
||||
|
||||
static HRESULT WINAPI ICMStream_fnWriteData(IAVIStream *iface, DWORD fcc,
|
||||
LPVOID lp, LONG size)
|
||||
{
|
||||
ICOM_THIS(IAVIStreamImpl,iface);
|
||||
|
||||
TRACE("(%p,0x%08lx,%p,%ld)\n", iface, fcc, lp, size);
|
||||
|
||||
assert(This->pStream != NULL);
|
||||
|
||||
return IAVIStream_WriteData(This->pStream, fcc, lp, size);
|
||||
}
|
||||
|
||||
static HRESULT WINAPI ICMStream_fnSetInfo(IAVIStream *iface,
|
||||
LPAVISTREAMINFOW info, LONG infolen)
|
||||
{
|
||||
FIXME("(%p,%p,%ld): stub\n", iface, info, infolen);
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
/***********************************************************************/
|
||||
|
||||
static HRESULT AVIFILE_EncodeFrame(IAVIStreamImpl *This,
|
||||
LPBITMAPINFOHEADER lpbi, LPVOID lpBits)
|
||||
{
|
||||
DWORD dwMinQual, dwMaxQual, dwCurQual;
|
||||
DWORD dwRequest;
|
||||
DWORD icmFlags = 0;
|
||||
DWORD idxFlags = 0;
|
||||
BOOL bDecreasedQual = FALSE;
|
||||
BOOL doSizeCheck;
|
||||
BOOL noPrev;
|
||||
|
||||
/* make lKeyFrameEvery and at start a keyframe */
|
||||
if ((This->lKeyFrameEvery != 0 &&
|
||||
(This->lCurrent - This->lLastKey) >= This->lKeyFrameEvery) ||
|
||||
This->lCurrent == This->sInfo.dwStart) {
|
||||
idxFlags = AVIIF_KEYFRAME;
|
||||
icmFlags = ICCOMPRESS_KEYFRAME;
|
||||
}
|
||||
|
||||
if (This->lKeyFrameEvery != 0) {
|
||||
if (This->lCurrent == This->sInfo.dwStart) {
|
||||
if (idxFlags & AVIIF_KEYFRAME) {
|
||||
/* for keyframes allow to consume all unused bytes */
|
||||
dwRequest = This->dwBytesPerFrame + This->dwUnusedBytes;
|
||||
This->dwUnusedBytes = 0;
|
||||
} else {
|
||||
/* for non-keyframes only allow something of the unused bytes to be consumed */
|
||||
DWORD tmp1 = 0;
|
||||
DWORD tmp2;
|
||||
|
||||
if (This->dwBytesPerFrame >= This->dwUnusedBytes)
|
||||
tmp1 = This->dwBytesPerFrame / This->lKeyFrameEvery;
|
||||
tmp2 = (This->dwUnusedBytes + tmp1) / This->lKeyFrameEvery;
|
||||
|
||||
dwRequest = This->dwBytesPerFrame - tmp1 + tmp2;
|
||||
This->dwUnusedBytes -= tmp2;
|
||||
}
|
||||
} else
|
||||
dwRequest = MAX_FRAMESIZE;
|
||||
} else {
|
||||
/* only one keyframe at start desired */
|
||||
if (This->lCurrent == This->sInfo.dwStart) {
|
||||
dwRequest = This->dwBytesPerFrame + This->dwUnusedBytes;
|
||||
This->dwUnusedBytes = 0;
|
||||
} else
|
||||
dwRequest = MAX_FRAMESIZE;
|
||||
}
|
||||
|
||||
/* must we check for framesize to gain requested
|
||||
* datarate or could we trust codec? */
|
||||
doSizeCheck = (dwRequest != 0 && ((This->dwICMFlags & (VIDCF_CRUNCH|VIDCF_QUALITY)) == 0));
|
||||
|
||||
dwMaxQual = dwCurQual = This->sInfo.dwQuality;
|
||||
dwMinQual = ICQUALITY_LOW;
|
||||
|
||||
noPrev = TRUE;
|
||||
if ((icmFlags & ICCOMPRESS_KEYFRAME) == 0 &&
|
||||
(This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0)
|
||||
noPrev = FALSE;
|
||||
|
||||
do {
|
||||
DWORD idxCkid = 0;
|
||||
HRESULT hr;
|
||||
|
||||
hr = ICCompress(This->hic,icmFlags,This->lpbiCur,This->lpCur,lpbi,lpBits,
|
||||
&idxCkid, &idxFlags, This->lCurrent, dwRequest, dwCurQual,
|
||||
noPrev ? NULL:This->lpbiPrev, noPrev ? NULL:This->lpPrev);
|
||||
if (hr == ICERR_NEWPALETTE) {
|
||||
FIXME(": codec has changed palette -- unhandled!\n");
|
||||
} else if (hr != ICERR_OK)
|
||||
return AVIERR_COMPRESSOR;
|
||||
|
||||
/* need to check for framesize */
|
||||
if (! doSizeCheck)
|
||||
break;
|
||||
|
||||
if (dwRequest >= This->lpbiCur->biSizeImage) {
|
||||
/* frame is smaller -- try to maximize quality */
|
||||
if (dwMaxQual - dwCurQual > 10) {
|
||||
DWORD tmp = dwRequest / 8;
|
||||
|
||||
if (tmp < MAX_FRAMESIZE_DIFF)
|
||||
tmp = MAX_FRAMESIZE_DIFF;
|
||||
|
||||
if (tmp < dwRequest - This->lpbiCur->biSizeImage && bDecreasedQual) {
|
||||
tmp = dwCurQual;
|
||||
dwCurQual = (dwMinQual + dwMaxQual) / 2;
|
||||
dwMinQual = tmp;
|
||||
continue;
|
||||
}
|
||||
} else
|
||||
break;
|
||||
} else if (dwMaxQual - dwMinQual <= 1) {
|
||||
break;
|
||||
} else {
|
||||
dwMaxQual = dwCurQual;
|
||||
|
||||
if (bDecreasedQual || dwCurQual == This->dwLastQuality)
|
||||
dwCurQual = (dwMinQual + dwMaxQual) / 2;
|
||||
else
|
||||
FIXME(": no new quality computed min=%lu cur=%lu max=%lu last=%lu\n",
|
||||
dwMinQual, dwCurQual, dwMaxQual, This->dwLastQuality);
|
||||
|
||||
bDecreasedQual = TRUE;
|
||||
}
|
||||
} while (TRUE);
|
||||
|
||||
/* remember some values */
|
||||
This->dwLastQuality = dwCurQual;
|
||||
This->dwUnusedBytes = dwRequest - This->lpbiCur->biSizeImage;
|
||||
if (icmFlags & ICCOMPRESS_KEYFRAME)
|
||||
This->lLastKey = This->lCurrent;
|
||||
|
||||
/* Does we manage previous frame? */
|
||||
if (This->lpPrev != NULL && This->lKeyFrameEvery != 1)
|
||||
ICDecompress(This->hic, 0, This->lpbiCur, This->lpCur,
|
||||
This->lpbiPrev, This->lpPrev);
|
||||
|
||||
return AVIERR_OK;
|
||||
}
|
||||
|
||||
static HRESULT AVIFILE_OpenGetFrame(IAVIStreamImpl *This)
|
||||
{
|
||||
LPBITMAPINFOHEADER lpbi;
|
||||
LONG size;
|
||||
|
||||
/* pre-conditions */
|
||||
assert(This != NULL);
|
||||
assert(This->pStream != NULL);
|
||||
assert(This->pg == NULL);
|
||||
|
||||
This->pg = AVIStreamGetFrameOpen(This->pStream, NULL);
|
||||
if (This->pg == NULL)
|
||||
return AVIERR_ERROR;
|
||||
|
||||
/* When we only decompress this is enough */
|
||||
if (This->sInfo.fccHandler == comptypeDIB)
|
||||
return AVIERR_OK;
|
||||
|
||||
assert(This->hic != (HIC)NULL);
|
||||
assert(This->lpbiOutput == NULL);
|
||||
|
||||
/* get input format */
|
||||
lpbi = (LPBITMAPINFOHEADER)AVIStreamGetFrame(This->pg, This->sInfo.dwStart);
|
||||
if (lpbi == NULL)
|
||||
return AVIERR_MEMORY;
|
||||
|
||||
/* get memory for output format */
|
||||
size = ICCompressGetFormatSize(This->hic, lpbi);
|
||||
if (size < sizeof(BITMAPINFOHEADER))
|
||||
return AVIERR_COMPRESSOR;
|
||||
This->lpbiOutput = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size);
|
||||
if (This->lpbiOutput == NULL)
|
||||
return AVIERR_MEMORY;
|
||||
This->cbOutput = size;
|
||||
|
||||
if (ICCompressGetFormat(This->hic, lpbi, This->lpbiOutput) < S_OK)
|
||||
return AVIERR_BADFORMAT;
|
||||
|
||||
/* update AVISTREAMINFO structure */
|
||||
This->sInfo.rcFrame.right =
|
||||
This->sInfo.rcFrame.left + This->lpbiOutput->biWidth;
|
||||
This->sInfo.rcFrame.bottom =
|
||||
This->sInfo.rcFrame.top + This->lpbiOutput->biHeight;
|
||||
This->sInfo.dwSuggestedBufferSize =
|
||||
ICCompressGetSize(This->hic, lpbi, This->lpbiOutput);
|
||||
|
||||
/* prepare codec for compression */
|
||||
if (ICCompressBegin(This->hic, lpbi, This->lpbiOutput) != S_OK)
|
||||
return AVIERR_COMPRESSOR;
|
||||
|
||||
/* allocate memory for current frame */
|
||||
size += This->sInfo.dwSuggestedBufferSize;
|
||||
This->lpbiCur = (LPBITMAPINFOHEADER)GlobalAllocPtr(GMEM_MOVEABLE, size);
|
||||
if (This->lpbiCur == NULL)
|
||||
return AVIERR_MEMORY;
|
||||
memcpy(This->lpbiCur, This->lpbiOutput, This->cbOutput);
|
||||
This->lpCur = DIBPTR(This->lpbiCur);
|
||||
|
||||
/* allocate memory for last frame if needed */
|
||||
if (This->lKeyFrameEvery != 1 &&
|
||||
(This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) {
|
||||
size = ICDecompressGetFormatSize(This->hic, This->lpbiOutput);
|
||||
This->lpbiPrev = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, size);
|
||||
if (This->lpbiPrev == NULL)
|
||||
return AVIERR_MEMORY;
|
||||
if (ICDecompressGetFormat(This->hic, This->lpbiOutput, This->lpbiPrev) < S_OK)
|
||||
return AVIERR_COMPRESSOR;
|
||||
|
||||
if (This->lpbiPrev->biSizeImage == 0) {
|
||||
This->lpbiPrev->biSizeImage =
|
||||
DIBWIDTHBYTES(*This->lpbiPrev) * This->lpbiPrev->biHeight;
|
||||
}
|
||||
|
||||
/* get memory for format and picture */
|
||||
size += This->lpbiPrev->biSizeImage;
|
||||
This->lpbiPrev =
|
||||
(LPBITMAPINFOHEADER)GlobalReAllocPtr(This->lpbiPrev,size,GMEM_MOVEABLE);
|
||||
if (This->lpbiPrev == NULL)
|
||||
return AVIERR_MEMORY;
|
||||
This->lpPrev = DIBPTR(This->lpbiPrev);
|
||||
|
||||
/* prepare codec also for decompression */
|
||||
if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK)
|
||||
return AVIERR_COMPRESSOR;
|
||||
}
|
||||
|
||||
return AVIERR_OK;
|
||||
}
|
Loading…
Reference in New Issue