📄 avifile.c
字号:
/* convert MainAVIHeader into AVIFILINFOW */
memset(&This->fInfo, 0, sizeof(This->fInfo));
This->fInfo.dwRate = MainAVIHdr.dwMicroSecPerFrame;
This->fInfo.dwScale = 1000000;
This->fInfo.dwMaxBytesPerSec = MainAVIHdr.dwMaxBytesPerSec;
This->fInfo.dwFlags = MainAVIHdr.dwFlags;
This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
This->fInfo.dwLength = MainAVIHdr.dwTotalFrames;
This->fInfo.dwStreams = MainAVIHdr.dwStreams;
This->fInfo.dwSuggestedBufferSize = MainAVIHdr.dwSuggestedBufferSize;
This->fInfo.dwWidth = MainAVIHdr.dwWidth;
This->fInfo.dwHeight = MainAVIHdr.dwHeight;
LoadStringW(AVIFILE_hModule, IDS_AVIFILETYPE, This->fInfo.szFileType,
sizeof(This->fInfo.szFileType));
/* go back to into header list */
if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
return AVIERR_FILEREAD;
/* foreach stream exists a "LIST","strl" chunk */
for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
/* get next nested chunk in this "LIST","strl" */
if (mmioDescend(This->hmmio, &ckLIST2, &ckLIST1, 0) != S_OK)
return AVIERR_FILEREAD;
/* nested chunk must be of type "LIST","strl" -- when not normally JUNK */
if (ckLIST2.ckid == FOURCC_LIST &&
ckLIST2.fccType == listtypeSTREAMHEADER) {
pStream = This->ppStreams[nStream] =
HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIStreamImpl));
if (pStream == NULL)
return AVIERR_MEMORY;
AVIFILE_ConstructAVIStream(This, nStream, NULL);
ck.ckid = 0;
while (mmioDescend(This->hmmio, &ck, &ckLIST2, 0) == S_OK) {
switch (ck.ckid) {
case ckidSTREAMHANDLERDATA:
if (pStream->lpHandlerData != NULL)
return AVIERR_BADFORMAT;
pStream->lpHandlerData = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
if (pStream->lpHandlerData == NULL)
return AVIERR_MEMORY;
pStream->cbHandlerData = ck.cksize;
if (mmioRead(This->hmmio, (HPSTR)pStream->lpHandlerData, ck.cksize) != ck.cksize)
return AVIERR_FILEREAD;
break;
case ckidSTREAMFORMAT:
if (pStream->lpFormat != NULL)
return AVIERR_BADFORMAT;
if (ck.cksize == 0)
break;
pStream->lpFormat = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
if (pStream->lpFormat == NULL)
return AVIERR_MEMORY;
pStream->cbFormat = ck.cksize;
if (mmioRead(This->hmmio, (HPSTR)pStream->lpFormat, ck.cksize) != ck.cksize)
return AVIERR_FILEREAD;
if (pStream->sInfo.fccType == streamtypeVIDEO) {
LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)pStream->lpFormat;
/* some corrections to the video format */
if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
lpbi->biClrUsed = 1u << lpbi->biBitCount;
if (lpbi->biCompression == BI_RGB && lpbi->biSizeImage == 0)
lpbi->biSizeImage = DIBWIDTHBYTES(*lpbi) * lpbi->biHeight;
if (lpbi->biCompression != BI_RGB && lpbi->biBitCount == 8) {
if (pStream->sInfo.fccHandler == mmioFOURCC('R','L','E','0') ||
pStream->sInfo.fccHandler == mmioFOURCC('R','L','E',' '))
lpbi->biCompression = BI_RLE8;
}
if (lpbi->biCompression == BI_RGB &&
(pStream->sInfo.fccHandler == 0 ||
pStream->sInfo.fccHandler == mmioFOURCC('N','O','N','E')))
pStream->sInfo.fccHandler = comptypeDIB;
/* init rcFrame if it's empty */
if (IsRectEmpty(&pStream->sInfo.rcFrame))
SetRect(&pStream->sInfo.rcFrame, 0, 0, lpbi->biWidth, lpbi->biHeight);
}
break;
case ckidSTREAMHEADER:
{
static const WCHAR streamTypeFmt[] = {'%','4','.','4','h','s',0};
AVIStreamHeader streamHdr;
WCHAR szType[25];
WCHAR streamNameFmt[25];
UINT count;
LONG n = ck.cksize;
if (ck.cksize > sizeof(streamHdr))
n = sizeof(streamHdr);
if (mmioRead(This->hmmio, (HPSTR)&streamHdr, n) != n)
return AVIERR_FILEREAD;
pStream->sInfo.fccType = streamHdr.fccType;
pStream->sInfo.fccHandler = streamHdr.fccHandler;
pStream->sInfo.dwFlags = streamHdr.dwFlags;
pStream->sInfo.wPriority = streamHdr.wPriority;
pStream->sInfo.wLanguage = streamHdr.wLanguage;
pStream->sInfo.dwInitialFrames = streamHdr.dwInitialFrames;
pStream->sInfo.dwScale = streamHdr.dwScale;
pStream->sInfo.dwRate = streamHdr.dwRate;
pStream->sInfo.dwStart = streamHdr.dwStart;
pStream->sInfo.dwLength = streamHdr.dwLength;
pStream->sInfo.dwSuggestedBufferSize =
streamHdr.dwSuggestedBufferSize;
pStream->sInfo.dwQuality = streamHdr.dwQuality;
pStream->sInfo.dwSampleSize = streamHdr.dwSampleSize;
pStream->sInfo.rcFrame.left = streamHdr.rcFrame.left;
pStream->sInfo.rcFrame.top = streamHdr.rcFrame.top;
pStream->sInfo.rcFrame.right = streamHdr.rcFrame.right;
pStream->sInfo.rcFrame.bottom = streamHdr.rcFrame.bottom;
pStream->sInfo.dwEditCount = 0;
pStream->sInfo.dwFormatChangeCount = 0;
/* generate description for stream like "filename.avi Type #n" */
if (streamHdr.fccType == streamtypeVIDEO)
LoadStringW(AVIFILE_hModule, IDS_VIDEO, szType, sizeof(szType));
else if (streamHdr.fccType == streamtypeAUDIO)
LoadStringW(AVIFILE_hModule, IDS_AUDIO, szType, sizeof(szType));
else
wsprintfW(szType, streamTypeFmt, (char*)&streamHdr.fccType);
/* get count of this streamtype up to this stream */
count = 0;
for (n = nStream; 0 <= n; n--) {
if (This->ppStreams[n]->sInfo.fccHandler == streamHdr.fccType)
count++;
}
memset(pStream->sInfo.szName, 0, sizeof(pStream->sInfo.szName));
LoadStringW(AVIFILE_hModule, IDS_AVISTREAMFORMAT, streamNameFmt, sizeof(streamNameFmt));
/* FIXME: avoid overflow -- better use wsnprintfW, which doesn't exists ! */
wsprintfW(pStream->sInfo.szName, streamNameFmt,
AVIFILE_BasenameW(This->szFileName), szType, count);
}
break;
case ckidSTREAMNAME:
{ /* streamname will be saved as ASCII string */
LPSTR str = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
if (str == NULL)
return AVIERR_MEMORY;
if (mmioRead(This->hmmio, (HPSTR)str, ck.cksize) != ck.cksize)
{
HeapFree(GetProcessHeap(), 0, str);
return AVIERR_FILEREAD;
}
MultiByteToWideChar(CP_ACP, 0, str, -1, pStream->sInfo.szName,
sizeof(pStream->sInfo.szName)/sizeof(pStream->sInfo.szName[0]));
HeapFree(GetProcessHeap(), 0, str);
}
break;
case ckidAVIPADDING:
case mmioFOURCC('p','a','d','d'):
break;
default:
WARN(": found extra chunk 0x%08X\n", ck.ckid);
hr = ReadChunkIntoExtra(&pStream->extra, This->hmmio, &ck);
if (FAILED(hr))
return hr;
};
if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
return AVIERR_FILEREAD;
}
} else {
/* nested chunks in "LIST","hdrl" which are not of type "LIST","strl" */
hr = ReadChunkIntoExtra(&This->fileextra, This->hmmio, &ckLIST2);
if (FAILED(hr))
return hr;
}
if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
return AVIERR_FILEREAD;
}
/* read any extra headers in "LIST","hdrl" */
FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, 0);
if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
return AVIERR_FILEREAD;
/* search "LIST","movi" chunk in "RIFF","AVI " */
ckLIST1.fccType = listtypeAVIMOVIE;
hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF,
MMIO_FINDLIST);
if (FAILED(hr))
return hr;
This->dwMoviChunkPos = ckLIST1.dwDataOffset;
This->dwIdxChunkPos = ckLIST1.cksize + ckLIST1.dwDataOffset;
if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
return AVIERR_FILEREAD;
/* try to find an index */
ck.ckid = ckidAVINEWINDEX;
hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio,
&ck, &ckRIFF, MMIO_FINDCHUNK);
if (SUCCEEDED(hr) && ck.cksize > 0) {
if (FAILED(AVIFILE_LoadIndex(This, ck.cksize, ckLIST1.dwDataOffset)))
This->fInfo.dwFlags &= ~AVIFILEINFO_HASINDEX;
}
/* when we haven't found an index or it's bad, then build one
* by parsing 'movi' chunk */
if ((This->fInfo.dwFlags & AVIFILEINFO_HASINDEX) == 0) {
for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++)
This->ppStreams[nStream]->lLastFrame = -1;
if (mmioSeek(This->hmmio, ckLIST1.dwDataOffset + sizeof(DWORD), SEEK_SET) == -1) {
ERR(": Oops, can't seek back to 'movi' chunk!\n");
return AVIERR_FILEREAD;
}
/* seek through the 'movi' list until end */
while (mmioDescend(This->hmmio, &ck, &ckLIST1, 0) == S_OK) {
if (ck.ckid != FOURCC_LIST) {
if (mmioAscend(This->hmmio, &ck, 0) == S_OK) {
nStream = StreamFromFOURCC(ck.ckid);
if (nStream > This->fInfo.dwStreams)
return AVIERR_BADFORMAT;
AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
ck.dwDataOffset - 2 * sizeof(DWORD), 0);
} else {
nStream = StreamFromFOURCC(ck.ckid);
WARN(": file seems to be truncated!\n");
if (nStream <= This->fInfo.dwStreams &&
This->ppStreams[nStream]->sInfo.dwSampleSize > 0) {
ck.cksize = mmioSeek(This->hmmio, 0, SEEK_END);
if (ck.cksize != -1) {
ck.cksize -= ck.dwDataOffset;
ck.cksize &= ~(This->ppStreams[nStream]->sInfo.dwSampleSize - 1);
AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
ck.dwDataOffset - 2 * sizeof(DWORD), 0);
}
}
}
}
}
}
/* find other chunks */
FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckRIFF, 0);
return AVIERR_OK;
}
static HRESULT AVIFILE_LoadIndex(IAVIFileImpl *This, DWORD size, DWORD offset)
{
AVIINDEXENTRY *lp;
DWORD pos, n;
HRESULT hr = AVIERR_OK;
BOOL bAbsolute = TRUE;
lp = HeapAlloc(GetProcessHeap(), 0, IDX_PER_BLOCK * sizeof(AVIINDEXENTRY));
if (lp == NULL)
return AVIERR_MEMORY;
/* adjust limits for index tables, so that inserting will be faster */
for (n = 0; n < This->fInfo.dwStreams; n++) {
IAVIStreamImpl *pStream = This->ppStreams[n];
pStream->lLastFrame = -1;
if (pStream->idxFrames != NULL) {
HeapFree(GetProcessHeap(), 0, pStream->idxFrames);
pStream->idxFrames = NULL;
pStream->nIdxFrames = 0;
}
if (pStream->sInfo.dwSampleSize != 0) {
if (n > 0 && This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
pStream->nIdxFrames = This->ppStreams[0]->nIdxFrames;
} else if (pStream->sInfo.dwSuggestedBufferSize) {
pStream->nIdxFrames =
pStream->sInfo.dwLength / pStream->sInfo.dwSuggestedBufferSize;
}
} else
pStream->nIdxFrames = pStream->sInfo.dwLength;
pStream->idxFrames =
HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pStream->nIdxFrames * sizeof(AVIINDEXENTRY));
if (pStream->idxFrames == NULL && pStream->nIdxFrames > 0) {
pStream->nIdxFrames = 0;
return AVIERR_MEMORY;
}
}
pos = (DWORD)-1;
while (size != 0) {
LONG read = min(IDX_PER_BLOCK * sizeof(AVIINDEXENTRY), size);
if (mmioRead(This->hmmio, (HPSTR)lp, read) != read) {
hr = AVIERR_FILEREAD;
break;
}
size -= read;
if (pos == (DWORD)-1)
pos = offset - lp->dwChunkOffset + sizeof(DWORD);
AVIFILE_ParseIndex(This, lp, read / sizeof(AVIINDEXENTRY),
pos, &bAbsolute);
}
HeapFree(GetProcessHeap(), 0, lp);
/* checking ... */
for (n = 0; n < This->fInfo.dwStreams; n++) {
IAVIStreamImpl *pStream = This->ppStreams[n];
if (pStream->sInfo.dwSampleSize == 0 &&
pStream->sInfo.dwLength != pStream->lLastFrame+1)
ERR("stream %u length mismatch: dwLength=%u found=%d\n",
n, pStream->sInfo.dwLength, pStream->lLastFrame);
}
return hr;
}
static HRESULT AVIFILE_ParseIndex(IAVIFileImpl *This, AVIINDEXENTRY *lp,
LONG count, DWORD pos, BOOL *bAbsolute)
{
if (lp == NULL)
return AVIERR_BADPARAM;
for (; count > 0; count--, lp++) {
WORD nStream = StreamFromFOURCC(lp->ckid);
if (lp->ckid == listtypeAVIRECORD || nStream == 0x7F)
continue; /* skip these */
if (nStream > This->fInfo.dwStreams)
return AVIERR_BADFORMAT;
if (*bAbsolute && lp->dwChunkOffset < This->dwMoviChunkPos)
*bAbsolute = FALSE;
if (*bAbsolute)
lp->dwChunkOffset += sizeof(DWORD);
else
lp->dwChunkOffset += pos;
if (FAILED(AVIFILE_AddFrame(This->ppStreams[nStream], lp->ckid, lp->dwChunkLength, lp->dwChunkOffset, lp->dwFlags)))
return AVIERR_MEMORY;
}
return AVIERR_OK;
}
static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD pos,
LPVOID buffer, LONG size)
{
/* pre-conditions */
assert(This != NULL);
assert(This->paf != NULL);
assert(This->paf->hmmio != NULL);
assert(This->sInfo.dwStart <= pos && pos < This->sInfo.dwLength);
assert(pos <= This->lLastFrame);
/* should we read as much as block gives us? */
if (size == 0 || size > This->idxFrames[pos].dwChunkLength)
size = This->idxFrames[pos].dwChunkLength;
/* read into out own buffer or given one? */
if (buffer == NULL) {
/* we also read the chunk */
size += 2 * sizeof(DWORD);
/* check that buffer is big enough -- don't trust dwSuggestedBufferSize */
if (This->lpBuffer == NULL || size < This->cbBuffer) {
DWORD maxSize = max(size, This->sInfo.dwSuggestedBufferSize);
if (This->lpBuffer == NULL)
This->lpBuffer = HeapAlloc(GetProcessHeap(), 0, maxSize);
else
This->lpBuffer = HeapReAlloc(GetProcessHeap(), 0, This->lpBuffer, maxSize);
if (This->lpBuffer == NULL)
return AVIERR_MEMORY;
This->cbBuffer = max(size, This->sInfo.dwSuggestedBufferSize);
}
/* now read the complete chunk into our buffer */
if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset, SEEK_SET) == -1)
return AVIERR_FILEREAD;
if (mmioRead(This->paf->hmmio, (HPSTR)This->lpBuffer, size) != size)
return AVIERR_FILEREAD;
/* check if it was the correct block which we have read */
if (This->lpBuffer[0] != This->idxFrames[pos].ckid ||
This->lpBuffer[1] != This->idxFrames[pos].dwChunkLength) {
ERR(": block %d not found at 0x%08X\n", pos, This->idxFrames[pos].dwChunkOffset);
ERR(": Index says: '%4.4s'(0x%08X) size 0x%08X\n",
(char*)&This->idxFrames[pos].ckid, This->idxFrames[pos].ckid,
This->idxFrames[pos].dwChunk
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -