1024 lines
32 KiB
C
1024 lines
32 KiB
C
/*
|
|
* Copyright 2003 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 "winuser.h"
|
|
#include "wingdi.h"
|
|
#include "winerror.h"
|
|
#include "mmsystem.h"
|
|
#include "vfw.h"
|
|
|
|
#include "avifile_private.h"
|
|
#include "extrachunk.h"
|
|
|
|
#include "wine/debug.h"
|
|
#include "initguid.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(avifile);
|
|
|
|
/***********************************************************************/
|
|
|
|
/* internal interface to get access to table of stream in an editable stream */
|
|
|
|
DEFINE_AVIGUID(IID_IEditStreamInternal, 0x0002000A,0,0);
|
|
|
|
typedef struct _EditStreamTable {
|
|
PAVISTREAM pStream; /* stream which contains the data */
|
|
DWORD dwStart; /* where starts the part which is also our */
|
|
DWORD dwLength; /* how many is also in this stream */
|
|
} EditStreamTable;
|
|
|
|
#define EditStreamEnd(This,streamNr) ((This)->pStreams[streamNr].dwStart + \
|
|
(This)->pStreams[streamNr].dwLength)
|
|
|
|
typedef struct _IAVIEditStreamImpl IAVIEditStreamImpl;
|
|
|
|
struct _IAVIEditStreamImpl {
|
|
IAVIEditStream IAVIEditStream_iface;
|
|
IAVIStream IAVIStream_iface;
|
|
|
|
LONG ref;
|
|
|
|
AVISTREAMINFOW sInfo;
|
|
|
|
EditStreamTable *pStreams;
|
|
DWORD nStreams; /* current fill level of pStreams table */
|
|
DWORD nTableSize; /* size of pStreams table */
|
|
|
|
BOOL bDecompress;
|
|
PAVISTREAM pCurStream;
|
|
PGETFRAME pg; /* IGetFrame for pCurStream */
|
|
LPBITMAPINFOHEADER lpFrame; /* frame of pCurStream */
|
|
};
|
|
|
|
static inline IAVIEditStreamImpl *impl_from_IAVIEditStream(IAVIEditStream *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, IAVIEditStreamImpl, IAVIEditStream_iface);
|
|
}
|
|
|
|
static inline IAVIEditStreamImpl *impl_from_IAVIStream(IAVIStream *iface)
|
|
{
|
|
return CONTAINING_RECORD(iface, IAVIEditStreamImpl, IAVIStream_iface);
|
|
}
|
|
|
|
/***********************************************************************/
|
|
|
|
static HRESULT AVIFILE_FindStreamInTable(IAVIEditStreamImpl* const This,
|
|
DWORD pos,PAVISTREAM *ppStream,
|
|
DWORD* streamPos,
|
|
DWORD* streamNr,BOOL bFindSample)
|
|
{
|
|
DWORD n;
|
|
|
|
TRACE("(%p,%u,%p,%p,%p,%d)\n",This,pos,ppStream,streamPos,
|
|
streamNr,bFindSample);
|
|
|
|
if (pos < This->sInfo.dwStart)
|
|
return AVIERR_BADPARAM;
|
|
|
|
pos -= This->sInfo.dwStart;
|
|
for (n = 0; n < This->nStreams; n++) {
|
|
if (pos < This->pStreams[n].dwLength) {
|
|
*ppStream = This->pStreams[n].pStream;
|
|
*streamPos = This->pStreams[n].dwStart + pos;
|
|
if (streamNr != NULL)
|
|
*streamNr = n;
|
|
|
|
return AVIERR_OK;
|
|
}
|
|
pos -= This->pStreams[n].dwLength;
|
|
}
|
|
if (pos == 0 && bFindSample) {
|
|
*ppStream = This->pStreams[--n].pStream;
|
|
*streamPos = EditStreamEnd(This, n);
|
|
if (streamNr != NULL)
|
|
*streamNr = n;
|
|
|
|
TRACE(" -- pos=0 && b=1 -> (%p,%u,%u)\n",*ppStream, *streamPos, n);
|
|
return AVIERR_OK;
|
|
} else {
|
|
*ppStream = NULL;
|
|
*streamPos = 0;
|
|
if (streamNr != NULL)
|
|
*streamNr = 0;
|
|
|
|
TRACE(" -> ERROR (NULL,0,0)\n");
|
|
return AVIERR_BADPARAM;
|
|
}
|
|
}
|
|
|
|
static LPVOID AVIFILE_ReadFrame(IAVIEditStreamImpl* const This,
|
|
PAVISTREAM pstream, LONG pos)
|
|
{
|
|
PGETFRAME pg;
|
|
|
|
TRACE("(%p,%p,%d)\n",This,pstream,pos);
|
|
|
|
if (pstream == NULL)
|
|
return NULL;
|
|
|
|
/* if stream changes make sure that only palette changes */
|
|
if (This->pCurStream != pstream) {
|
|
pg = AVIStreamGetFrameOpen(pstream, NULL);
|
|
if (pg == NULL)
|
|
return NULL;
|
|
if (This->pg != NULL) {
|
|
if (IGetFrame_SetFormat(pg, This->lpFrame, NULL, 0, 0, -1, -1) != S_OK) {
|
|
AVIStreamGetFrameClose(pg);
|
|
ERR(": IGetFrame_SetFormat failed\n");
|
|
return NULL;
|
|
}
|
|
AVIStreamGetFrameClose(This->pg);
|
|
}
|
|
This->pg = pg;
|
|
This->pCurStream = pstream;
|
|
}
|
|
|
|
/* now get the decompressed frame */
|
|
This->lpFrame = AVIStreamGetFrame(This->pg, pos);
|
|
if (This->lpFrame != NULL)
|
|
This->sInfo.dwSuggestedBufferSize = This->lpFrame->biSizeImage;
|
|
|
|
return This->lpFrame;
|
|
}
|
|
|
|
static HRESULT AVIFILE_RemoveStream(IAVIEditStreamImpl* const This, DWORD nr)
|
|
{
|
|
assert(This != NULL);
|
|
assert(nr < This->nStreams);
|
|
|
|
/* remove part nr */
|
|
IAVIStream_Release(This->pStreams[nr].pStream);
|
|
This->nStreams--;
|
|
if (nr < This->nStreams)
|
|
memmove(&This->pStreams[nr], &This->pStreams[nr + 1],
|
|
(This->nStreams - nr) * sizeof(This->pStreams[0]));
|
|
This->pStreams[This->nStreams].pStream = NULL;
|
|
This->pStreams[This->nStreams].dwStart = 0;
|
|
This->pStreams[This->nStreams].dwLength = 0;
|
|
|
|
/* try to merge the part before the deleted one and the one after it */
|
|
if (0 < nr && 0 < This->nStreams &&
|
|
This->pStreams[nr - 1].pStream == This->pStreams[nr].pStream) {
|
|
if (EditStreamEnd(This, nr - 1) == This->pStreams[nr].dwStart) {
|
|
This->pStreams[nr - 1].dwLength += This->pStreams[nr].dwLength;
|
|
return AVIFILE_RemoveStream(This, nr);
|
|
}
|
|
}
|
|
|
|
return AVIERR_OK;
|
|
}
|
|
|
|
static BOOL AVIFILE_FormatsEqual(PAVISTREAM avi1, PAVISTREAM avi2)
|
|
{
|
|
LPVOID fmt1 = NULL, fmt2 = NULL;
|
|
LONG size1, size2, start1, start2;
|
|
BOOL status = FALSE;
|
|
|
|
assert(avi1 != NULL && avi2 != NULL);
|
|
|
|
/* get stream starts and check format sizes */
|
|
start1 = AVIStreamStart(avi1);
|
|
start2 = AVIStreamStart(avi2);
|
|
if (FAILED(AVIStreamFormatSize(avi1, start1, &size1)))
|
|
return FALSE;
|
|
if (FAILED(AVIStreamFormatSize(avi2, start2, &size2)))
|
|
return FALSE;
|
|
if (size1 != size2)
|
|
return FALSE;
|
|
|
|
/* sizes match, now get formats and compare them */
|
|
fmt1 = HeapAlloc(GetProcessHeap(), 0, size1);
|
|
if (fmt1 == NULL)
|
|
return FALSE;
|
|
if (SUCCEEDED(AVIStreamReadFormat(avi1, start1, fmt1, &size1))) {
|
|
fmt2 = HeapAlloc(GetProcessHeap(), 0, size1);
|
|
if (fmt2 != NULL) {
|
|
if (SUCCEEDED(AVIStreamReadFormat(avi2, start2, fmt2, &size1)))
|
|
status = (memcmp(fmt1, fmt2, size1) == 0);
|
|
}
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, fmt2);
|
|
HeapFree(GetProcessHeap(), 0, fmt1);
|
|
|
|
return status;
|
|
}
|
|
|
|
/***********************************************************************/
|
|
|
|
static HRESULT WINAPI IAVIEditStream_fnQueryInterface(IAVIEditStream*iface,REFIID refiid,LPVOID *obj)
|
|
{
|
|
IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
|
|
|
|
TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj);
|
|
|
|
if (IsEqualGUID(&IID_IUnknown, refiid) ||
|
|
IsEqualGUID(&IID_IAVIEditStream, refiid) ||
|
|
IsEqualGUID(&IID_IEditStreamInternal, refiid)) {
|
|
*obj = iface;
|
|
IAVIEditStream_AddRef(iface);
|
|
|
|
return S_OK;
|
|
} else if (IsEqualGUID(&IID_IAVIStream, refiid)) {
|
|
*obj = &This->IAVIStream_iface;
|
|
IAVIEditStream_AddRef(iface);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
static ULONG WINAPI IAVIEditStream_fnAddRef(IAVIEditStream*iface)
|
|
{
|
|
IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
|
|
ULONG ref = InterlockedIncrement(&This->ref);
|
|
|
|
TRACE("(%p) -> %d\n", iface, ref);
|
|
|
|
return ref;
|
|
}
|
|
|
|
static ULONG WINAPI IAVIEditStream_fnRelease(IAVIEditStream*iface)
|
|
{
|
|
IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
|
|
DWORD i;
|
|
ULONG ref = InterlockedDecrement(&This->ref);
|
|
|
|
TRACE("(%p) -> %d\n", iface, ref);
|
|
|
|
if (!ref) {
|
|
/* release memory */
|
|
if (This->pg != NULL)
|
|
AVIStreamGetFrameClose(This->pg);
|
|
if (This->pStreams != NULL) {
|
|
for (i = 0; i < This->nStreams; i++) {
|
|
if (This->pStreams[i].pStream != NULL)
|
|
IAVIStream_Release(This->pStreams[i].pStream);
|
|
}
|
|
HeapFree(GetProcessHeap(), 0, This->pStreams);
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, This);
|
|
return 0;
|
|
}
|
|
return ref;
|
|
}
|
|
|
|
static HRESULT WINAPI IAVIEditStream_fnCut(IAVIEditStream*iface,LONG*plStart,
|
|
LONG*plLength,PAVISTREAM*ppResult)
|
|
{
|
|
IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
|
|
PAVISTREAM stream;
|
|
DWORD start, len, streamPos, streamNr;
|
|
HRESULT hr;
|
|
|
|
TRACE("(%p,%p,%p,%p)\n",iface,plStart,plLength,ppResult);
|
|
|
|
if (ppResult != NULL)
|
|
*ppResult = NULL;
|
|
if (plStart == NULL || plLength == NULL || *plStart < 0)
|
|
return AVIERR_BADPARAM;
|
|
|
|
/* if asked for cut part copy it before deleting */
|
|
if (ppResult != NULL) {
|
|
hr = IAVIEditStream_Copy(iface, plStart, plLength, ppResult);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
start = *plStart;
|
|
len = *plLength;
|
|
|
|
/* now delete the requested part */
|
|
while (len > 0) {
|
|
hr = AVIFILE_FindStreamInTable(This, start, &stream,
|
|
&streamPos, &streamNr, FALSE);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
if (This->pStreams[streamNr].dwStart == streamPos) {
|
|
/* deleting from start of part */
|
|
if (len < This->pStreams[streamNr].dwLength) {
|
|
start += len;
|
|
This->pStreams[streamNr].dwStart += len;
|
|
This->pStreams[streamNr].dwLength -= len;
|
|
This->sInfo.dwLength -= len;
|
|
len = 0;
|
|
|
|
/* we must return decompressed data now */
|
|
This->bDecompress = TRUE;
|
|
} else {
|
|
/* deleting hole part */
|
|
len -= This->pStreams[streamNr].dwLength;
|
|
AVIFILE_RemoveStream(This,streamNr);
|
|
}
|
|
} else if (EditStreamEnd(This, streamNr) <= streamPos + len) {
|
|
/* deleting at end of a part */
|
|
DWORD count = EditStreamEnd(This, streamNr) - streamPos;
|
|
This->sInfo.dwLength -= count;
|
|
len -= count;
|
|
This->pStreams[streamNr].dwLength =
|
|
streamPos - This->pStreams[streamNr].dwStart;
|
|
} else {
|
|
/* splitting */
|
|
if (This->nStreams + 1 >= This->nTableSize) {
|
|
This->pStreams = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->pStreams,
|
|
(This->nTableSize + 32) * sizeof(EditStreamTable));
|
|
if (This->pStreams == NULL)
|
|
return AVIERR_MEMORY;
|
|
This->nTableSize += 32;
|
|
}
|
|
memmove(This->pStreams + streamNr + 1, This->pStreams + streamNr,
|
|
(This->nStreams - streamNr) * sizeof(EditStreamTable));
|
|
This->nStreams++;
|
|
|
|
IAVIStream_AddRef(This->pStreams[streamNr + 1].pStream);
|
|
This->pStreams[streamNr + 1].dwStart = streamPos + len;
|
|
This->pStreams[streamNr + 1].dwLength =
|
|
EditStreamEnd(This, streamNr) - This->pStreams[streamNr + 1].dwStart;
|
|
|
|
This->pStreams[streamNr].dwLength =
|
|
streamPos - This->pStreams[streamNr].dwStart;
|
|
This->sInfo.dwLength -= len;
|
|
len = 0;
|
|
}
|
|
}
|
|
|
|
This->sInfo.dwEditCount++;
|
|
|
|
return AVIERR_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI IAVIEditStream_fnCopy(IAVIEditStream*iface,LONG*plStart,
|
|
LONG*plLength,PAVISTREAM*ppResult)
|
|
{
|
|
IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
|
|
IAVIEditStreamImpl* pEdit;
|
|
HRESULT hr;
|
|
LONG start = 0;
|
|
|
|
TRACE("(%p,%p,%p,%p)\n",iface,plStart,plLength,ppResult);
|
|
|
|
if (ppResult == NULL)
|
|
return AVIERR_BADPARAM;
|
|
*ppResult = NULL;
|
|
if (plStart == NULL || plLength == NULL || *plStart < 0 || *plLength < 0)
|
|
return AVIERR_BADPARAM;
|
|
|
|
/* check bounds */
|
|
if (*(LPDWORD)plLength > This->sInfo.dwLength)
|
|
*(LPDWORD)plLength = This->sInfo.dwLength;
|
|
if (*(LPDWORD)plStart < This->sInfo.dwStart) {
|
|
*(LPDWORD)plLength -= This->sInfo.dwStart - *(LPDWORD)plStart;
|
|
*(LPDWORD)plStart = This->sInfo.dwStart;
|
|
if (*plLength < 0)
|
|
return AVIERR_BADPARAM;
|
|
}
|
|
if (*(LPDWORD)plStart + *(LPDWORD)plLength > This->sInfo.dwStart + This->sInfo.dwLength)
|
|
*(LPDWORD)plLength = This->sInfo.dwStart + This->sInfo.dwLength -
|
|
*(LPDWORD)plStart;
|
|
|
|
pEdit = (IAVIEditStreamImpl*)AVIFILE_CreateEditStream(NULL);
|
|
if (pEdit == NULL)
|
|
return AVIERR_MEMORY;
|
|
|
|
hr = IAVIEditStream_Paste((PAVIEDITSTREAM)pEdit, &start, plLength, &This->IAVIStream_iface,
|
|
*plStart, *plStart + *plLength);
|
|
*plStart = start;
|
|
if (FAILED(hr))
|
|
IAVIEditStream_Release((PAVIEDITSTREAM)pEdit);
|
|
else
|
|
*ppResult = &This->IAVIStream_iface;
|
|
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT WINAPI IAVIEditStream_fnPaste(IAVIEditStream*iface,LONG*plStart,
|
|
LONG*plLength,PAVISTREAM pSource,
|
|
LONG lStart,LONG lLength)
|
|
{
|
|
IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
|
|
AVISTREAMINFOW srcInfo;
|
|
IAVIEditStreamImpl *pEdit = NULL;
|
|
PAVISTREAM pStream;
|
|
DWORD startPos, endPos, streamNr, nStreams;
|
|
ULONG n;
|
|
|
|
TRACE("(%p,%p,%p,%p,%d,%d)\n",iface,plStart,plLength,
|
|
pSource,lStart,lLength);
|
|
|
|
if (pSource == NULL)
|
|
return AVIERR_BADHANDLE;
|
|
if (plStart == NULL || *plStart < 0)
|
|
return AVIERR_BADPARAM;
|
|
if (This->sInfo.dwStart + This->sInfo.dwLength < *plStart)
|
|
return AVIERR_BADPARAM; /* Can't paste with holes */
|
|
if (FAILED(IAVIStream_Info(pSource, &srcInfo, sizeof(srcInfo))))
|
|
return AVIERR_ERROR;
|
|
if (lStart < srcInfo.dwStart || lStart >= srcInfo.dwStart + srcInfo.dwLength)
|
|
return AVIERR_BADPARAM;
|
|
if (This->sInfo.fccType == 0) {
|
|
/* This stream is empty */
|
|
IAVIStream_Info(pSource, &This->sInfo, sizeof(This->sInfo));
|
|
This->sInfo.dwStart = *plStart;
|
|
This->sInfo.dwLength = 0;
|
|
}
|
|
if (This->sInfo.fccType != srcInfo.fccType)
|
|
return AVIERR_UNSUPPORTED; /* different stream types */
|
|
if (lLength == -1) /* Copy the hole stream */
|
|
lLength = srcInfo.dwLength;
|
|
if (lStart + lLength > srcInfo.dwStart + srcInfo.dwLength)
|
|
lLength = srcInfo.dwStart + srcInfo.dwLength - lStart;
|
|
if (lLength + *plStart >= 0x80000000)
|
|
return AVIERR_MEMORY;
|
|
|
|
/* streamtype specific tests */
|
|
if (srcInfo.fccType == streamtypeVIDEO) {
|
|
LONG size;
|
|
|
|
size = srcInfo.rcFrame.right - srcInfo.rcFrame.left;
|
|
if (size != This->sInfo.rcFrame.right - This->sInfo.rcFrame.left)
|
|
return AVIERR_UNSUPPORTED; /* FIXME: Can't GetFrame convert it? */
|
|
size = srcInfo.rcFrame.bottom - srcInfo.rcFrame.top;
|
|
if (size != This->sInfo.rcFrame.bottom - This->sInfo.rcFrame.top)
|
|
return AVIERR_UNSUPPORTED; /* FIXME: Can't GetFrame convert it? */
|
|
} else if (srcInfo.fccType == streamtypeAUDIO) {
|
|
if (!AVIFILE_FormatsEqual(&This->IAVIStream_iface, pSource))
|
|
return AVIERR_UNSUPPORTED;
|
|
} else {
|
|
/* FIXME: streamtypeMIDI and streamtypeTEXT */
|
|
return AVIERR_UNSUPPORTED;
|
|
}
|
|
|
|
/* try to get an IEditStreamInternal interface */
|
|
if (SUCCEEDED(IAVIStream_QueryInterface(pSource, &IID_IEditStreamInternal, (LPVOID*)&pEdit)))
|
|
IAVIEditStream_Release(&pEdit->IAVIEditStream_iface); /* pSource holds a reference */
|
|
|
|
/* for video must check for change of format */
|
|
if (This->sInfo.fccType == streamtypeVIDEO) {
|
|
if (! This->bDecompress) {
|
|
/* Need to decompress if any of the following conditions matches:
|
|
* - pSource is an editable stream which decompresses
|
|
* - the nearest keyframe of pSource isn't lStart
|
|
* - the nearest keyframe of this stream isn't *plStart
|
|
* - the format of pSource doesn't match this one
|
|
*/
|
|
if ((pEdit != NULL && pEdit->bDecompress) ||
|
|
AVIStreamNearestKeyFrame(pSource, lStart) != lStart ||
|
|
AVIStreamNearestKeyFrame(&This->IAVIStream_iface, *plStart) != *plStart ||
|
|
(This->nStreams > 0 && !AVIFILE_FormatsEqual(&This->IAVIStream_iface, pSource))) {
|
|
/* Use first stream part to get format to convert everything to */
|
|
AVIFILE_ReadFrame(This, This->pStreams[0].pStream,
|
|
This->pStreams[0].dwStart);
|
|
|
|
/* Check if we could convert the source streams to the desired format... */
|
|
if (pEdit != NULL) {
|
|
if (FAILED(AVIFILE_FindStreamInTable(pEdit, lStart, &pStream,
|
|
&startPos, &streamNr, TRUE)))
|
|
return AVIERR_INTERNAL;
|
|
for (n = lStart; n < lStart + lLength; streamNr++) {
|
|
if (AVIFILE_ReadFrame(This, pEdit->pStreams[streamNr].pStream, startPos) == NULL)
|
|
return AVIERR_BADFORMAT;
|
|
startPos = pEdit->pStreams[streamNr].dwStart;
|
|
n += pEdit->pStreams[streamNr].dwLength;
|
|
}
|
|
} else if (AVIFILE_ReadFrame(This, pSource, lStart) == NULL)
|
|
return AVIERR_BADFORMAT;
|
|
|
|
This->bDecompress = TRUE;
|
|
This->sInfo.fccHandler = 0;
|
|
}
|
|
} else if (AVIFILE_ReadFrame(This, pSource, lStart) == NULL)
|
|
return AVIERR_BADFORMAT; /* Can't convert source to own format */
|
|
} /* FIXME: something special for the other formats? */
|
|
|
|
/* Make sure we have enough memory for parts */
|
|
if (pEdit != NULL) {
|
|
DWORD nLastStream;
|
|
|
|
AVIFILE_FindStreamInTable(pEdit, lStart + lLength, &pStream,
|
|
&endPos, &nLastStream, TRUE);
|
|
AVIFILE_FindStreamInTable(pEdit, lStart, &pStream,
|
|
&startPos, &streamNr, FALSE);
|
|
if (nLastStream == streamNr)
|
|
nLastStream++;
|
|
|
|
nStreams = nLastStream - streamNr;
|
|
} else
|
|
nStreams = 1;
|
|
if (This->nStreams + nStreams + 1 > This->nTableSize) {
|
|
n = This->nStreams + nStreams + 33;
|
|
|
|
This->pStreams = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->pStreams, n * sizeof(EditStreamTable));
|
|
if (This->pStreams == NULL)
|
|
return AVIERR_MEMORY;
|
|
This->nTableSize = n;
|
|
}
|
|
|
|
if (plLength != NULL)
|
|
*plLength = lLength;
|
|
|
|
/* now do the real work */
|
|
if (This->sInfo.dwStart + This->sInfo.dwLength > *plStart) {
|
|
AVIFILE_FindStreamInTable(This, *plStart, &pStream,
|
|
&startPos, &streamNr, FALSE);
|
|
if (startPos != This->pStreams[streamNr].dwStart) {
|
|
/* split stream streamNr at startPos */
|
|
memmove(This->pStreams + streamNr + nStreams + 1,
|
|
This->pStreams + streamNr,
|
|
(This->nStreams + nStreams - streamNr + 1) * sizeof(EditStreamTable));
|
|
|
|
This->pStreams[streamNr + 2].dwLength =
|
|
EditStreamEnd(This, streamNr + 2) - startPos;
|
|
This->pStreams[streamNr + 2].dwStart = startPos;
|
|
This->pStreams[streamNr].dwLength =
|
|
startPos - This->pStreams[streamNr].dwStart;
|
|
IAVIStream_AddRef(This->pStreams[streamNr].pStream);
|
|
streamNr++;
|
|
} else {
|
|
/* insert before stream at streamNr */
|
|
memmove(This->pStreams + streamNr + nStreams, This->pStreams + streamNr,
|
|
(This->nStreams + nStreams - streamNr) * sizeof(EditStreamTable));
|
|
}
|
|
} else /* append the streams */
|
|
streamNr = This->nStreams;
|
|
|
|
if (pEdit != NULL) {
|
|
/* insert the parts of the editable stream instead of itself */
|
|
AVIFILE_FindStreamInTable(pEdit, lStart + lLength, &pStream,
|
|
&endPos, NULL, FALSE);
|
|
AVIFILE_FindStreamInTable(pEdit, lStart, &pStream, &startPos, &n, FALSE);
|
|
|
|
memcpy(This->pStreams + streamNr, pEdit->pStreams + n,
|
|
nStreams * sizeof(EditStreamTable));
|
|
if (This->pStreams[streamNr].dwStart < startPos) {
|
|
This->pStreams[streamNr].dwLength =
|
|
EditStreamEnd(This, streamNr) - startPos;
|
|
This->pStreams[streamNr].dwStart = startPos;
|
|
}
|
|
if (endPos < EditStreamEnd(This, streamNr + nStreams))
|
|
This->pStreams[streamNr + nStreams].dwLength =
|
|
endPos - This->pStreams[streamNr + nStreams].dwStart;
|
|
} else {
|
|
/* a simple stream */
|
|
This->pStreams[streamNr].pStream = pSource;
|
|
This->pStreams[streamNr].dwStart = lStart;
|
|
This->pStreams[streamNr].dwLength = lLength;
|
|
}
|
|
|
|
for (n = 0; n < nStreams; n++) {
|
|
IAVIStream_AddRef(This->pStreams[streamNr + n].pStream);
|
|
if (0 < streamNr + n &&
|
|
This->pStreams[streamNr + n - 1].pStream != This->pStreams[streamNr + n].pStream) {
|
|
This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
|
|
This->sInfo.dwFormatChangeCount++;
|
|
}
|
|
}
|
|
This->sInfo.dwEditCount++;
|
|
This->sInfo.dwLength += lLength;
|
|
This->nStreams += nStreams;
|
|
|
|
return AVIERR_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI IAVIEditStream_fnClone(IAVIEditStream*iface,
|
|
PAVISTREAM*ppResult)
|
|
{
|
|
IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
|
|
IAVIEditStreamImpl* pEdit;
|
|
DWORD i;
|
|
|
|
TRACE("(%p,%p)\n",iface,ppResult);
|
|
|
|
if (ppResult == NULL)
|
|
return AVIERR_BADPARAM;
|
|
*ppResult = NULL;
|
|
|
|
pEdit = (IAVIEditStreamImpl*)AVIFILE_CreateEditStream(NULL);
|
|
if (pEdit == NULL)
|
|
return AVIERR_MEMORY;
|
|
if (This->nStreams > pEdit->nTableSize) {
|
|
pEdit->pStreams = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pEdit->pStreams,
|
|
This->nStreams * sizeof(EditStreamTable));
|
|
if (pEdit->pStreams == NULL)
|
|
return AVIERR_MEMORY;
|
|
pEdit->nTableSize = This->nStreams;
|
|
}
|
|
pEdit->nStreams = This->nStreams;
|
|
memcpy(pEdit->pStreams, This->pStreams,
|
|
This->nStreams * sizeof(EditStreamTable));
|
|
memcpy(&pEdit->sInfo,&This->sInfo,sizeof(This->sInfo));
|
|
for (i = 0; i < This->nStreams; i++) {
|
|
if (pEdit->pStreams[i].pStream != NULL)
|
|
IAVIStream_AddRef(pEdit->pStreams[i].pStream);
|
|
}
|
|
|
|
*ppResult = &This->IAVIStream_iface;
|
|
|
|
return AVIERR_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI IAVIEditStream_fnSetInfo(IAVIEditStream*iface,
|
|
LPAVISTREAMINFOW asi,LONG size)
|
|
{
|
|
IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
|
|
|
|
TRACE("(%p,%p,%d)\n",iface,asi,size);
|
|
|
|
/* check parameters */
|
|
if (size >= 0 && size < sizeof(AVISTREAMINFOW))
|
|
return AVIERR_BADSIZE;
|
|
|
|
This->sInfo.wLanguage = asi->wLanguage;
|
|
This->sInfo.wPriority = asi->wPriority;
|
|
This->sInfo.dwStart = asi->dwStart;
|
|
This->sInfo.dwRate = asi->dwRate;
|
|
This->sInfo.dwScale = asi->dwScale;
|
|
This->sInfo.dwQuality = asi->dwQuality;
|
|
This->sInfo.rcFrame = asi->rcFrame;
|
|
memcpy(This->sInfo.szName, asi->szName, sizeof(asi->szName));
|
|
This->sInfo.dwEditCount++;
|
|
|
|
return AVIERR_OK;
|
|
}
|
|
|
|
static const struct IAVIEditStreamVtbl ieditstream = {
|
|
IAVIEditStream_fnQueryInterface,
|
|
IAVIEditStream_fnAddRef,
|
|
IAVIEditStream_fnRelease,
|
|
IAVIEditStream_fnCut,
|
|
IAVIEditStream_fnCopy,
|
|
IAVIEditStream_fnPaste,
|
|
IAVIEditStream_fnClone,
|
|
IAVIEditStream_fnSetInfo
|
|
};
|
|
|
|
static HRESULT WINAPI IEditAVIStream_fnQueryInterface(IAVIStream*iface,
|
|
REFIID refiid,LPVOID*obj)
|
|
{
|
|
IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
|
|
return IAVIEditStream_QueryInterface(&This->IAVIEditStream_iface,refiid,obj);
|
|
}
|
|
|
|
static ULONG WINAPI IEditAVIStream_fnAddRef(IAVIStream*iface)
|
|
{
|
|
IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
|
|
return IAVIEditStream_AddRef(&This->IAVIEditStream_iface);
|
|
}
|
|
|
|
static ULONG WINAPI IEditAVIStream_fnRelease(IAVIStream*iface)
|
|
{
|
|
IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
|
|
return IAVIEditStream_Release(&This->IAVIEditStream_iface);
|
|
}
|
|
|
|
static HRESULT WINAPI IEditAVIStream_fnCreate(IAVIStream*iface,
|
|
LPARAM lParam1,LPARAM lParam2)
|
|
{
|
|
IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
|
|
|
|
if (lParam2 != 0)
|
|
return AVIERR_ERROR;
|
|
|
|
if (This->pStreams == NULL) {
|
|
This->pStreams = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 256 * sizeof(EditStreamTable));
|
|
if (This->pStreams == NULL)
|
|
return AVIERR_MEMORY;
|
|
This->nTableSize = 256;
|
|
}
|
|
|
|
if (lParam1 != 0) {
|
|
IAVIStream_Info((PAVISTREAM)lParam1, &This->sInfo, sizeof(This->sInfo));
|
|
IAVIStream_AddRef((PAVISTREAM)lParam1);
|
|
This->pStreams[0].pStream = (PAVISTREAM)lParam1;
|
|
This->pStreams[0].dwStart = This->sInfo.dwStart;
|
|
This->pStreams[0].dwLength = This->sInfo.dwLength;
|
|
This->nStreams = 1;
|
|
}
|
|
return AVIERR_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI IEditAVIStream_fnInfo(IAVIStream*iface,
|
|
AVISTREAMINFOW *psi,LONG size)
|
|
{
|
|
IAVIEditStreamImpl *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;
|
|
|
|
if (This->bDecompress)
|
|
This->sInfo.fccHandler = 0;
|
|
|
|
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 IEditAVIStream_fnFindSample(IAVIStream*iface,LONG pos,
|
|
LONG flags)
|
|
{
|
|
IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
|
|
PAVISTREAM stream;
|
|
DWORD streamPos, streamNr;
|
|
|
|
TRACE("(%p,%d,0x%08X)\n",iface,pos,flags);
|
|
|
|
if (flags & FIND_FROM_START)
|
|
pos = (LONG)This->sInfo.dwStart;
|
|
|
|
/* outside of stream? */
|
|
if (pos < (LONG)This->sInfo.dwStart ||
|
|
(LONG)This->sInfo.dwStart + (LONG)This->sInfo.dwLength <= pos)
|
|
return -1;
|
|
|
|
/* map our position to a stream and position in it */
|
|
if (AVIFILE_FindStreamInTable(This, pos, &stream, &streamPos,
|
|
&streamNr, TRUE) != S_OK)
|
|
return -1; /* doesn't exist */
|
|
|
|
if (This->bDecompress) {
|
|
/* only one stream -- format changes only at start */
|
|
if (flags & FIND_FORMAT)
|
|
return (flags & FIND_NEXT ? -1 : 0);
|
|
|
|
/* FIXME: map positions back to us */
|
|
return IAVIStream_FindSample(stream, streamPos, flags);
|
|
} else {
|
|
/* assume change of format every frame */
|
|
return pos;
|
|
}
|
|
}
|
|
|
|
static HRESULT WINAPI IEditAVIStream_fnReadFormat(IAVIStream*iface,LONG pos,
|
|
LPVOID format,LONG*fmtsize)
|
|
{
|
|
IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
|
|
LPBITMAPINFOHEADER lp;
|
|
PAVISTREAM stream;
|
|
DWORD n;
|
|
HRESULT hr;
|
|
|
|
TRACE("(%p,%d,%p,%p)\n",iface,pos,format,fmtsize);
|
|
|
|
if (fmtsize == NULL || pos < This->sInfo.dwStart ||
|
|
This->sInfo.dwStart + This->sInfo.dwLength <= pos)
|
|
return AVIERR_BADPARAM;
|
|
|
|
/* find stream corresponding to position */
|
|
hr = AVIFILE_FindStreamInTable(This, pos, &stream, &n, NULL, FALSE);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (! This->bDecompress)
|
|
return IAVIStream_ReadFormat(stream, n, format, fmtsize);
|
|
|
|
lp = AVIFILE_ReadFrame(This, stream, n);
|
|
if (lp == NULL)
|
|
return AVIERR_ERROR;
|
|
if (lp->biBitCount <= 8) {
|
|
n = (lp->biClrUsed > 0 ? lp->biClrUsed : 1 << lp->biBitCount);
|
|
n *= sizeof(RGBQUAD);
|
|
} else
|
|
n = 0;
|
|
n += lp->biSize;
|
|
|
|
memcpy(format, lp, min((LONG)n, *fmtsize));
|
|
hr = ((LONG)n > *fmtsize ? AVIERR_BUFFERTOOSMALL : AVIERR_OK);
|
|
*fmtsize = n;
|
|
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT WINAPI IEditAVIStream_fnSetFormat(IAVIStream*iface,LONG pos,
|
|
LPVOID format,LONG formatsize)
|
|
{
|
|
TRACE("(%p,%d,%p,%d)\n",iface,pos,format,formatsize);
|
|
|
|
return AVIERR_UNSUPPORTED;
|
|
}
|
|
|
|
static HRESULT WINAPI IEditAVIStream_fnRead(IAVIStream*iface,LONG start,
|
|
LONG samples,LPVOID buffer,
|
|
LONG buffersize,LONG*bytesread,
|
|
LONG*samplesread)
|
|
{
|
|
IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
|
|
PAVISTREAM stream;
|
|
DWORD streamPos, streamNr;
|
|
LONG readBytes, readSamples, count;
|
|
HRESULT hr;
|
|
|
|
TRACE("(%p,%d,%d,%p,%d,%p,%p) -- 0x%08X\n",iface,start,samples,
|
|
buffer,buffersize,bytesread,samplesread,This->sInfo.fccType);
|
|
|
|
/* check parameters */
|
|
if (bytesread != NULL)
|
|
*bytesread = 0;
|
|
if (samplesread != NULL)
|
|
*samplesread = 0;
|
|
if (buffersize < 0)
|
|
return AVIERR_BADSIZE;
|
|
if ((DWORD)start < This->sInfo.dwStart ||
|
|
This->sInfo.dwStart + This->sInfo.dwLength < (DWORD)start)
|
|
return AVIERR_BADPARAM;
|
|
|
|
if (! This->bDecompress) {
|
|
/* audio like data -- sample-based */
|
|
do {
|
|
if (samples == 0)
|
|
return AVIERR_OK; /* nothing at all or already done */
|
|
|
|
if (FAILED(AVIFILE_FindStreamInTable(This, start, &stream,
|
|
&streamPos, &streamNr, FALSE)))
|
|
return AVIERR_ERROR;
|
|
|
|
/* limit to end of the stream */
|
|
count = samples;
|
|
if (streamPos + count > EditStreamEnd(This, streamNr))
|
|
count = EditStreamEnd(This, streamNr) - streamPos;
|
|
|
|
hr = IAVIStream_Read(stream, streamPos, count, buffer, buffersize,
|
|
&readBytes, &readSamples);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
if (readBytes == 0 && readSamples == 0 && count != 0)
|
|
return AVIERR_FILEREAD; /* for bad stream implementations */
|
|
|
|
if (samplesread != NULL)
|
|
*samplesread += readSamples;
|
|
if (bytesread != NULL)
|
|
*bytesread += readBytes;
|
|
if (buffer != NULL) {
|
|
buffer = ((LPBYTE)buffer)+readBytes;
|
|
buffersize -= readBytes;
|
|
}
|
|
start += count;
|
|
samples -= count;
|
|
} while (This->sInfo.dwStart + This->sInfo.dwLength > start);
|
|
} else {
|
|
/* video like data -- frame-based */
|
|
LPBITMAPINFOHEADER lp;
|
|
|
|
if (samples == 0)
|
|
return AVIERR_OK;
|
|
|
|
if (FAILED(AVIFILE_FindStreamInTable(This, start, &stream,
|
|
&streamPos, &streamNr, FALSE)))
|
|
return AVIERR_ERROR;
|
|
|
|
lp = AVIFILE_ReadFrame(This, stream, streamPos);
|
|
if (lp == NULL)
|
|
return AVIERR_ERROR;
|
|
|
|
if (buffer != NULL) {
|
|
/* need size of format to skip */
|
|
if (lp->biBitCount <= 8) {
|
|
count = lp->biClrUsed > 0 ? lp->biClrUsed : 1 << lp->biBitCount;
|
|
count *= sizeof(RGBQUAD);
|
|
} else
|
|
count = 0;
|
|
count += lp->biSize;
|
|
|
|
if (buffersize < lp->biSizeImage)
|
|
return AVIERR_BUFFERTOOSMALL;
|
|
memcpy(buffer, (LPBYTE)lp + count, lp->biSizeImage);
|
|
}
|
|
|
|
if (bytesread != NULL)
|
|
*bytesread = lp->biSizeImage;
|
|
if (samplesread != NULL)
|
|
*samplesread = 1;
|
|
}
|
|
|
|
return AVIERR_OK;
|
|
}
|
|
|
|
static HRESULT WINAPI IEditAVIStream_fnWrite(IAVIStream*iface,LONG start,
|
|
LONG samples,LPVOID buffer,
|
|
LONG buffersize,DWORD flags,
|
|
LONG*sampwritten,LONG*byteswritten)
|
|
{
|
|
TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n",iface,start,samples,buffer,
|
|
buffersize,flags,sampwritten,byteswritten);
|
|
|
|
/* be sure return parameters have correct values */
|
|
if (sampwritten != NULL)
|
|
*sampwritten = 0;
|
|
if (byteswritten != NULL)
|
|
*byteswritten = 0;
|
|
|
|
return AVIERR_UNSUPPORTED;
|
|
}
|
|
|
|
static HRESULT WINAPI IEditAVIStream_fnDelete(IAVIStream*iface,LONG start,
|
|
LONG samples)
|
|
{
|
|
IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
|
|
|
|
TRACE("(%p,%d,%d)\n",iface,start,samples);
|
|
|
|
return IAVIEditStream_Cut(&This->IAVIEditStream_iface,&start,&samples,NULL);
|
|
}
|
|
|
|
static HRESULT WINAPI IEditAVIStream_fnReadData(IAVIStream*iface,DWORD fcc,
|
|
LPVOID lp,LONG *lpread)
|
|
{
|
|
IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
|
|
DWORD n;
|
|
|
|
TRACE("(%p,0x%08X,%p,%p)\n",iface,fcc,lp,lpread);
|
|
|
|
/* check parameters */
|
|
if (lp == NULL || lpread == NULL)
|
|
return AVIERR_BADPARAM;
|
|
|
|
/* simply ask every stream and return the first block found */
|
|
for (n = 0; n < This->nStreams; n++) {
|
|
HRESULT hr = IAVIStream_ReadData(This->pStreams[n].pStream,fcc,lp,lpread);
|
|
|
|
if (SUCCEEDED(hr))
|
|
return hr;
|
|
}
|
|
|
|
*lpread = 0;
|
|
return AVIERR_NODATA;
|
|
}
|
|
|
|
static HRESULT WINAPI IEditAVIStream_fnWriteData(IAVIStream*iface,DWORD fcc,
|
|
LPVOID lp,LONG size)
|
|
{
|
|
TRACE("(%p,0x%08X,%p,%d)\n",iface,fcc,lp,size);
|
|
|
|
return AVIERR_UNSUPPORTED;
|
|
}
|
|
|
|
static HRESULT WINAPI IEditAVIStream_fnSetInfo(IAVIStream*iface,
|
|
AVISTREAMINFOW*info,LONG len)
|
|
{
|
|
IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
|
|
|
|
TRACE("(%p,%p,%d)\n",iface,info,len);
|
|
|
|
return IAVIEditStream_SetInfo(&This->IAVIEditStream_iface,info,len);
|
|
}
|
|
|
|
static const struct IAVIStreamVtbl ieditstast = {
|
|
IEditAVIStream_fnQueryInterface,
|
|
IEditAVIStream_fnAddRef,
|
|
IEditAVIStream_fnRelease,
|
|
IEditAVIStream_fnCreate,
|
|
IEditAVIStream_fnInfo,
|
|
IEditAVIStream_fnFindSample,
|
|
IEditAVIStream_fnReadFormat,
|
|
IEditAVIStream_fnSetFormat,
|
|
IEditAVIStream_fnRead,
|
|
IEditAVIStream_fnWrite,
|
|
IEditAVIStream_fnDelete,
|
|
IEditAVIStream_fnReadData,
|
|
IEditAVIStream_fnWriteData,
|
|
IEditAVIStream_fnSetInfo
|
|
};
|
|
|
|
PAVIEDITSTREAM AVIFILE_CreateEditStream(PAVISTREAM pstream)
|
|
{
|
|
IAVIEditStreamImpl *pedit = NULL;
|
|
|
|
pedit = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIEditStreamImpl));
|
|
if (pedit == NULL)
|
|
return NULL;
|
|
|
|
pedit->IAVIEditStream_iface.lpVtbl = &ieditstream;
|
|
pedit->IAVIStream_iface.lpVtbl = &ieditstast;
|
|
pedit->ref = 1;
|
|
|
|
IAVIStream_Create(&pedit->IAVIStream_iface, (LPARAM)pstream, 0);
|
|
|
|
return (PAVIEDITSTREAM)pedit;
|
|
}
|