907 lines
23 KiB
C++
907 lines
23 KiB
C++
/***
|
|
Copyright (C) Microsoft Corporation 1985-1995. All rights reserved.
|
|
AVIIDX.C - AVI Index stuff
|
|
**/
|
|
|
|
#include <win32.h> // Win16/32 porting
|
|
#include <vfw.h>
|
|
#include "aviidx.h"
|
|
|
|
#ifdef AVIIDX_READONLY
|
|
#include "common.h" // for DEBUG
|
|
#else
|
|
#include "debug.h" // for DEBUG
|
|
#endif
|
|
|
|
|
|
#define INDEXALLOC 512
|
|
#define STACK _based(_segname("_STACK"))
|
|
|
|
// used by SearchIndex() to return where a sample is
|
|
|
|
typedef struct {
|
|
LONG lx; // index position
|
|
LONG lPos; // position in samples.
|
|
LONG lSize; // size in samples.
|
|
LONG lOffset; // file offset.
|
|
LONG lLength; // size in bytes.
|
|
}IDXPOS;
|
|
|
|
/*
|
|
* @doc INTERNAL
|
|
* @api PAVIINDEX | IndexAddFileIndex
|
|
* add a bunch of entries from a AVIFILE index to the index.
|
|
*/
|
|
EXTERN_C PAVIINDEX IndexAddFileIndex(PAVIINDEX px, AVIINDEXENTRY _huge* pidx, LONG cnt, LONG lAdjust, BOOL fRle)
|
|
{
|
|
LONG lx;
|
|
LONG l;
|
|
LONG lxRec;
|
|
DWORD ckid;
|
|
UINT stream;
|
|
DWORD offset;
|
|
DWORD length;
|
|
UINT flags;
|
|
|
|
Assert(px);
|
|
Assert(pidx);
|
|
|
|
if (px == NULL || pidx == NULL)
|
|
return NULL;
|
|
|
|
Assert(sizeof(AVIINDEXENTRY) > sizeof(AVIIDX));
|
|
|
|
// grow the index if needed.
|
|
if (px->nIndex + cnt > px->nIndexSize) {
|
|
LONG grow = px->nIndex + cnt - px->nIndexSize;
|
|
LPVOID p;
|
|
|
|
if (grow < INDEXALLOC)
|
|
grow = INDEXALLOC;
|
|
|
|
p = (LPVOID)GlobalReAllocPtr(px,
|
|
sizeof(AVIINDEX) + (px->nIndexSize + grow) * sizeof(AVIIDX),
|
|
GMEM_MOVEABLE | GMEM_SHARE);
|
|
if (!p)
|
|
return NULL;
|
|
|
|
px = (PAVIINDEX)p;
|
|
px->nIndexSize += grow;
|
|
}
|
|
|
|
for (lxRec = -1, l = 0; l < cnt; l++, pidx++) {
|
|
lx = px->nIndex + l;
|
|
|
|
// adjust the offset to be absolute
|
|
offset = pidx->dwChunkOffset + lAdjust;
|
|
length = pidx->dwChunkLength;
|
|
ckid = pidx->ckid;
|
|
stream = StreamFromFOURCC(ckid);
|
|
flags = 0;
|
|
|
|
if (ckid == listtypeAVIRECORD)
|
|
stream = STREAM_REC;
|
|
|
|
if (ckid == listtypeAVIRECORD)
|
|
lxRec = lx;
|
|
|
|
// handle over flows in a "sane" way.
|
|
if (offset >= MAX_OFFSET)
|
|
break;
|
|
|
|
if (stream >= MAX_STREAM)
|
|
break;
|
|
|
|
if (length >= MAX_LENGTH)
|
|
length = MAX_LENGTH - 1;
|
|
|
|
if (pidx->dwFlags & AVIIF_KEYFRAME)
|
|
flags |= IDX_KEY;
|
|
else
|
|
flags |= IDX_NONKEY;
|
|
|
|
// length == 0 samples are not real
|
|
if (length == 0)
|
|
flags &= ~(IDX_NONKEY | IDX_KEY);
|
|
|
|
// mark palette changes
|
|
if (TWOCCFromFOURCC(ckid) == cktypePALchange) {
|
|
flags |= IDX_PAL;
|
|
flags &= ~(IDX_NONKEY | IDX_KEY);
|
|
}
|
|
|
|
// fix up bogus index's by adding any missing KEYFRAME
|
|
// bits. ie this only applies for silly RLE files.
|
|
if (fRle && length > 0 && TWOCCFromFOURCC(ckid) == cktypeDIBbits)
|
|
flags |= IDX_KEY;
|
|
|
|
// do we need to support these?
|
|
if (fRle && TWOCCFromFOURCC(ckid) == aviTWOCC('d', 'x'))
|
|
flags |= IDX_HALF;
|
|
|
|
// audio is always a key.
|
|
if (TWOCCFromFOURCC(ckid) == cktypeWAVEbytes)
|
|
flags |= IDX_KEY | IDX_NONKEY; //hack to get audio back!
|
|
|
|
// make sure records are marked as contining a key
|
|
|
|
//if (lxRec > 0 && (flags & IDX_KEY))
|
|
// IndexSetKey(px, lxRec);
|
|
|
|
IndexSetFlags(px, lx, flags);
|
|
IndexSetOffset(px, lx, offset);
|
|
IndexSetLength(px, lx, length);
|
|
IndexSetStream(px, lx, stream);
|
|
}
|
|
|
|
cnt = l;
|
|
px->nIndex += cnt;
|
|
|
|
return px;
|
|
}
|
|
|
|
|
|
static LONG FAR PASCAL mmioReadProc(HMMIO hmmio, LONG lSeek, LONG lRead, LPVOID lpBuffer)
|
|
{
|
|
if (mmioSeek(hmmio, lSeek, SEEK_SET) == -1)
|
|
return -1;
|
|
|
|
if (mmioRead(hmmio, (HPSTR)lpBuffer, lRead) != lRead)
|
|
return -1;
|
|
|
|
return lRead;
|
|
}
|
|
|
|
|
|
static LONG FAR PASCAL mmioWriteProc(HMMIO hmmio, LONG lSeek, LONG lWrite, LPVOID lpBuffer)
|
|
{
|
|
if (mmioSeek(hmmio, lSeek, SEEK_SET) == -1)
|
|
return -1;
|
|
|
|
if (mmioWrite(hmmio, (HPSTR)lpBuffer, lWrite) != lWrite)
|
|
return -1;
|
|
|
|
return lWrite;
|
|
}
|
|
|
|
/*
|
|
* @doc INTERNAL
|
|
* @api PSTREAMINDEX | MakeStreamIndex
|
|
* makes a STREAMINDEX structure that will be used later to read/find samples in a stream.
|
|
*/
|
|
EXTERN_C PSTREAMINDEX MakeStreamIndex(PAVIINDEX px, UINT stream, LONG lStart, LONG lSampleSize, HANDLE hFile, STREAMIOPROC ReadProc, STREAMIOPROC WriteProc)
|
|
{
|
|
LONG lPos;
|
|
LONG lx;
|
|
PSTREAMINDEX psx;
|
|
|
|
Assert(px);
|
|
|
|
if (px == NULL)
|
|
return NULL;
|
|
|
|
psx = (PSTREAMINDEX)LocalAlloc(LPTR, sizeof(STREAMINDEX));
|
|
|
|
if (psx == NULL)
|
|
return NULL;
|
|
|
|
//!!! fixed length sample streams should never have this
|
|
|
|
if (lSampleSize != 0 && lStart < 0) {
|
|
#ifdef DEBUG
|
|
//AssertSz(0, "Audio streams should not have initial frames");
|
|
#endif
|
|
lStart = 0;
|
|
}
|
|
|
|
psx->px = px;
|
|
psx->lStart = lStart;
|
|
psx->lSampleSize = lSampleSize;
|
|
psx->lMaxSampleSize = 0;
|
|
psx->stream = stream;
|
|
psx->flags = 0;
|
|
psx->lStart = lStart;
|
|
psx->lxStart = IndexFirst(px, stream);
|
|
psx->lPos = lStart;
|
|
psx->lx = psx->lxStart;
|
|
psx->lFrames = 0;
|
|
psx->lKeyFrames = 0;
|
|
psx->lPalFrames = 0;
|
|
psx->lNulFrames = 0;
|
|
psx->hFile = hFile;
|
|
|
|
if (ReadProc == NULL)
|
|
psx->Read = (STREAMIOPROC)mmioReadProc;
|
|
else
|
|
psx->Read = ReadProc;
|
|
|
|
if (WriteProc == NULL)
|
|
psx->Write = (STREAMIOPROC)mmioWriteProc;
|
|
else
|
|
psx->Write = WriteProc;
|
|
|
|
lPos = lStart;
|
|
|
|
for (lx = psx->lxStart; lx >= 0 && lx < px->nIndex; lx = IndexNext(px, lx, 0)) {
|
|
if (psx->lMaxSampleSize < IndexLength(px, lx))
|
|
psx->lMaxSampleSize = IndexLength(px, lx);
|
|
|
|
// make sure the start sample is a key frame (unless it's wave data!)
|
|
if (lPos == 0 || (lPos >= 0 && lPos == psx->lStart)) {
|
|
if ((IndexFlags(px, lx) & (IDX_KEY | IDX_NONKEY)) !=
|
|
(IDX_KEY | IDX_NONKEY)) {
|
|
IndexSetKey(px, lx);
|
|
}
|
|
}
|
|
|
|
// make sure sample size is correct
|
|
if (psx->lSampleSize && ((IndexLength(px, lx) % lSampleSize) != 0)) {
|
|
DPF("!!! Bad chunk size found: force sample size to 0???\n");
|
|
// psx->lSampleSize = 0; !!! turned off because of possible
|
|
// partial audio chunks at file's end.....
|
|
}
|
|
|
|
// or all the flags together so we can see what a stream has.
|
|
psx->flags |= IndexFlags(px, lx);
|
|
|
|
// check for all key frames.
|
|
if (IndexFlags(px, lx) & IDX_KEY)
|
|
psx->lKeyFrames++;
|
|
|
|
// check for all palette changes
|
|
if (IndexFlags(px, lx) & IDX_PAL)
|
|
psx->lPalFrames++;
|
|
|
|
// check for empty frames
|
|
if (IndexLength(px, lx) == 0)
|
|
psx->lNulFrames++;
|
|
|
|
// advance the position
|
|
if (!(IndexFlags(px, lx) & IDX_NOTIME)) {
|
|
if (lSampleSize)
|
|
lPos += IndexLength(px, lx) / lSampleSize;
|
|
else
|
|
lPos++;
|
|
}
|
|
|
|
psx->lFrames++;
|
|
}
|
|
|
|
// correct the length
|
|
psx->lEnd = lPos;
|
|
|
|
DPF("MakeStreamIndex stream=#%d lStart=%ld, lEnd=%ld\n", stream, psx->lStart, psx->lEnd);
|
|
DPF(" lFrames = %ld, lKeys = %ld, lPals = %ld, lEmpty = %ld\n",
|
|
psx->lFrames, psx->lKeyFrames, psx->lPalFrames, psx->lNulFrames);
|
|
|
|
return psx;
|
|
}
|
|
|
|
#ifndef AVIIDX_READONLY
|
|
|
|
/*
|
|
* @doc INTERNAL
|
|
* @api PAVIINDEX | IndexGetFileIndex
|
|
* make a file index out of a in memory index
|
|
*/
|
|
EXTERN_C LONG IndexGetFileIndex(PAVIINDEX px, LONG l, LONG cnt, PAVIINDEXENTRY pidx, LONG lAdjust)
|
|
{
|
|
LONG lx;
|
|
DWORD ckid;
|
|
UINT stream;
|
|
DWORD offset;
|
|
DWORD length;
|
|
UINT flags;
|
|
DWORD dwFlags;
|
|
|
|
Assert(pidx);
|
|
Assert(px);
|
|
|
|
if (pidx == NULL || px == NULL)
|
|
return NULL;
|
|
|
|
Assert(sizeof(AVIINDEXENTRY) > sizeof(AVIIDX));
|
|
|
|
for (lx = l; lx < px->nIndex && lx < l + cnt; lx++) {
|
|
// adjust the offset to be relative
|
|
offset = IndexOffset(px, lx) + lAdjust;
|
|
length = IndexLength(px, lx);
|
|
stream = IndexStream(px, lx);
|
|
flags = IndexFlags(px, lx);
|
|
|
|
if (length == MAX_LENGTH - 1) {
|
|
}
|
|
|
|
ckid = MAKEAVICKID(0, stream);
|
|
dwFlags = 0;
|
|
|
|
// set the flags, there are only a few flags in file index's
|
|
// AVIIF_KEYFRAME, AVIIF_LIST, AVIIF_NOTIME
|
|
|
|
if (flags & IDX_KEY)
|
|
dwFlags |= AVIIF_KEYFRAME;
|
|
|
|
if (flags & IDX_PAL)
|
|
dwFlags |= AVIIF_NOTIME;
|
|
|
|
if (stream == STREAM_REC)
|
|
dwFlags |= AVIIF_LIST;
|
|
|
|
// now figure out the ckid
|
|
|
|
if (stream == STREAM_REC)
|
|
ckid = listtypeAVIRECORD;
|
|
else if ((flags & (IDX_KEY | IDX_NONKEY)) == (IDX_KEY | IDX_NONKEY))
|
|
ckid |= MAKELONG(0, aviTWOCC('w', 'b'));
|
|
else if (flags & IDX_PAL)
|
|
ckid |= MAKELONG(0, aviTWOCC('p', 'c'));
|
|
else if (flags & IDX_HALF)
|
|
ckid |= MAKELONG(0, aviTWOCC('d', 'x'));
|
|
else if (flags & IDX_KEY)
|
|
ckid |= MAKELONG(0, aviTWOCC('d', 'b'));
|
|
else
|
|
ckid |= MAKELONG(0, aviTWOCC('d', 'c'));
|
|
|
|
// set the info
|
|
pidx->dwChunkOffset = offset;
|
|
pidx->dwChunkLength = length;
|
|
pidx->dwFlags = dwFlags;
|
|
pidx->ckid = ckid;
|
|
|
|
pidx++;
|
|
}
|
|
|
|
return lx - l; // return count copied
|
|
}
|
|
|
|
/*
|
|
* @doc INTERNAL
|
|
* @api PAVIINDEX | IndexCreate | make a index.
|
|
*/
|
|
EXTERN_C PAVIINDEX IndexCreate(void)
|
|
{
|
|
PAVIINDEX px;
|
|
|
|
px = (PAVIINDEX)GlobalAllocPtr(GHND | GMEM_SHARE, sizeof(AVIINDEX) + INDEXALLOC * sizeof(AVIIDX));
|
|
if (px == NULL)
|
|
return NULL;
|
|
|
|
px->nIndex = 0;// index size
|
|
px->nIndexSize = INDEXALLOC; // allocated size
|
|
|
|
return px;
|
|
}
|
|
|
|
#endif // AVIIDX_READONLY
|
|
|
|
/*
|
|
* @doc INTERNAL MCIAVI
|
|
* @api LONG | IndexFirst | returns the first index entry for a stream
|
|
* @rdesc returns the first index entry, -1 for error
|
|
*/
|
|
EXTERN_C LONG IndexFirst(PAVIINDEX px, UINT stream)
|
|
{
|
|
LONG l;
|
|
|
|
Assert(px);
|
|
|
|
for (l = 0; l < px->nIndex; l++) {
|
|
if (IndexStream(px, l) == stream)
|
|
return l;
|
|
}
|
|
|
|
return ERR_IDX;
|
|
}
|
|
|
|
/*
|
|
* @doc INTERNAL MCIAVI
|
|
* @api LONG | IndexNext | go forward in a index
|
|
*/
|
|
EXTERN_C LONG IndexNext(PAVIINDEX px, LONG l, UINT f)
|
|
{
|
|
WORD bStream;
|
|
|
|
Assert(px);
|
|
|
|
if (l < 0 || l >= px->nIndex)
|
|
return ERR_IDX;
|
|
|
|
bStream = IndexStream(px, l);
|
|
|
|
for (l++; l < px->nIndex; l++) {
|
|
if (IndexStream(px, l) != bStream)
|
|
continue;
|
|
|
|
if (!f || (IndexFlags(px, l) & f))
|
|
return l;
|
|
}
|
|
|
|
return ERR_IDX;
|
|
}
|
|
|
|
/*
|
|
* @doc INTERNAL MCIAVI
|
|
* @api LONG | IndexPrev | step backward in a stream
|
|
*/
|
|
EXTERN_C LONG IndexPrev(PAVIINDEX px, LONG l, UINT f)
|
|
{
|
|
WORD bStream;
|
|
|
|
Assert(px);
|
|
|
|
if (l < 0 || l >= px->nIndex)
|
|
return ERR_IDX;
|
|
|
|
bStream = IndexStream(px, l);
|
|
|
|
for (l--; l >= 0; l--) {
|
|
if (IndexStream(px, l) != bStream)
|
|
continue;
|
|
|
|
if (!f || (IndexFlags(px, l) & f))
|
|
return l;
|
|
}
|
|
|
|
return ERR_IDX;
|
|
}
|
|
|
|
|
|
INLINE BOOL StreamNext(PSTREAMINDEX psx, LONG FAR& l, LONG FAR& lPos, UINT flags)
|
|
{
|
|
BYTE bStream = (BYTE)psx->stream;
|
|
LONG lSampleSize = psx->lSampleSize;
|
|
LONG lSave = l;
|
|
LONG lPosSave = lPos;
|
|
PAVIINDEX px = psx->px;
|
|
|
|
Assert(px && l >= 0 && l < px->nIndex);
|
|
|
|
if (lSampleSize == 0) {
|
|
lPos += 1;
|
|
l++;
|
|
|
|
for (; l < px->nIndex; l++) {
|
|
if (IndexStream(px, l) != bStream)
|
|
continue;
|
|
|
|
if (!flags || (IndexFlags(px, l) & flags))
|
|
return TRUE;
|
|
|
|
if (!(IndexFlags(px, l) & IDX_NOTIME))
|
|
lPos += 1;
|
|
}
|
|
} else {
|
|
lPos += IndexLength(px, l) / lSampleSize;
|
|
l++;
|
|
|
|
for (; l < px->nIndex; l++) {
|
|
if (IndexStream(px, l) != bStream)
|
|
continue;
|
|
|
|
if (!flags || (IndexFlags(px, l) & flags))
|
|
return TRUE;
|
|
|
|
lPos += IndexLength(px, l) / lSampleSize;
|
|
}
|
|
}
|
|
|
|
lPos = lPosSave;
|
|
l = lSave;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
INLINE BOOL StreamPrev(PSTREAMINDEX psx, LONG FAR& l, LONG FAR& lPos, UINT flags)
|
|
{
|
|
BYTE bStream = (BYTE)psx->stream;
|
|
LONG lSampleSize = psx->lSampleSize;
|
|
LONG lSave = l;
|
|
LONG lPosSave = lPos;
|
|
PAVIINDEX px = psx->px;
|
|
|
|
Assert(px && l >= 0 && l < px->nIndex);
|
|
|
|
if (lSampleSize == 0) {
|
|
for (l--; l >= 0; l--) {
|
|
if (IndexStream(px, l) != bStream)
|
|
continue;
|
|
|
|
if (!(IndexFlags(px, l) & IDX_NOTIME))
|
|
lPos -= 1;
|
|
|
|
if (!flags || (IndexFlags(px, l) & flags))
|
|
return TRUE;
|
|
}
|
|
} else {
|
|
for (l--; l >= 0; l--) {
|
|
if (IndexStream(px, l) != bStream)
|
|
continue;
|
|
|
|
lPos -= IndexLength(px, l) / lSampleSize;
|
|
if (!flags || (IndexFlags(px, l) & flags))
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
lPos = lPosSave;
|
|
l = lSave;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static LONG SearchIndex(PSTREAMINDEX psx, LONG lPos, UINT uFlags, IDXPOS FAR* pos)
|
|
{
|
|
LONG l;
|
|
LONG lScan;
|
|
LONG lFound;
|
|
LONG lFoundPos;
|
|
LONG lLen;
|
|
UINT flags;
|
|
PAVIINDEX px = psx->px;
|
|
|
|
Assert(psx);
|
|
Assert(psx->px);
|
|
|
|
if (psx == NULL)
|
|
return ERR_POS;
|
|
|
|
if (lPos < psx->lStart)
|
|
return ERR_POS;
|
|
|
|
if (lPos >= psx->lEnd)
|
|
return ERR_POS;
|
|
|
|
// figure out where to start in the index.
|
|
|
|
if (psx->lx != -1) {
|
|
lScan = psx->lPos;
|
|
l = psx->lx;
|
|
} else {
|
|
DPF3("Starting index search at begining\n");
|
|
lScan = psx->lStart;
|
|
|
|
for (l = 0; l < px->nIndex; l++)
|
|
if (IndexStream(px, l) == (UINT)psx->stream)
|
|
break;
|
|
}
|
|
|
|
Assert(l >= 0 && l < px->nIndex);
|
|
Assert(IndexStream(px, l) == psx->stream);
|
|
|
|
#ifdef DEBUG
|
|
if (!(uFlags & FIND_DIR))
|
|
uFlags |= FIND_PREV;
|
|
|
|
switch (uFlags & (FIND_TYPE | FIND_DIR)) {
|
|
case FIND_NEXT | FIND_KEY: DPF3("SearchIndex(%d): %ld next key, start=%ld", psx->stream, lPos, lScan); break;
|
|
case FIND_NEXT | FIND_ANY: DPF3("SearchIndex(%d): %ld next any, start=%ld", psx->stream, lPos, lScan); break;
|
|
case FIND_NEXT | FIND_FORMAT: DPF3("SearchIndex(%d): %ld next fmt, start=%ld", psx->stream, lPos, lScan); break;
|
|
case FIND_NEXT: DPF3("SearchIndex(%d): %ld next , start=%ld", psx->stream, lPos, lScan); break;
|
|
case FIND_PREV | FIND_KEY: DPF3("SearchIndex(%d): %ld prev key, start=%ld", psx->stream, lPos, lScan); break;
|
|
case FIND_PREV | FIND_ANY: DPF3("SearchIndex(%d): %ld prev any, start=%ld", psx->stream, lPos, lScan); break;
|
|
case FIND_PREV | FIND_FORMAT: DPF3("SearchIndex(%d): %ld prev fmt, start=%ld", psx->stream, lPos, lScan); break;
|
|
case FIND_PREV: DPF3("SearchIndex(%d): %ld prev , start=%ld", psx->stream, lPos, lScan); break;
|
|
}
|
|
|
|
LONG time = timeGetTime();
|
|
#endif
|
|
|
|
lLen = psx->lSampleSize == 0 ? 1 : IndexLength(px, l) / psx->lSampleSize;
|
|
|
|
if (lScan + lLen <= lPos) {
|
|
// search forward for this position
|
|
while (lScan <= lPos) {
|
|
lFound = l;
|
|
lFoundPos = lScan;
|
|
|
|
if (lScan == lPos)
|
|
break;
|
|
|
|
if (!StreamNext(psx, l, lScan, IDX_KEY | IDX_NONKEY))
|
|
break;
|
|
}
|
|
|
|
if ((lScan > lPos) && !(uFlags & FIND_NEXT)) {
|
|
lScan = lFoundPos;
|
|
l = lFound;
|
|
}
|
|
} else if (lScan > lPos) {
|
|
// search backward for this position
|
|
while (lScan >= lPos) {
|
|
lFound = l;
|
|
lFoundPos = lScan;
|
|
|
|
if (lScan == lPos)
|
|
break;
|
|
|
|
if (!StreamPrev(psx, l, lScan, IDX_KEY | IDX_NONKEY))
|
|
break;
|
|
}
|
|
|
|
if (uFlags & FIND_NEXT) {
|
|
lScan = lFoundPos;
|
|
l = lFound;
|
|
}
|
|
} else {
|
|
Assert(lScan <= lPos && lPos < lScan + lLen);
|
|
}
|
|
|
|
Assert(l >= 0 && l < px->nIndex);
|
|
Assert(IndexStream(px, l) == psx->stream);
|
|
|
|
// cache what we found.
|
|
|
|
psx->lx = l;
|
|
psx->lPos = lScan;
|
|
|
|
if (uFlags & FIND_TYPE) {
|
|
switch (uFlags & FIND_TYPE) {
|
|
case FIND_ANY: flags = IDX_KEY | IDX_NONKEY; break;
|
|
case FIND_FORMAT: flags = IDX_PAL; break;
|
|
case FIND_KEY: flags = IDX_KEY; break;
|
|
}
|
|
|
|
if (!(IndexFlags(px, l) & flags)) {
|
|
if (!(uFlags & FIND_NEXT)) {
|
|
if (!StreamPrev(psx, l, lScan, flags)) {
|
|
DPF3("!, EOI, time = %ld\n", timeGetTime() - time);
|
|
return ERR_POS;
|
|
}
|
|
} else {
|
|
if (!StreamNext(psx, l, lScan, flags)) {
|
|
DPF3("!, EOI, time = %ld\n", timeGetTime() - time);
|
|
return ERR_POS;
|
|
}
|
|
}
|
|
}
|
|
|
|
Assert(l >= 0 && l < px->nIndex);
|
|
Assert(IndexStream(px, l) == psx->stream);
|
|
Assert(IndexFlags(px, l) & flags);
|
|
}
|
|
|
|
Assert(lScan >= psx->lStart && lScan < psx->lEnd);
|
|
|
|
DPF3("!, found %ld, time = %ld\n", lScan, timeGetTime() - time);
|
|
|
|
if (pos == NULL)
|
|
return lScan;
|
|
|
|
if (psx->lSampleSize != 0) {
|
|
lLen = IndexLength(px, l);
|
|
if (lLen == MAX_LENGTH - 1)
|
|
lLen = 0x7FFFFFFF;
|
|
|
|
if (psx->lSampleSize > 1)
|
|
lLen /= psx->lSampleSize;
|
|
} else {
|
|
lLen = 1;
|
|
}
|
|
|
|
pos->lx = l;
|
|
pos->lPos = lScan;
|
|
pos->lSize = lLen;
|
|
pos->lOffset = IndexOffset(px, l);
|
|
pos->lLength = IndexLength(px, l);
|
|
|
|
// if the FIND_TYPE is not one of FIND_ANY, FIND_KEY, FIND_FORMAT
|
|
// make sure we realy found the wanted sample.
|
|
|
|
if ((uFlags & FIND_TYPE) == 0) {
|
|
if (lPos < lScan || lPos >= lScan + lLen) {
|
|
pos->lOffset = -1;
|
|
pos->lLength = 0;
|
|
pos->lSize = 0;
|
|
pos->lPos = lPos;
|
|
} else if (psx->lSampleSize > 0) {
|
|
pos->lOffset += (lPos - lScan) * psx->lSampleSize;
|
|
pos->lLength -= (lPos - lScan) * psx->lSampleSize;
|
|
pos->lSize -= (lPos - lScan);
|
|
pos->lPos = lPos;
|
|
}
|
|
}
|
|
|
|
return pos->lPos;
|
|
}
|
|
|
|
/*
|
|
* @doc INTERNAL MCIAVI
|
|
* @api LONG | FindSample | find a sample in a stream
|
|
*/
|
|
EXTERN_C LONG StreamFindSample(PSTREAMINDEX psx, LONG lPos, UINT uFlags)
|
|
{
|
|
Assert(psx);
|
|
Assert(psx->px);
|
|
|
|
if (lPos < psx->lStart)
|
|
return ERR_POS;
|
|
|
|
if (lPos >= psx->lEnd)
|
|
return ERR_POS;
|
|
|
|
if ((uFlags & FIND_RET) == FIND_POS) {
|
|
switch (uFlags & FIND_TYPE) {
|
|
case FIND_FORMAT:
|
|
if (psx->lPalFrames == 0) {
|
|
if ((uFlags & FIND_NEXT) && lPos > psx->lStart)
|
|
return ERR_POS;
|
|
else
|
|
return psx->lStart;
|
|
}
|
|
break;
|
|
case FIND_ANY:
|
|
if (psx->lNulFrames == 0) {
|
|
return lPos;
|
|
}
|
|
break;
|
|
case FIND_KEY:
|
|
if (psx->lKeyFrames == psx->lFrames) {
|
|
return lPos;
|
|
}
|
|
break;
|
|
default:
|
|
return lPos;
|
|
}
|
|
|
|
return SearchIndex(psx, lPos, uFlags, NULL);
|
|
} else {
|
|
IDXPOS pos;
|
|
|
|
if (SearchIndex(psx, lPos, uFlags, &pos) == ERR_POS)
|
|
return ERR_POS;
|
|
|
|
switch (uFlags & FIND_RET) {
|
|
case FIND_POS:
|
|
return pos.lPos;
|
|
case FIND_OFFSET:
|
|
return pos.lOffset + 8;
|
|
case FIND_LENGTH:
|
|
return pos.lLength;
|
|
case FIND_SIZE:
|
|
return pos.lSize;
|
|
case FIND_INDEX:
|
|
return pos.lx;
|
|
}
|
|
}
|
|
|
|
return ERR_POS;
|
|
}
|
|
|
|
/*
|
|
* @doc INTERNAL MCIAVI
|
|
* @api LONG | StreamRead | read from a stream
|
|
*/
|
|
EXTERN_C LONG StreamRead(PSTREAMINDEX psx, LONG lStart, LONG lSamples, LPVOID lpBuffer, LONG cbBuffer)
|
|
{
|
|
LONG lBytes;
|
|
LONG lSampleSize;
|
|
LONG lSeek;
|
|
LONG lRead;
|
|
IDXPOS pos;
|
|
|
|
Assert(psx);
|
|
Assert(psx->px);
|
|
Assert(psx->hFile);
|
|
Assert(psx->Read);
|
|
|
|
if (lStart < psx->lStart)
|
|
return -1;
|
|
|
|
if (lStart >= psx->lEnd)
|
|
return -1;
|
|
|
|
//DPF("%cst: %d : %d\n", psx->stream ? '\t':' ', psx->stream, lStart);
|
|
|
|
// find nearest chunk
|
|
if (SearchIndex(psx, lStart, FIND_PREV, &pos) == ERR_POS)
|
|
return -1;
|
|
|
|
// only continue if the sample we want is in here.
|
|
if (lStart < pos.lPos || lStart >= pos.lPos + pos.lSize)
|
|
return 0;
|
|
|
|
// if they give us a NULL buffer dummy up the cbBuffer so we return
|
|
// what we would have read if we had enough room
|
|
if (lpBuffer == NULL && cbBuffer == 0 && lSamples != 0)
|
|
cbBuffer = 0x7FFFFFFF;
|
|
|
|
if (lSampleSize = psx->lSampleSize) {
|
|
// If they wanted to read/write only a "convenient amount",
|
|
// pretend the buffer is only large enough to hold the rest of this chunk.
|
|
if (lSamples == -1l)
|
|
cbBuffer = min(cbBuffer, pos.lLength);
|
|
|
|
/* Fixed-length samples, if lSamples is zero, just fill the buffer. */
|
|
if (lSamples > 0)
|
|
lSamples = min(lSamples, cbBuffer / lSampleSize);
|
|
else
|
|
lSamples = cbBuffer / lSampleSize;
|
|
|
|
lBytes = lSamples * lSampleSize;
|
|
} else {
|
|
lBytes = pos.lLength;
|
|
}
|
|
|
|
if (lpBuffer == NULL)
|
|
return lBytes;
|
|
|
|
if (cbBuffer < lBytes)
|
|
return -1; // buffer is too small
|
|
|
|
#define WORDALIGN(x) ((x) + ((x) & 1))
|
|
|
|
if (lSampleSize == 0) {
|
|
DWORD adw[2];
|
|
psx->Read(psx->hFile, pos.lOffset, sizeof(adw), adw);
|
|
if (StreamFromFOURCC(adw[0]) != psx->stream) {
|
|
Assert(0);
|
|
} else {
|
|
Assert(WORDALIGN(adw[1]) == WORDALIGN((DWORD)pos.lLength));
|
|
pos.lLength = adw[1]; // !!! Make netware video work!
|
|
lBytes = pos.lLength;
|
|
}
|
|
} else {
|
|
#ifdef DEBUG
|
|
IDXPOS x;
|
|
DWORD adw[2];
|
|
SearchIndex(psx, lStart, FIND_PREV | FIND_ANY, &x);
|
|
psx->Read(psx->hFile, x.lOffset, sizeof(adw), adw);
|
|
Assert(StreamFromFOURCC(adw[0]) == psx->stream);
|
|
Assert(WORDALIGN(adw[1]) == WORDALIGN((DWORD)x.lLength));
|
|
#endif
|
|
}
|
|
|
|
cbBuffer = lBytes;
|
|
lBytes = 0;
|
|
|
|
while (cbBuffer > 0) {
|
|
lSeek = pos.lOffset + 8;
|
|
lRead = min(pos.lLength, cbBuffer);
|
|
|
|
if (lRead <= 0) {
|
|
DPF3("!!!! lRead <= 0 in AVIStreamRead\n");
|
|
break;
|
|
}
|
|
|
|
DPF3("StreamRead: %ld bytes @%ld\n", lRead, lSeek);
|
|
|
|
if (psx->Read(psx->hFile, lSeek, lRead, lpBuffer) != lRead)
|
|
return -1;
|
|
|
|
lBytes += lRead;
|
|
cbBuffer -= lRead;
|
|
|
|
if (cbBuffer > 0) {
|
|
if (lSampleSize == 0) {
|
|
DPF("%ld bytes to read, but sample size is 0!\n", cbBuffer);
|
|
break;
|
|
}
|
|
|
|
lpBuffer = (LPVOID)(((BYTE _huge*)lpBuffer) + lRead);
|
|
|
|
lStart += lRead / lSampleSize;
|
|
lStart = SearchIndex(psx, lStart, FIND_PREV, &pos);
|
|
if (lStart == ERR_POS)
|
|
break;
|
|
}
|
|
}
|
|
|
|
return lBytes;// success return number of bytes read
|
|
}
|
|
|
|
/*
|
|
* @doc INTERNAL MCIAVI
|
|
* @api LONG | StreamWrite | write to a stream
|
|
*/
|
|
EXTERN_C LONG StreamWrite(
|
|
PSTREAMINDEX psx,
|
|
LONG lStart,
|
|
LONG lSamples,
|
|
LPVOID lpBuffer,
|
|
LONG cbBuffer)
|
|
{
|
|
return -1;
|
|
}
|