📄 icmstream.c
字号:
/*
* Copyright 2002 Michael G黱newig
*
* 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 "winnls.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);
#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);
static const struct IAVIStreamVtbl iicmst = {
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 */
const IAVIStreamVtbl *lpVtbl;
LONG 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);
static 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 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIStreamImpl));
if (pstream == NULL)
return AVIERR_MEMORY;
pstream->lpVtbl = &iicmst;
AVIFILE_Reset(pstream);
hr = IAVIStream_QueryInterface((IAVIStream*)pstream, riid, ppv);
if (FAILED(hr))
HeapFree(GetProcessHeap(), 0, pstream);
return hr;
}
static HRESULT WINAPI ICMStream_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 ICMStream_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 ICMStream_fnRelease(IAVIStream* iface)
{
IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
ULONG ref = InterlockedDecrement(&This->ref);
TRACE("(%p) -> %d\n", iface, ref);
if (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 != NULL) {
if (This->lpbiPrev != NULL) {
ICDecompressEnd(This->hic);
HeapFree(GetProcessHeap(), 0, This->lpbiPrev);
This->lpbiPrev = NULL;
This->lpPrev = NULL;
}
ICCompressEnd(This->hic);
This->hic = NULL;
}
if (This->lpbiCur != NULL) {
HeapFree(GetProcessHeap(), 0, This->lpbiCur);
This->lpbiCur = NULL;
This->lpCur = NULL;
}
if (This->lpbiOutput != NULL) {
HeapFree(GetProcessHeap(), 0, This->lpbiOutput);
This->lpbiOutput = NULL;
This->cbOutput = 0;
}
if (This->lpbiInput != NULL) {
HeapFree(GetProcessHeap(), 0, This->lpbiInput);
This->lpbiInput = NULL;
This->cbInput = 0;
}
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
*/
static HRESULT WINAPI ICMStream_fnCreate(IAVIStream *iface, LPARAM lParam1,
LPARAM lParam2)
{
IAVIStreamImpl *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 == 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)
{
IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
TRACE("(%p,%p,%d)\n", iface, psi, size);
if (psi == NULL)
return AVIERR_BADPARAM;
if (size < 0)
return AVIERR_BADSIZE;
memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo)));
if ((DWORD)size < sizeof(This->sInfo))
return AVIERR_BUFFERTOOSMALL;
return AVIERR_OK;
}
static LONG WINAPI ICMStream_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;
}
if (flags & FIND_RET)
WARN(": FIND_RET flags will be ignored!\n");
if (flags & FIND_KEY) {
if (This->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)
{
IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
LPBITMAPINFOHEADER lpbi;
HRESULT hr;
TRACE("(%p,%d,%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 == 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)
{
IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
TRACE("(%p,%d,%p,%d)\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) {
ULONG size;
assert(This->hic != NULL);
/* get memory for input format */
This->lpbiInput = HeapAlloc(GetProcessHeap(), 0, formatsize);
if (This->lpbiInput == NULL)
return AVIERR_MEMORY;
This->cbInput = formatsize;
memcpy(This->lpbiInput, format, formatsize);
/* get output format */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -