/**************************************************************************** * * 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 #include #include "avifile.h" #include #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