NT4/private/windows/media/avi/avifile.16/editstrm.cpp
2020-09-30 17:12:29 +02:00

1541 lines
37 KiB
C++

/****************************************************************************
*
* EDITSTRM.C
*
* routines for reading Standard AVI files
*
* Copyright (c) 1992 Microsoft Corporation. All Rights Reserved.
*
* You have a royalty-free right to use, modify, reproduce and
* distribute the Sample Files (and/or any modified version) in
* any way you find useful, provided that you agree that
* Microsoft has no warranty obligations or liability for any
* Sample Application Files which are modified.
*
***************************************************************************/
#include <win32.h>
#include <compman.h>
#include "avifile.h"
#include <storage.h>
#include "editstrm.h"
#ifdef DEBUG
static void CDECL dprintf(LPSTR, ...);
#define DPF dprintf
#else
#define DPF ; / ## /
#endif
/*
* memcopy.asm
*/
#ifdef WIN32
#define MemCopy(dst, src, cnt) memmove(dst,src,cnt)
#else
EXTERN_C LONG FAR PASCAL MemCopy(HPSTR, HPSTR, DWORD);
#endif
STDAPI EditStreamCut(PAVISTREAM pavi, LONG FAR *plStart, LONG FAR *plLength, PAVISTREAM FAR * ppResult)
{
PAVIEDITSTREAM pedit = NULL;
HRESULT hr;
pavi->QueryInterface(IID_IAVIEditStream, (LPVOID FAR *) &pedit);
if (!pedit)
return ResultFromScode(E_NOINTERFACE);
hr = pedit->Cut(plStart, plLength, ppResult);
pedit->Release();
return hr;
}
STDAPI EditStreamCopy(PAVISTREAM pavi, LONG FAR *plStart, LONG FAR *plLength, PAVISTREAM FAR * ppResult)
{
PAVIEDITSTREAM pedit = NULL;
HRESULT hr;
pavi->QueryInterface(IID_IAVIEditStream, (LPVOID FAR *) &pedit);
if (!pedit)
return ResultFromScode(E_NOINTERFACE);
hr = pedit->Copy(plStart, plLength, ppResult);
pedit->Release();
return hr;
}
STDAPI EditStreamPaste(PAVISTREAM pavi, LONG FAR *plPos, LONG FAR *plLength, PAVISTREAM pstream, LONG lStart, LONG lLength)
{
PAVIEDITSTREAM pedit = NULL;
HRESULT hr;
pavi->QueryInterface(IID_IAVIEditStream, (LPVOID FAR *) &pedit);
if (!pedit)
return ResultFromScode(E_NOINTERFACE);
hr = pedit->Paste(plPos, plLength, pstream, lStart, lLength);
pedit->Release();
return hr;
}
STDAPI EditStreamClone(PAVISTREAM pavi, PAVISTREAM FAR *ppResult)
{
PAVIEDITSTREAM pedit = NULL;
HRESULT hr;
pavi->QueryInterface(IID_IAVIEditStream, (LPVOID FAR *) &pedit);
if (!pedit)
return ResultFromScode(E_NOINTERFACE);
hr = pedit->Clone(ppResult);
pedit->Release();
return hr;
}
STDAPI EditStreamSetInfo(PAVISTREAM pavi, AVISTREAMINFO FAR *lpInfo, LONG cbInfo)
{
PAVIEDITSTREAM pedit = NULL;
HRESULT hr;
pavi->QueryInterface(IID_IAVIEditStream, (LPVOID FAR *) &pedit);
if (!pedit)
return ResultFromScode(E_NOINTERFACE);
hr = pedit->SetInfo(lpInfo, cbInfo);
pedit->Release();
return hr;
}
STDAPI EditStreamSetName(PAVISTREAM pavi, LPCSTR lpszName)
{
PAVIEDITSTREAM pedit = NULL;
HRESULT hr;
AVISTREAMINFO info;
pavi->QueryInterface(IID_IAVIEditStream, (LPVOID FAR *) &pedit);
if (!pedit)
return ResultFromScode(E_NOINTERFACE);
pavi->Info(&info, sizeof(info));
_fstrncpy(info.szName, lpszName, sizeof(info.szName));
hr = pedit->SetInfo(&info, sizeof(info));
pedit->Release();
return hr;
}
// #define EDITCHECK
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
#define USUAL_ALLOC 16
#define EXTRA_ALLOC 8
HRESULT CEditStream::AllocEditSpace(LONG l, LONG cNew)
{
LPEDIT p;
LONG size;
if (cedits + cNew > maxedits) {
size = maxedits + max(cNew + EXTRA_ALLOC, USUAL_ALLOC);
p = (LPEDIT) GlobalReAllocPtr(edits, size * sizeof(EDIT), GHND | GMEM_SHARE);
if (!p)
return ResultFromScode(AVIERR_MEMORY);
edits = p;
maxedits = size;
}
if (l < cedits)
MemCopy((HPSTR) &edits[l + cNew],
(HPSTR) &edits[l],
(cedits - l) * sizeof(EDIT));
cedits += cNew;
return AVIERR_OK;
}
HRESULT CEditStream::PossiblyRemoveEdit(LONG l)
{
if (edits[l].lLength > 0)
return AVIERR_OK;
if (edits[l].pavi)
AVIStreamRelease(edits[l].pavi);
--cedits;
if (l < cedits)
MemCopy((HPSTR) &edits[l],
(HPSTR) &edits[l + 1],
(cedits - l) * sizeof(EDIT));
return AVIERR_OK;
}
CEditStream FAR * CEditStream::NewEditStream(PAVISTREAM psSource)
{
CEditStream FAR * pedit;
pedit = new CEditStream;
if (pedit)
(pedit->Create)((LONG) psSource, 0);
// !!! error check
return pedit;
}
STDAPI CreateEditableStream(
PAVISTREAM FAR * ppsEditable,
PAVISTREAM psSource)
{
// First, check if the stream is already editable....
if (psSource) {
PAVIEDITSTREAM paviedit = NULL;
psSource->QueryInterface(IID_IAVIEditStream, (LPVOID FAR *) &paviedit);
if (paviedit) {
paviedit->Clone(ppsEditable);
paviedit->Release();
return AVIERR_OK;
}
}
*ppsEditable = (PAVISTREAM) CEditStream::NewEditStream(psSource);
if (!*ppsEditable)
return ResultFromScode(AVIERR_MEMORY);
return AVIERR_OK;
}
///////////////////////////////////////////////////////////////////////////
//
// EditStreamOpen()
//
// open a single stream of a particular type from a AVI file.
//
// params:
// szFile - AVI file name
// fccType - stream type 0 for any type
// iStream - zero based stream number
//
// returns:
// a PAVISTREAM for the specifed stream or NULL.
//
// example:
//
// EditStreamOpen(pavi, "Foo.avi", 0, 0)
//
// will open stream 0 (the first stream)
//
// EditStreamOpen(pavi, "Foo.avi", 1)
//
// will open stream 1
//
// EditStreamOpenStream(pavi, "Foo.avi", 'vids', 0)
//
// will open the first video stream
//
// AVIOpenStream(pavi, "Foo.avi", 'auds', 0)
//
// will open the first audio stream
//
///////////////////////////////////////////////////////////////////////////
HRESULT STDMETHODCALLTYPE CEditStream::Create(LONG lParam1, LONG lParam2)
{
this->edits = (LPEDIT) GlobalAllocPtr(GHND | GMEM_SHARE, USUAL_ALLOC * sizeof(EDIT));
if (this->edits == NULL)
return ResultFromScode(AVIERR_MEMORY);
this->maxedits = USUAL_ALLOC;
this->ulRefCount = 1;
this->pgf = NULL;
this->psgf = NULL;
this->lpbiLast = NULL;
this->fFullFrames = FALSE;
this->edits[0].pavi = (PAVISTREAM)lParam1;
_fmemset(&this->sinfo, 0, sizeof(this->sinfo));
this->cedits = 1;
if (this->edits[0].pavi) {
AVIStreamAddRef(this->edits[0].pavi);
this->edits[0].lStart = AVIStreamStart(this->edits[0].pavi);
this->edits[0].lLength = AVIStreamLength(this->edits[0].pavi);
AVIStreamInfo(this->edits[0].pavi, &this->sinfo, sizeof(this->sinfo));
CheckEditList();
} else {
this->edits[0].lStart = 0;
this->edits[0].lLength = 0;
}
DPF("Edit %08lx: Usage++=%lx\n", (DWORD) (LPVOID) this, 1L);
//
// all done return success.
//
return 0; // success
}
///////////////////////////////////////////////////////////////////////////
//
// EditStreamAddRef()
//
// increase the reference count of the stream
//
///////////////////////////////////////////////////////////////////////////
ULONG STDMETHODCALLTYPE CEditStream::AddRef()
{
DPF("Edit %08lx: Usage++=%lx\n", (DWORD) (LPVOID) this, this->ulRefCount + 1);
return ++this->ulRefCount;
}
///////////////////////////////////////////////////////////////////////////
//
// EditStreamRelease()
//
// close a EditStream stream
//
///////////////////////////////////////////////////////////////////////////
ULONG STDMETHODCALLTYPE CEditStream::Release()
{
LONG l;
DPF("Edit %08lx: Usage--=%lx\n", (DWORD) (LPVOID) this, this->ulRefCount - 1);
if (--this->ulRefCount)
return this->ulRefCount;
// free edits....
for (l = 0; l < this->cedits; l++) {
if (this->edits[l].pavi)
AVIStreamRelease(this->edits[l].pavi);
}
GlobalFreePtr(this->edits);
this->edits = 0;
if (this->pgf)
AVIStreamGetFrameClose(this->pgf);
delete this;
return 0;
}
LPBITMAPINFOHEADER NEAR PASCAL CEditStream::CallGetFrame(
PAVISTREAM p,
LONG l)
{
if (psgf != p) {
PGETFRAME pgfNew;
pgfNew = AVIStreamGetFrameOpen(p, NULL);
if (!pgfNew)
return NULL;
if (pgf) {
#ifdef DEBUG
DPF("Trying to SetFormat %dx%dx%d '%4.4s'\n",
(int)lpbiLast->biWidth,
(int)lpbiLast->biHeight,
(int)lpbiLast->biBitCount,
(lpbiLast->biCompression == BI_RGB ? (LPSTR)"None" :
lpbiLast->biCompression == BI_RLE8 ? (LPSTR)"Rle8" :
lpbiLast->biCompression == BI_RLE4 ? (LPSTR)"Rle4" :
(LPSTR)&lpbiLast->biCompression));
#endif
if (pgfNew->SetFormat(lpbiLast, NULL, 0, 0, -1, -1) != AVIERR_OK) {
DPF("Couldn't AVIStreamGetFrameSetFormat!\n");
AVIStreamGetFrameClose(pgfNew);
return NULL;
}
AVIStreamGetFrameClose(pgf);
}
pgf = pgfNew;
psgf = p;
}
lpbiLast = (LPBITMAPINFOHEADER) AVIStreamGetFrame(pgf, l);
if (lpbiLast)
sinfo.dwSuggestedBufferSize = lpbiLast->biSizeImage;
return lpbiLast;
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
HRESULT STDMETHODCALLTYPE CEditStream::ReadFormat(LONG lPos, LPVOID lpFormat, LONG FAR *lpcbFormat)
{
PAVISTREAM p;
LONG l;
HRESULT hr;
if ((lPos < (LONG) sinfo.dwStart) ||
(lPos >= (LONG) (sinfo.dwStart + sinfo.dwLength))) {
return ResultFromScode(AVIERR_BADPARAM);
}
hr = ResolveEdits(lPos, &p, &l, NULL, FALSE);
if (hr != 0) {
DPF("ReadFormat: ResolveEdits failed!\n");
return hr;
}
if (fFullFrames) {
LPBITMAPINFOHEADER lpbi;
LONG lSize;
// This isn't really right: we really need to make the formats
// agree. Should we just get the format from the first frame?
lpbi = CallGetFrame(p, l);
if (!lpbi) {
DPF("ReadFormat: GetFrame failed!\n");
return ResultFromScode(E_FAIL);
}
lSize = lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD);
if (lpFormat)
hmemcpy(lpFormat, lpbi, min(*lpcbFormat, lSize));
*lpcbFormat = lSize;
return 0;
} else {
return AVIStreamReadFormat(p, l, lpFormat, lpcbFormat);
}
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
HRESULT STDMETHODCALLTYPE CEditStream::Info(AVISTREAMINFO FAR * psi, LONG lSize)
{
if (psi)
hmemcpy(psi, &sinfo, min(lSize, sizeof(sinfo)));
return 0; // !!! sizeof(pavi->sinfo);
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
LONG STDMETHODCALLTYPE CEditStream::FindSample(LONG lPos, LONG lFlags)
{
PAVISTREAM p;
LONG l;
LONG edit;
LONG lRet;
HRESULT hr;
if ((lPos < (LONG) sinfo.dwStart) ||
(lPos >= (LONG) (sinfo.dwStart + sinfo.dwLength))) {
return -1;
}
hr = ResolveEdits(lPos, &p, &l, &edit, TRUE);
if (hr != 0) {
DPF("FindSample: error from ResolveEdits!\n");
return -1;
}
if (lFlags & FIND_FORMAT) {
// !!!! This isn't right, obviously.
if (lFlags & FIND_PREV)
return 0;
else
return -1;
}
if (this->fFullFrames) {
return lPos;
}
// !!! This won't really work, especially for searching forward.
lRet = AVIStreamFindSample(p, l, lFlags);
#ifdef DEBUG
if (lRet < edits[edit].lStart) {
DPF("We were about to return a key frame before a segment: returning %ld instead of %ld.\n", edits[edit].lStart, lRet);
}
#endif
// DPF("FindSample: lPos = %ld, Key = %ld\n", lPos, lPos - (l - lRet));
return lPos - (l - lRet);
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
HRESULT STDMETHODCALLTYPE CEditStream::Read(
LONG lStart,
LONG lSamples,
LPVOID lpBuffer,
LONG cbBuffer,
LONG FAR * plBytes,
LONG FAR * plSamples)
{
PAVISTREAM p;
LONG l;
LONG edit;
LONG lSamplesRead;
LONG lBytesRead;
LONG lSamplesThisTime;
HRESULT hr;
if (plBytes)
*plBytes = 0;
if (plSamples)
*plSamples = 0;
#ifdef TOOMUCHDEBUG
if (lpBuffer) {
DPF("Read %08lx: Start = %ld Length = %ld\n", (DWORD) (LPVOID) this, lStart, lSamples);
}
#endif
if ((lStart < (LONG) sinfo.dwStart) ||
(lStart >= (LONG) (sinfo.dwStart + sinfo.dwLength))) {
DPF("Read at position %ld, start = %lu, len = %lu\n", lStart, sinfo.dwStart, sinfo.dwStart + sinfo.dwLength);
return ResultFromScode(AVIERR_BADPARAM);
}
while (lSamples) {
hr = ResolveEdits(lStart, &p, &l, &edit, FALSE);
if (hr != 0) {
DPF("Read: ResolveEdits failed!\n");
return ResultFromScode(E_FAIL);
}
// Don't read past the end of this edit.
if ((l - this->edits[edit].lStart) + lSamples > this->edits[edit].lLength)
lSamplesThisTime = this->edits[edit].lLength - (l - this->edits[edit].lStart);
else
lSamplesThisTime = lSamples;
if (this->fFullFrames) {
LPBITMAPINFOHEADER lpbi;
LPVOID lp;
lpbi = CallGetFrame(p, l);
if (!lpbi) {
DPF("Read: GetFrame failed!\n");
return ResultFromScode(E_FAIL);
}
//
// a NULL buffer means return the size buffer needed to read
// the given sample.
//
if (lpBuffer == NULL)
goto exit;
lp = (LPBYTE) lpbi + lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD);
if (cbBuffer >= (LONG) lpbi->biSizeImage) {
hmemcpy(lpBuffer, lp, lpbi->biSizeImage);
exit:
if (plBytes)
*plBytes = lpbi->biSizeImage;
if (plSamples)
*plSamples = 1;
return 0;
}
if (plBytes)
*plBytes = 0;
if (plSamples)
*plSamples = 0;
return ResultFromScode(AVIERR_BUFFERTOOSMALL);
} else {
hr = AVIStreamRead(p, l, lSamplesThisTime, lpBuffer, cbBuffer,
&lBytesRead, &lSamplesRead);
if (hr != NOERROR)
return hr;
if (plBytes)
*plBytes += lBytesRead;
if (plSamples)
*plSamples += lSamplesRead;
lpBuffer = (BYTE _huge *) lpBuffer + lBytesRead;
lStart += lSamplesThisTime;
lSamples -= lSamplesThisTime;
cbBuffer -= lBytesRead;
// If we've read up to the end of the file,
// stop now, rather than return an error....
if (lStart >= (LONG) (this->sinfo.dwLength + this->sinfo.dwStart))
break;
}
}
#ifdef TOOMUCHDEBUG
if (lpBuffer && plBytes) {
DPF("Read %08lx: Bytes Read = %ld\n", (DWORD) (LPVOID) this, *plBytes);
}
#endif
return 0;
}
void CEditStream::CheckEditList()
{
#ifdef EDITCHECK
LONG lTotal = 0;
LONG l;
DPF("Edit list %08lx: %s\n", (DWORD) this, fFullFrames ? (LPSTR) " (Using full frames)" : (LPSTR) "");
for (l = 0; l < cedits; l++) {
DPF("\t\t%ld:\t%08lx\t%ld\t%ld\n", l, (DWORD) (LPVOID) edits[l].pavi, edits[l].lStart, edits[l].lLength);
lTotal += edits[l].lLength;
}
if (lTotal != (LONG) sinfo.dwLength) {
* (LPSTR) 0 = 0;
}
#endif
}
HRESULT CEditStream::ResolveEdits(LONG lPos, PAVISTREAM FAR *ppavi,
LONG FAR *plPos, LONG FAR *pl, BOOL fAllowEnd)
{
LONG edit;
//
// Search edit list, get position...
//
if (lPos < (LONG) this->sinfo.dwStart) {
DPF("ResolveEdits: Read at %ld, before start at %ld\n", lPos, this->sinfo.dwStart);
return ResultFromScode(AVIERR_BADPARAM);
}
lPos -= (LONG) this->sinfo.dwStart;
for (edit = 0; edit < this->cedits; edit++) {
if (lPos < this->edits[edit].lLength) {
*ppavi = this->edits[edit].pavi;
*plPos = lPos + this->edits[edit].lStart;
if (pl)
*pl = edit;
return 0;
}
lPos -= this->edits[edit].lLength;
}
// Normally, we don't return a position at the end of an edit--we instead
// go to the next thing.
if (lPos == 0 && fAllowEnd) {
edit--;
*ppavi = this->edits[edit].pavi;
*plPos = this->edits[edit].lStart + this->edits[edit].lLength;
if (pl)
*pl = edit;
return 0;
}
*ppavi = 0;
*plPos = 0;
if (pl)
*pl = 0;
return ResultFromScode(AVIERR_BADPARAM);
}
//
// Cut:
//
// Takes start, length to cut out
//
// returns actual start, length cut, along with new stream
//
STDMETHODIMP CEditStream::Cut(LONG FAR *plStart, LONG FAR *plLength, PAVISTREAM FAR * ppResult)
{
HRESULT hr = AVIERR_OK;
PAVISTREAM p;
LONG l;
LONG edit;
LONG lStart, lLength;
l = AVIStreamLength(this);
if (ppResult)
*ppResult = 0;
if (!plStart || !plLength) {
return ResultFromScode(AVIERR_BADPARAM);
}
if (*plStart < 0) {
return ResultFromScode(AVIERR_BADPARAM);
}
if (*plLength < 0 || *plStart + *plLength > l) {
if (*plStart >= l)
return ResultFromScode(AVIERR_BADPARAM);
*plLength = l - *plStart;
}
#ifdef KEYALWAYS
// Make cut end at key frame
for (l = *plStart + *plLength; l < AVIStreamLength(this); l++) {
if (AVIStreamFindSample(this, l, 0) == l)
break;
}
*plLength = l - *plStart;
#else
// we cut whatever they ask us to....
#endif
// Make a copy of the section being cut out
if (ppResult) {
// This will make cut start at key frame if it needs to
hr = this->Copy(plStart, plLength, ppResult);
if (hr != AVIERR_OK)
return hr;
}
lLength = *plLength;
lStart = *plStart;
#ifndef KEYALWAYS
if (!this->fFullFrames &&
lStart + lLength < AVIStreamLength(this) &&
AVIStreamFindSample(this, lStart + lLength, 0) != lStart + lLength) {
DPF("Cut: Converting stream to full frames\n");
this->fFullFrames = TRUE;
this->sinfo.dwFormatChangeCount++;
this->sinfo.fccHandler = 0;
}
#endif
// Now do the actual cut
hr = ResolveEdits(lStart, &p, &l, &edit, FALSE);
if (hr != NOERROR)
return hr;
if (this->edits[edit].lStart + this->edits[edit].lLength > l + lLength) {
// The part cut out is entirely within this edit.
if (this->edits[edit].lStart == l) {
// The part cut out is the start of this edit
this->edits[edit].lStart = l + lLength;
this->edits[edit].lLength -= lLength;
} else {
hr = AllocEditSpace(edit, 1);
if (hr == AVIERR_OK) {
this->edits[edit] = this->edits[edit+1];
if (this->edits[edit].pavi)
AVIStreamAddRef(this->edits[edit].pavi);
this->edits[edit].lStart = this->edits[edit+1].lStart;
this->edits[edit].lLength = l - this->edits[edit].lStart;
this->edits[edit+1].lStart = l + lLength;
this->edits[edit+1].lLength -= lLength +
this->edits[edit].lLength;
}
}
} else if (this->edits[edit].lStart + this->edits[edit].lLength == l + lLength) {
// The part cut out is the end of this edit
this->edits[edit].lLength = l - this->edits[edit].lStart;
} else {
LONG lTemp = lLength;
// We're cutting out more than this one edit.
// First, cut out the rest of this edit.
lTemp -= this->edits[edit].lStart + this->edits[edit].lLength - l;
this->edits[edit].lLength = l - this->edits[edit].lStart;
edit++;
// As long as subsequent edits are still shorter than the cut,
// kill them..
while (edit < this->cedits &&
this->edits[edit].lLength < lTemp) {
lTemp -= this->edits[edit].lLength;
this->edits[edit].lLength = 0;
edit++;
}
if (edit < this->cedits) {
this->edits[edit].lStart += lTemp;
this->edits[edit].lLength -= lTemp;
}
}
if (hr == AVIERR_OK) {
this->sinfo.dwLength -= lLength;
this->sinfo.dwEditCount++;
CheckEditList();
} else {
if (ppResult)
AVIStreamRelease(*ppResult);
}
return hr;
}
//
// Copy:
//
// Takes start, length to cut out
//
// returns actual start, length cut, along with new stream
//
//
STDMETHODIMP CEditStream::Copy(LONG FAR *plStart, LONG FAR *plLength, PAVISTREAM FAR * ppResult)
{
PAVISTREAM p1;
LONG l1;
LONG edit1;
PAVISTREAM p2;
LONG l2;
LONG edit2;
LONG l;
CEditStream FAR *p;
HRESULT hr;
LONG lStart, lLength;
l1 = AVIStreamLength(this);
// If start, length < 0, pick some defaults
if (*plStart < 0)
*plStart = 0;
if (*plLength < 0)
*plLength = l1 - *plStart;
// Make sure the start position is within range
if (*plStart > l1) {
if (ppResult)
*ppResult = 0;
return ResultFromScode(AVIERR_BADPARAM);
}
// Make sure the length is within range
if (*plStart + *plLength > l1)
*plLength = l1 - *plStart;
#ifdef KEYALWAYS
// Make copy start at key frame
lStart = AVIStreamFindSample(this, *plStart, 0);
*plLength += *plStart - lStart;
*plStart = lStart;
#endif
lLength = *plLength;
lStart = *plStart;
p = NewEditStream(NULL);
*ppResult = (PAVISTREAM) p;
if (!p)
return ResultFromScode(AVIERR_MEMORY);
hmemcpy(&p->sinfo, &this->sinfo, sizeof(p->sinfo));
if (lLength <= 0)
lLength = (LONG) (p->sinfo.dwLength + p->sinfo.dwStart) - lStart;
hr = ResolveEdits(lStart, &p1, &l1, &edit1, FALSE);
hr = ResolveEdits(lStart + lLength, &p2, &l2, &edit2, TRUE);
if (edit1 == edit2) {
p->edits[0].pavi = p1;
if (p1)
AVIStreamAddRef(p1);
p->edits[0].lStart = l1;
p->edits[0].lLength = lLength;
} else {
hr = p->AllocEditSpace(1, edit2 - edit1);
for (l = 0; l <= edit2 - edit1; l++) {
if (l == 0) {
p->edits[l].pavi = p1;
if (p1)
AVIStreamAddRef(p1);
p->edits[l].lStart = l1;
p->edits[l].lLength = this->edits[edit1].lStart +
this->edits[edit1].lLength - l1;
} else if (l < edit2 - edit1) {
p->edits[l] = this->edits[l+edit1];
if (p->edits[l].pavi)
AVIStreamAddRef(p->edits[l].pavi);
} else {
p->edits[l] = this->edits[edit2];
if (p->edits[l].pavi)
AVIStreamAddRef(p->edits[l].pavi);
p->edits[l].lLength = l2 - p->edits[l].lStart;
}
}
p->PossiblyRemoveEdit(edit2 - edit1);
p->PossiblyRemoveEdit(0);
}
#ifndef KEYALWAYS
l1 = AVIStreamFindSample(p->edits[0].pavi, p->edits[0].lStart, 0);
DPF("edit starts at %ld, key at %ld\n", p->edits[0].lStart, l1);
if (l1 != p->edits[0].lStart) {
p->fFullFrames = TRUE;
DPF("Copy: Converting new stream to full frames\n");
}
#endif
AVIStreamInfo(this->edits[0].pavi, &p->sinfo, sizeof(p->sinfo));
p->sinfo.dwStart = 0;
p->sinfo.dwLength = (DWORD) lLength;
p->sinfo.dwEditCount = 0;
p->sinfo.dwFormatChangeCount = 0;
if (p->fFullFrames)
p->sinfo.fccHandler = 0;
p->CheckEditList();
CheckEditList();
return AVIERR_OK;
}
/**************************************************************************
* @doc INTERNAL DRAWDIB
*
* @api BOOL | DibEq | This function compares two dibs.
*
* @parm LPBITMAPINFOHEADER lpbi1 | Pointer to one bitmap.
* this DIB is assumed to have the colors after the BITMAPINFOHEADER
*
* @parm LPBITMAPINFOHEADER | lpbi2 | Pointer to second bitmap.
* this DIB is assumed to have the colors after biSize bytes.
*
* @rdesc Returns TRUE if bitmaps are identical, FALSE otherwise.
*
**************************************************************************/
inline BOOL DibEq(LPBITMAPINFOHEADER lpbi1, LPBITMAPINFOHEADER lpbi2)
{
return
lpbi1->biCompression == lpbi2->biCompression &&
lpbi1->biSize == lpbi2->biSize &&
lpbi1->biWidth == lpbi2->biWidth &&
lpbi1->biHeight == lpbi2->biHeight &&
lpbi1->biBitCount == lpbi2->biBitCount;
}
BOOL AreVideoStreamsCompatible(PAVISTREAM ps1, PAVISTREAM ps2)
{
LONG cb1, cb2;
BITMAPINFOHEADER bih1, bih2;
AVIStreamReadFormat(ps1, AVIStreamStart(ps1), NULL, &cb1);
AVIStreamReadFormat(ps2, AVIStreamStart(ps2), NULL, &cb2);
if (cb1 != cb2)
return FALSE;
cb1 = sizeof(bih1);
cb2 = sizeof(bih2);
AVIStreamReadFormat(ps1, AVIStreamStart(ps1), &bih1, &cb1);
AVIStreamReadFormat(ps2, AVIStreamStart(ps2), &bih2, &cb2);
if (DibEq(&bih1, &bih2))
return TRUE;
return FALSE;
}
BOOL AreAudioStreamsCompatible(PAVISTREAM ps1, PAVISTREAM ps2)
{
LONG cb1, cb2;
LPVOID lpf;
BOOL f;
AVIStreamReadFormat(ps1, AVIStreamStart(ps1), NULL, &cb1);
AVIStreamReadFormat(ps2, AVIStreamStart(ps2), NULL, &cb2);
if (cb1 != cb2)
return FALSE;
lpf = GlobalAllocPtr(GHND, cb1 + cb2);
if (!lpf)
return FALSE; // !!!
AVIStreamReadFormat(ps1, AVIStreamStart(ps1), lpf, &cb1);
AVIStreamReadFormat(ps2, AVIStreamStart(ps2), (BYTE FAR *)lpf + cb1, &cb2);
f = !_fmemcmp(lpf, (BYTE FAR *)lpf + cb1, (UINT) cb1);
GlobalFreePtr(lpf);
return f;
}
//
// Paste:
//
// Takes stream to paste, along with start and length within that stream,
// and also target stream and position within the stream to do the paste.
//
// Returns position and length pasted.
//
STDMETHODIMP CEditStream::Paste(LONG FAR *plPos, LONG FAR *plLength, PAVISTREAM pstream, LONG lStart, LONG lLength)
{
PAVISTREAM p;
LONG l;
LONG edit;
HRESULT hr;
LONG lPos;
CEditStream FAR *pnew;
AVISTREAMINFO strinfo;
AVIStreamInfo(pstream, &strinfo, sizeof(strinfo));
if (this->sinfo.fccType == 0) {
AVIStreamInfo(pstream, &this->sinfo, sizeof(this->sinfo));
this->sinfo.dwLength = 0;
this->sinfo.dwStart = *plPos;
}
if (*plPos > (LONG) (sinfo.dwLength + sinfo.dwStart)) {
// !!! We should handle this case....
return ResultFromScode(AVIERR_BADPARAM);
}
#ifdef KEYALWAYS
// Make paste go before a key frame...
*plPos = AVIStreamFindSample(this, *plPos, 0);
#endif
lPos = *plPos;
if (strinfo.fccType != this->sinfo.fccType) {
DPF("Paste: Incompatible stream types!\n");
return ResultFromScode(AVIERR_UNSUPPORTED);
}
if (lLength <= 0 || ((lStart + lLength) >
(LONG) (strinfo.dwStart + strinfo.dwLength))) {
if (lStart >= (LONG) (strinfo.dwLength + strinfo.dwStart))
return ResultFromScode(AVIERR_BADPARAM);
lLength = (LONG) (strinfo.dwLength + strinfo.dwStart) - lStart;
}
if ((DWORD) lPos + (DWORD) lLength > 0x80000000) {
DPF("Paste result would be more than 2 billion frames!\n");
return ResultFromScode(AVIERR_MEMORY);
}
// !!! What if the frame rates don't match?
#define SIZEMISMATCH(rc1, rc2) \
(((rc1.right - rc1.left) != (rc2.right - rc2.left)) || \
((rc1.bottom - rc1.top) != (rc2.bottom - rc2.top)))
if (strinfo.fccType == streamtypeVIDEO &&
SIZEMISMATCH(strinfo.rcFrame, this->sinfo.rcFrame)) {
// !!! It would be nice if this worked.
DPF("Paste: Video streams are different sizes!\n");
return ResultFromScode(AVIERR_UNSUPPORTED);
}
if (this->sinfo.fccType == streamtypeAUDIO) {
if (!AreAudioStreamsCompatible((PAVISTREAM) this, pstream)) {
DPF("Paste: Audio streams are different formats!\n");
return ResultFromScode(AVIERR_UNSUPPORTED);
}
}
pnew = NULL;
pstream->QueryInterface(CLSID_EditStream, (LPVOID FAR *) &pnew);
#ifndef KEYALWAYS
if (this->sinfo.fccType == streamtypeVIDEO) {
if (!this->fFullFrames) {
if ((!AVIStreamIsKeyFrame(pstream, lStart) ||
(pnew && pnew->fFullFrames)) ||
((lPos < (LONG) (sinfo.dwLength + sinfo.dwStart)) &&
!AVIStreamIsKeyFrame((PAVISTREAM) this, lPos)) ||
!AreVideoStreamsCompatible((PAVISTREAM) this, pstream)) {
// !!! What if we're pasting, say, an 8-bit and a 32-bit
// movie together? Do we have to pick a common format
// to convert to?
CallGetFrame(this->edits[0].pavi, this->edits[0].lStart);
if (CallGetFrame(pstream, lStart) == NULL) {
DPF("Paste: Can't make a common format!\n");
if (pnew)
pnew->Release();
return ResultFromScode(AVIERR_BADFORMAT);
}
this->fFullFrames = TRUE;
DPF("Paste: Converting stream to full frames\n");
this->sinfo.dwFormatChangeCount++;
// ??? !!! Call get frame once, just so it's been done....
}
} else {
if (CallGetFrame(pstream, lStart) == NULL) {
DPF("Paste: Can't make a common format!\n");
if (pnew)
pnew->Release();
return ResultFromScode(AVIERR_BADFORMAT);
}
}
}
#endif
// Find where to do the paste...
hr = ResolveEdits(lPos, &p, &l, &edit, TRUE);
// Report back the size of what we pasted...
if (plLength)
*plLength = lLength;
if (pnew) {
LONG lNew;
// The inserted stream is itself an edit list; take advantage
// of this fact.
hr = AllocEditSpace(edit, 1 + pnew->cedits);
this->edits[edit].pavi = this->edits[edit + 1 + pnew->cedits].pavi;
if (this->edits[edit].pavi)
AVIStreamAddRef(this->edits[edit].pavi);
this->edits[edit].lStart = this->edits[edit + 1 + pnew->cedits].lStart;
this->edits[edit].lLength = l - this->edits[edit].lStart;
// !!! We're ignoring lStart and lLength!
for (lNew = 0; lNew < pnew->cedits; lNew++) {
this->edits[edit + 1 + lNew] = pnew->edits[lNew];
AVIStreamAddRef(pnew->edits[lNew].pavi);
}
this->edits[edit + pnew->cedits + 1].lStart = l;
this->edits[edit + pnew->cedits + 1].lLength -= this->edits[edit].lLength;
// Get rid of zero-length edits....
PossiblyRemoveEdit(edit + pnew->cedits + 1);
PossiblyRemoveEdit(edit);
this->sinfo.dwLength += lLength;
pnew->CheckEditList();
pnew->Release();
} else {
// Just insert the stream as a blob.
hr = AllocEditSpace(edit, 2);
this->edits[edit].pavi = this->edits[edit+2].pavi;
if (this->edits[edit].pavi)
AVIStreamAddRef(this->edits[edit].pavi);
this->edits[edit].lStart = this->edits[edit+2].lStart;
this->edits[edit].lLength = l - this->edits[edit+2].lStart;
this->edits[edit+ 1].pavi = pstream;
if (pstream)
AVIStreamAddRef(pstream);
this->edits[edit + 1].lStart = lStart;
this->edits[edit + 1].lLength = lLength;
this->edits[edit + 2].lStart = l;
this->edits[edit + 2].lLength -= this->edits[edit].lLength;
// No addref here, since the edit we're splitting had a ref already
this->sinfo.dwLength += lLength;
// Get rid of zero-length edits....
PossiblyRemoveEdit(edit + 2);
PossiblyRemoveEdit(edit);
}
CheckEditList();
this->sinfo.dwEditCount++;
return AVIERR_OK;
}
STDMETHODIMP CEditStream::Clone(PAVISTREAM FAR *ppResult)
{
CEditStream FAR * pnew;
HRESULT hr;
LONG l;
pnew = NewEditStream(NULL);
*ppResult = (PAVISTREAM) pnew;
if (!pnew)
return ResultFromScode(AVIERR_MEMORY);
if (this->cedits > 1) {
hr = pnew->AllocEditSpace(1, this->cedits - 1);
if (hr != NOERROR) {
// !!! Clean things up
return hr;
}
}
for (l = 0; l < this->cedits; l++) {
pnew->edits[l] = this->edits[l];
if (pnew->edits[l].pavi)
AVIStreamAddRef(pnew->edits[l].pavi);
}
pnew->sinfo = this->sinfo;
pnew->fFullFrames = this->fFullFrames;
pnew->CheckEditList();
return AVIERR_OK;
}
STDMETHODIMP CEditStream::SetInfo(AVISTREAMINFO FAR * lpInfo, LONG cbInfo)
{
if ((cbInfo < sizeof(AVISTREAMINFO)) ||
(IsBadReadPtr(lpInfo, sizeof(AVISTREAMINFO))))
return ResultFromScode(AVIERR_BADPARAM);
// Things we don't copy:
// fccType
// fccHandler
// dwFlags
// dwCaps
// dwLength
// dwInitialFrames
// dwSuggestedBufferSize
// dwSampleSize
// dwEditCount
// dwFormatChangeCount
this->sinfo.wPriority = lpInfo->wPriority;
this->sinfo.wLanguage = lpInfo->wLanguage;
this->sinfo.dwScale = lpInfo->dwScale;
this->sinfo.dwRate = lpInfo->dwRate;
this->sinfo.dwStart = lpInfo->dwStart; // !!! ???
this->sinfo.dwQuality = lpInfo->dwQuality;
this->sinfo.rcFrame = lpInfo->rcFrame;
if (lpInfo->szName[0])
_fmemcpy(this->sinfo.szName, lpInfo->szName, sizeof(this->sinfo.szName));
// The stream has been changed....
++this->sinfo.dwEditCount;
return NOERROR;
}
//
//
// Extra unimplemented functions.....
//
//
//
HRESULT STDMETHODCALLTYPE CEditStream::QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
{
if (riid == IID_IUnknown)
*ppvObj = ((IUnknown FAR *) (IAVIStream FAR *) this);
else if (riid == IID_IAVIStream)
*ppvObj = ((IAVIStream FAR *) this);
else if (riid == IID_IAVIEditStream)
*ppvObj = ((IAVIEditStream FAR *) this);
#if 0
else if ((riid == IID_IMarshal) && CanMarshalSimply()) // !!!! Remove once fixed!
*ppvObj = ((IMarshal FAR *) this);
#endif
else if (riid == CLSID_EditStream)
*ppvObj = this;
else { // unsupported interface
*ppvObj = NULL;
return ResultFromScode(E_NOINTERFACE);
}
AddRef();
return NOERROR;
}
HRESULT STDMETHODCALLTYPE CEditStream::ReadData (DWORD fcc, LPVOID lp, LONG FAR *lpcb)
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}
HRESULT STDMETHODCALLTYPE CEditStream::SetFormat (LONG lPos, LPVOID lpFormat, LONG cbFormat)
{
// !!! We could set the whole format of the stream here, and do mapping....
return ResultFromScode(AVIERR_UNSUPPORTED);
}
HRESULT STDMETHODCALLTYPE CEditStream::WriteData (DWORD fcc, LPVOID lp, LONG cb)
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}
HRESULT STDMETHODCALLTYPE CEditStream::Write (LONG lStart, LONG lSamples, LPVOID lpBuffer, LONG cbBuffer, DWORD dwFlags, LONG FAR *plSampWritten, LONG FAR *plBytesWritten)
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}
HRESULT STDMETHODCALLTYPE CEditStream::Delete (LONG lStart, LONG lSamples)
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}
HRESULT CEditStream::NewInstance(IUnknown FAR* pUnknownOuter,
REFIID riid,
LPVOID FAR* ppv)
{
CEditStream FAR * pedit;
HRESULT hr;
pedit = new CEditStream;
if (pedit)
(pedit->Create)(NULL, 0);
// !!! error check
hr = pedit->QueryInterface(riid, ppv);
if (FAILED(GetScode(hr)))
delete pedit;
return hr;
}
BOOL CEditStream::CanMarshalSimply()
{
LONG l;
LPUNKNOWN punk;
for (l = 0; l < this->cedits; l++) {
punk = NULL;
this->edits[l].pavi->QueryInterface(CLSID_AVISimpleUnMarshal,
(LPVOID FAR *) &punk);
if (!punk)
return FALSE;
punk->Release();
}
return TRUE;
}
STDMETHODIMP CEditStream::GetUnmarshalClass (REFIID riid, LPVOID pv,
DWORD dwDestContext, LPVOID pvDestContext,
DWORD mshlflags, LPCLSID pCid)
{
if (CanMarshalSimply()) {
DPF("UnMarshalClass called (simple)\n");
*pCid = CLSID_AVISimpleUnMarshal;
} else {
DPF("UnMarshalClass called (not simple)\n");
*pCid = CLSID_EditStream;
}
return NOERROR;
}
STDMETHODIMP CEditStream::GetMarshalSizeMax (REFIID riid, LPVOID pv,
DWORD dwDestContext, LPVOID pvDestContext,
DWORD mshlflags, LPDWORD pSize)
{
if (CanMarshalSimply())
*pSize = 4;
else
*pSize = 0;
return NOERROR;
}
STDMETHODIMP CEditStream::MarshalInterface (LPSTREAM pStm, REFIID riid,
LPVOID pv, DWORD dwDestContext, LPVOID pvDestContext,
DWORD mshlflags)
{
HRESULT hr;
if ((riid != IID_IAVIStream && riid != IID_IAVIFile && riid != IID_IUnknown))
return ResultFromScode(E_INVALIDARG);
if (CanMarshalSimply()) {
LPUNKNOWN pUnk = (LPUNKNOWN) (PAVISTREAM) this;
DPF("MarshalInterface called (simple): Marshalling %lx\n", (DWORD) pUnk);
if ((hr = pStm->Write(&pUnk, sizeof(pUnk), NULL)) == NOERROR)
AddRef();
} else {
DPF("MarshalInterface called (not simple)\n");
hr = ResultFromScode(AVIERR_UNSUPPORTED);
}
DPF("Returns %lx\n", (DWORD) (LPVOID) hr);
return hr;
}
STDMETHODIMP CEditStream::UnmarshalInterface (LPSTREAM pStm, REFIID riid,
LPVOID FAR* ppv)
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}
STDMETHODIMP CEditStream::ReleaseMarshalData (LPSTREAM pStm)
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}
STDMETHODIMP CEditStream::DisconnectObject (DWORD dwReserved)
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}
HRESULT STDMETHODCALLTYPE CEditStream::Reserved1()
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}
HRESULT STDMETHODCALLTYPE CEditStream::Reserved2()
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}
HRESULT STDMETHODCALLTYPE CEditStream::Reserved3()
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}
HRESULT STDMETHODCALLTYPE CEditStream::Reserved4()
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}
HRESULT STDMETHODCALLTYPE CEditStream::Reserved5()
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}
/*****************************************************************************
*
* dprintf() is called by the DPF macro if DEBUG is defined at compile time.
*
* The messages will be send to COM1: like any debug message. To
* enable debug output, add the following to WIN.INI :
*
* [debug]
* ICSAMPLE=1
*
****************************************************************************/
#ifdef DEBUG
#define MODNAME "EditStrm"
static BOOL fDebug = -1;
static void cdecl dprintf(LPSTR szFormat, ...)
{
char ach[128];
#ifdef WIN32
va_list va;
if (fDebug == -1)
fDebug = GetProfileIntA("Debug",MODNAME, FALSE);
if (!fDebug)
return;
va_start(va, szFormat);
if (szFormat[0] == '!')
ach[0]=0, szFormat++;
else
lstrcpyA(ach, MODNAME ": ");
wvsprintfA(ach+lstrlenA(ach),szFormat,(LPSTR)va);
va_end(va);
// lstrcatA(ach, "\r\r\n");
OutputDebugStringA(ach);
#else
if (fDebug == -1)
fDebug = GetProfileInt("Debug",MODNAME, FALSE);
if (!fDebug)
return;
if (szFormat[0] == '!')
ach[0]=0, szFormat++;
else
lstrcpy(ach, MODNAME ": ");
wvsprintf(ach+lstrlen(ach),szFormat,(LPSTR)(&szFormat+1));
// lstrcat(ach, "\r\r\n");
OutputDebugString(ach);
#endif
}
#endif