diff --git a/dlls/avifil32/api.c b/dlls/avifil32/api.c index 8ad311a9294..02b58ef621b 100644 --- a/dlls/avifil32/api.c +++ b/dlls/avifil32/api.c @@ -1,6 +1,6 @@ /* * Copyright 1999 Marcus Meissner - * Copyright 2002 Michael Günnewig + * Copyright 2002-2003 Michael Günnewig * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -1388,7 +1388,7 @@ INT_PTR CALLBACK AVISaveOptionsDlgProc(HWND hWnd, UINT uMsg, } /* fall through */ case IDCANCEL: - EndDialog(hWnd, GET_WM_COMMAND_CMD(wParam, lParam) == IDOK); + EndDialog(hWnd, GET_WM_COMMAND_ID(wParam, lParam) == IDOK); break; case IDC_INTERLEAVE: EnableWindow(GetDlgItem(hWnd, IDC_INTERLEAVEEVERY), @@ -1450,10 +1450,12 @@ BOOL WINAPI AVISaveOptions(HWND hWnd, UINT uFlags, INT nStreams, ret = FALSE; /* restore options when user pressed cancel */ - if (pSavedOptions != NULL && ret == FALSE) { - for (n = 0; n < nStreams; n++) { - if (ppOptions[n] != NULL) - memcpy(ppOptions[n], pSavedOptions + n, sizeof(AVICOMPRESSOPTIONS)); + if (pSavedOptions != NULL) { + if (ret == FALSE) { + for (n = 0; n < nStreams; n++) { + if (ppOptions[n] != NULL) + memcpy(ppOptions[n], pSavedOptions + n, sizeof(AVICOMPRESSOPTIONS)); + } } GlobalFreePtr(pSavedOptions); } @@ -1529,20 +1531,443 @@ HRESULT WINAPI AVISaveVA(LPCSTR szFile, CLSID *pclsidHandler, return hr; } +/*********************************************************************** + * AVIFILE_AVISaveDefaultCallback (internal) + */ +static BOOL WINAPI AVIFILE_AVISaveDefaultCallback(INT progress) +{ + TRACE("(%d)\n", progress); + + return FALSE; +} + /*********************************************************************** * AVISaveVW (AVIFIL32.@) */ HRESULT WINAPI AVISaveVW(LPCWSTR szFile, CLSID *pclsidHandler, - AVISAVECALLBACK lpfnCallback, int nStream, + AVISAVECALLBACK lpfnCallback, int nStreams, PAVISTREAM *ppavi, LPAVICOMPRESSOPTIONS *plpOptions) { - FIXME("(%s,%p,%p,%d,%p,%p), stub!\n", debugstr_w(szFile), pclsidHandler, - lpfnCallback, nStream, ppavi, plpOptions); + LONG lStart[MAX_AVISTREAMS]; + PAVISTREAM pOutStreams[MAX_AVISTREAMS]; + PAVISTREAM pInStreams[MAX_AVISTREAMS]; + AVIFILEINFOW fInfo; + AVISTREAMINFOW sInfo; + + PAVIFILE pfile = NULL; /* the output AVI file */ + LONG lFirstVideo = -1; + int curStream; + + /* for interleaving ... */ + DWORD dwInterleave = 0; /* interleave rate */ + DWORD dwFileInitialFrames; + LONG lFileLength; + LONG lSampleInc; + + /* for reading/writing the data ... */ + LPVOID lpBuffer = NULL; + LONG cbBuffer; /* real size of lpBuffer */ + LONG lBufferSize; /* needed bytes for format(s), etc. */ + LONG lReadBytes; + LONG lReadSamples; + HRESULT hres; + + TRACE("(%s,%p,%p,%d,%p,%p)\n", debugstr_w(szFile), pclsidHandler, + lpfnCallback, nStreams, ppavi, plpOptions); if (szFile == NULL || ppavi == NULL || plpOptions == NULL) return AVIERR_BADPARAM; + if (nStreams >= MAX_AVISTREAMS) { + WARN("Can't write AVI with %d streams only supports %d -- change MAX_AVISTREAMS!\n", nStreams, MAX_AVISTREAMS); + return AVIERR_INTERNAL; + } - return AVIERR_UNSUPPORTED; + if (lpfnCallback == NULL) + lpfnCallback = AVIFILE_AVISaveDefaultCallback; + + /* clear local variable(s) */ + for (curStream = 0; curStream < nStreams; curStream++) { + pInStreams[curStream] = NULL; + pOutStreams[curStream] = NULL; + } + + /* open output AVI file (create it if it doesn't exist) */ + hres = AVIFileOpenW(&pfile, szFile, OF_CREATE|OF_SHARE_EXCLUSIVE|OF_WRITE, + pclsidHandler); + if (FAILED(hres)) + return hres; + AVIFileInfoW(pfile, &fInfo, sizeof(fInfo)); /* for dwCaps */ + + /* initialize our data structures part 1 */ + for (curStream = 0; curStream < nStreams; curStream++) { + PAVISTREAM pCurStream = ppavi[curStream]; + + hres = AVIStreamInfoW(pCurStream, &sInfo, sizeof(sInfo)); + if (FAILED(hres)) + goto error; + + /* search first video stream and check for interleaving */ + if (sInfo.fccType == streamtypeVIDEO) { + /* remember first video stream -- needed for interleaving */ + if (lFirstVideo < 0) + lFirstVideo = curStream; + } else if (!dwInterleave && plpOptions != NULL) { + /* check if any non-video stream wants to be interleaved */ + WARN("options.flags=0x%lX options.dwInterleave=%lu\n",plpOptions[curStream]->dwFlags,plpOptions[curStream]->dwInterleaveEvery); + if (plpOptions[curStream] != NULL && + plpOptions[curStream]->dwFlags & AVICOMPRESSF_INTERLEAVE) + dwInterleave = plpOptions[curStream]->dwInterleaveEvery; + } + + /* create de-/compressed stream interface if needed */ + pInStreams[curStream] = NULL; + if (plpOptions != NULL && plpOptions[curStream] != NULL) { + if (plpOptions[curStream]->fccHandler || + plpOptions[curStream]->lpFormat != NULL) { + DWORD dwKeySave = plpOptions[curStream]->dwKeyFrameEvery; + + if (fInfo.dwCaps & AVIFILECAPS_ALLKEYFRAMES) + plpOptions[curStream]->dwKeyFrameEvery = 1; + + hres = AVIMakeCompressedStream(&pInStreams[curStream], pCurStream, + plpOptions[curStream], NULL); + plpOptions[curStream]->dwKeyFrameEvery = dwKeySave; + if (FAILED(hres) || pInStreams[curStream] == NULL) { + pInStreams[curStream] = NULL; + goto error; + } + + /* test stream interface and update stream-info */ + hres = AVIStreamInfoW(pInStreams[curStream], &sInfo, sizeof(sInfo)); + if (FAILED(hres)) + goto error; + } + } + + /* now handle streams which will only be copied */ + if (pInStreams[curStream] == NULL) { + pCurStream = pInStreams[curStream] = ppavi[curStream]; + AVIStreamAddRef(pCurStream); + } else + pCurStream = pInStreams[curStream]; + + lStart[curStream] = sInfo.dwStart; + } /* for all streams */ + + /* check that first video stream is the first stream */ + if (lFirstVideo > 0) { + PAVISTREAM pTmp = pInStreams[lFirstVideo]; + LONG lTmp = lStart[lFirstVideo]; + + pInStreams[lFirstVideo] = pInStreams[0]; + pInStreams[0] = pTmp; + lStart[lFirstVideo] = lStart[0]; + lStart[0] = lTmp; + lFirstVideo = 0; + } + + /* allocate buffer for formats, data, etc. of an initiale size of 64 kByte */ + lpBuffer = GlobalAllocPtr(GPTR, cbBuffer = 0x00010000); + if (lpBuffer == NULL) { + hres = AVIERR_MEMORY; + goto error; + } + + AVIStreamInfoW(pInStreams[0], &sInfo, sizeof(sInfo)); + lFileLength = sInfo.dwLength; + dwFileInitialFrames = 0; + if (lFirstVideo >= 0) { + /* check for correct version of the format + * -- need atleast BITMAPINFOHEADER or newer + */ + lSampleInc = 1; + lBufferSize = cbBuffer; + hres = AVIStreamReadFormat(pInStreams[lFirstVideo], AVIStreamStart(pInStreams[lFirstVideo]), lpBuffer, &lBufferSize); + if (lBufferSize < (LONG)sizeof(BITMAPINFOHEADER)) + hres = AVIERR_INTERNAL; + if (FAILED(hres)) + goto error; + } else /* use one second blocks for interleaving if no video present */ + lSampleInc = AVIStreamTimeToSample(pInStreams[0], 1000000); + + /* create output streams */ + for (curStream = 0; curStream < nStreams; curStream++) { + AVIStreamInfoW(pInStreams[curStream], &sInfo, sizeof(sInfo)); + + sInfo.dwInitialFrames = 0; + if (dwInterleave != 0 && curStream > 0 && sInfo.fccType != streamtypeVIDEO) { + /* 750 ms initial frames for non-video streams */ + sInfo.dwInitialFrames = AVIStreamTimeToSample(pInStreams[0], 750); + } + + hres = AVIFileCreateStreamW(pfile, &pOutStreams[curStream], &sInfo); + if (pOutStreams[curStream] != NULL && SUCCEEDED(hres)) { + /* copy initial format for this stream */ + lBufferSize = cbBuffer; + hres = AVIStreamReadFormat(pInStreams[curStream], sInfo.dwStart, + lpBuffer, &lBufferSize); + if (FAILED(hres)) + goto error; + hres = AVIStreamSetFormat(pOutStreams[curStream], 0, lpBuffer, lBufferSize); + if (FAILED(hres)) + goto error; + + /* try to copy stream handler data */ + lBufferSize = cbBuffer; + hres = AVIStreamReadData(pInStreams[curStream], ckidSTREAMHANDLERDATA, + lpBuffer, &lBufferSize); + if (SUCCEEDED(hres) && lBufferSize > 0) { + hres = AVIStreamWriteData(pOutStreams[curStream],ckidSTREAMHANDLERDATA, + lpBuffer, lBufferSize); + if (FAILED(hres)) + goto error; + } + + if (dwFileInitialFrames < sInfo.dwInitialFrames) + dwFileInitialFrames = sInfo.dwInitialFrames; + lReadBytes = + AVIStreamSampleToSample(pOutStreams[0], pInStreams[curStream], + sInfo.dwLength); + if (lFileLength < lReadBytes) + lFileLength = lReadBytes; + } else { + /* creation of de-/compression stream interface failed */ + WARN("creation of (de-)compression stream failed for stream %d\n",curStream); + AVIStreamRelease(pInStreams[curStream]); + if (curStream + 1 >= nStreams) { + /* move the others one up */ + PAVISTREAM *ppas = &pInStreams[curStream]; + int n = nStreams - (curStream + 1); + + do { + *ppas = pInStreams[curStream + 1]; + } while (--n); + } + nStreams--; + curStream--; + } + } /* create output streams for all input streams */ + + /* have we still something to write, or lost everything? */ + if (nStreams <= 0) + goto error; + + if (dwInterleave) { + LONG lCurFrame = -dwFileInitialFrames; + + /* interleaved file */ + if (dwInterleave == 1) + AVIFileEndRecord(pfile); + + for (; lCurFrame < lFileLength; lCurFrame += lSampleInc) { + for (curStream = 0; curStream < nStreams; curStream++) { + LONG lLastSample; + + hres = AVIStreamInfoW(pOutStreams[curStream], &sInfo, sizeof(sInfo)); + if (FAILED(hres)) + goto error; + + /* initial frames phase at the end for this stream? */ + if (-(LONG)sInfo.dwInitialFrames > lCurFrame) + continue; + + if ((lFileLength - lSampleInc) <= lCurFrame) { + lLastSample = AVIStreamLength(pInStreams[curStream]); + lFirstVideo = lLastSample + AVIStreamStart(pInStreams[curStream]); + } else { + if (curStream != 0) { + lFirstVideo = + AVIStreamSampleToSample(pInStreams[curStream], pInStreams[0], + (sInfo.fccType == streamtypeVIDEO ? + (LONG)dwInterleave : lSampleInc) + + sInfo.dwInitialFrames + lCurFrame); + } else + lFirstVideo = lSampleInc + (sInfo.dwInitialFrames + lCurFrame); + + lLastSample = AVIStreamEnd(pInStreams[curStream]); + if (lLastSample <= lFirstVideo) + lFirstVideo = lLastSample; + } + + /* copy needed samples now */ + WARN("copy from stream %d samples %ld to %ld...\n",curStream, + lStart[curStream],lFirstVideo); + while (lFirstVideo > lStart[curStream]) { + DWORD flags = 0; + + /* copy format for case it can change */ + lBufferSize = cbBuffer; + hres = AVIStreamReadFormat(pInStreams[curStream], lStart[curStream], + lpBuffer, &lBufferSize); + if (FAILED(hres)) + goto error; + AVIStreamSetFormat(pOutStreams[curStream], lStart[curStream], + lpBuffer, lBufferSize); + + /* try to read data until we got it, or error */ + do { + hres = AVIStreamRead(pInStreams[curStream], lStart[curStream], + lFirstVideo - lStart[curStream], lpBuffer, + cbBuffer, &lReadBytes, &lReadSamples); + } while ((hres == AVIERR_BUFFERTOOSMALL) && + (lpBuffer = GlobalReAllocPtr(lpBuffer, cbBuffer *= 2, GPTR)) != NULL); + if (lpBuffer == NULL) + hres = AVIERR_MEMORY; + if (FAILED(hres)) + goto error; + + if (AVIStreamIsKeyFrame(pInStreams[curStream], (LONG)sInfo.dwStart)) + flags = AVIIF_KEYFRAME; + hres = AVIStreamWrite(pOutStreams[curStream], -1, lReadSamples, + lpBuffer, lReadBytes, flags, NULL, NULL); + if (FAILED(hres)) + goto error; + + lStart[curStream] += lReadSamples; + } + lStart[curStream] = lFirstVideo; + } /* stream by stream */ + + /* need to close this block? */ + if (dwInterleave == 1) { + hres = AVIFileEndRecord(pfile); + if (FAILED(hres)) + break; + } + + /* show progress */ + if (lpfnCallback(MulDiv(dwFileInitialFrames + lCurFrame, 100, + dwFileInitialFrames + lFileLength))) { + hres = AVIERR_USERABORT; + break; + } + } /* copy frame by frame */ + } else { + /* non-interleaved file */ + + for (curStream = 0; curStream < nStreams; curStream++) { + /* show progress */ + if (lpfnCallback(MulDiv(curStream, 100, nStreams))) { + hres = AVIERR_USERABORT; + goto error; + } + + AVIStreamInfoW(pInStreams[curStream], &sInfo, sizeof(sInfo)); + + if (sInfo.dwSampleSize != 0) { + /* sample-based data like audio */ + while (sInfo.dwStart < sInfo.dwLength) { + LONG lSamples = cbBuffer / sInfo.dwSampleSize; + + /* copy format for case it can change */ + lBufferSize = cbBuffer; + hres = AVIStreamReadFormat(pInStreams[curStream], sInfo.dwStart, + lpBuffer, &lBufferSize); + if (FAILED(hres)) + return hres; + AVIStreamSetFormat(pOutStreams[curStream], sInfo.dwStart, + lpBuffer, lBufferSize); + + /* limit to stream boundaries */ + if (lSamples != (LONG)(sInfo.dwLength - sInfo.dwStart)) + lSamples = sInfo.dwLength - sInfo.dwStart; + + /* now try to read until we got it, or error occures */ + do { + lReadBytes = cbBuffer; + lReadSamples = 0; + hres = AVIStreamRead(pInStreams[curStream],sInfo.dwStart,lSamples, + lpBuffer,cbBuffer,&lReadBytes,&lReadSamples); + } while ((hres == AVIERR_BUFFERTOOSMALL) && + (lpBuffer = GlobalReAllocPtr(lpBuffer, cbBuffer *= 2, GPTR)) != NULL); + if (lpBuffer == NULL) + hres = AVIERR_MEMORY; + if (FAILED(hres)) + goto error; + if (lReadSamples != 0) { + sInfo.dwStart += lReadSamples; + hres = AVIStreamWrite(pOutStreams[curStream], -1, lReadSamples, + lpBuffer, lReadBytes, 0, NULL , NULL); + if (FAILED(hres)) + goto error; + + /* show progress */ + if (lpfnCallback(MulDiv(sInfo.dwStart,100,nStreams*sInfo.dwLength)+ + MulDiv(curStream, 100, nStreams))) { + hres = AVIERR_USERABORT; + goto error; + } + } else { + if ((sInfo.dwLength - sInfo.dwStart) != 1) { + hres = AVIERR_FILEREAD; + goto error; + } + } + } + } else { + /* block-based data like video */ + for (; sInfo.dwStart < sInfo.dwLength; sInfo.dwStart++) { + DWORD flags = 0; + + /* copy format for case it can change */ + lBufferSize = cbBuffer; + hres = AVIStreamReadFormat(pInStreams[curStream], sInfo.dwStart, + lpBuffer, &lBufferSize); + if (FAILED(hres)) + goto error; + AVIStreamSetFormat(pOutStreams[curStream], sInfo.dwStart, + lpBuffer, lBufferSize); + + /* try to read block and resize buffer if neccessary */ + do { + lReadSamples = 0; + lReadBytes = cbBuffer; + hres = AVIStreamRead(pInStreams[curStream], sInfo.dwStart, 1, + lpBuffer, cbBuffer,&lReadBytes,&lReadSamples); + } while ((hres == AVIERR_BUFFERTOOSMALL) && + (lpBuffer = GlobalReAllocPtr(lpBuffer, cbBuffer *= 2, GPTR)) != NULL); + if (lpBuffer == NULL) + hres = AVIERR_MEMORY; + if (FAILED(hres)) + goto error; + if (lReadSamples != 1) { + hres = AVIERR_FILEREAD; + goto error; + } + + if (AVIStreamIsKeyFrame(pInStreams[curStream], (LONG)sInfo.dwStart)) + flags = AVIIF_KEYFRAME; + hres = AVIStreamWrite(pOutStreams[curStream], -1, lReadSamples, + lpBuffer, lReadBytes, flags, NULL, NULL); + if (FAILED(hres)) + goto error; + + /* show progress */ + if (lpfnCallback(MulDiv(sInfo.dwStart,100,nStreams*sInfo.dwLength)+ + MulDiv(curStream, 100, nStreams))) { + hres = AVIERR_USERABORT; + goto error; + } + } /* copy all blocks */ + } + } /* copy data stream by stream */ + } + + error: + if (lpBuffer != NULL) + GlobalFreePtr(lpBuffer); + if (pfile != NULL) { + for (curStream = 0; curStream < nStreams; curStream++) { + if (pOutStreams[curStream] != NULL) + AVIStreamRelease(pOutStreams[curStream]); + if (pInStreams[curStream] != NULL) + AVIStreamRelease(pInStreams[curStream]); + } + + AVIFileRelease(pfile); + } + + return hres; } /*********************************************************************** @@ -1550,16 +1975,30 @@ HRESULT WINAPI AVISaveVW(LPCWSTR szFile, CLSID *pclsidHandler, */ HRESULT WINAPI CreateEditableStream(PAVISTREAM *ppEditable, PAVISTREAM pSource) { - FIXME("(%p,%p), stub!\n", ppEditable, pSource); + IAVIEditStream *pEdit = NULL; + HRESULT hr; + + FIXME("(%p,%p), semi stub!\n", ppEditable, pSource); - if (pSource == NULL) - return AVIERR_BADHANDLE; if (ppEditable == NULL) return AVIERR_BADPARAM; *ppEditable = NULL; - return AVIERR_UNSUPPORTED; + if (pSource != NULL) { + hr = IAVIStream_QueryInterface(pSource, &IID_IAVIEditStream, + (LPVOID*)&pEdit); + if (FAILED(hr) || pEdit == NULL) { + /* need own implementation of IAVIEditStream */ + + return AVIERR_UNSUPPORTED; + } + } + + hr = IAVIEditStream_Clone(pEdit, ppEditable); + IAVIEditStream_Release(pEdit); + + return hr; } /*********************************************************************** diff --git a/dlls/avifil32/avifile.c b/dlls/avifil32/avifile.c index beb0e958aea..9112b424043 100644 --- a/dlls/avifil32/avifile.c +++ b/dlls/avifil32/avifile.c @@ -1,6 +1,6 @@ /* * Copyright 1999 Marcus Meissner - * Copyright 2002 Michael Günnewig + * Copyright 2002-2003 Michael Günnewig * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,12 +18,16 @@ */ /* TODO: - * - IAVIFile_fnEndRecord: a stub -- needed for creating interleaved AVIs. * - IAVIStreaming interface is missing for the IAVIStreamImpl * - IAVIStream_fnFindSample: FIND_INDEX isn't supported. * - IAVIStream_fnReadFormat: formatchanges aren't read in. * - IAVIStream_fnDelete: a stub. * - IAVIStream_fnSetInfo: a stub. + * - make thread safe + * + * KNOWN Bugs: + * - native version can hangup when reading a file generated with this DLL. + * When index is missing it works, but index seems to be okay. */ #define COM_NO_WINDOWS_H @@ -186,9 +190,12 @@ struct _IAVIFileImpl { DWORD dwMoviChunkPos; /* some stuff for saving ... */ DWORD dwIdxChunkPos; DWORD dwNextFramePos; + DWORD dwInitialFrames; + MMCKINFO ckLastRecord; AVIINDEXENTRY *idxRecords; /* won't be updated while loading */ - DWORD nIdxRecords; + DWORD nIdxRecords; /* current fill level */ + DWORD cbIdxRecords; /* size of idxRecords */ /* IPersistFile stuff ... */ HMMIO hmmio; @@ -201,6 +208,7 @@ struct _IAVIFileImpl { static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, DWORD offset, DWORD flags); +static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This); static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This); static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr, LPAVISTREAMINFOW asi); @@ -468,16 +476,41 @@ static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface) { ICOM_THIS(IAVIFileImpl,iface); - FIXME("(%p): stub\n",iface); + TRACE("(%p)\n",iface); if ((This->uMode & MMIO_RWMODE) == 0) return AVIERR_READONLY; This->fDirty = TRUE; - /* FIXME: end record -- for interleaved files */ + /* no frames written to any stream? -- compute start of 'movi'-chunk */ + if (This->dwMoviChunkPos == 0) + AVIFILE_ComputeMoviStart(This); - return E_FAIL; + This->fInfo.dwFlags |= AVIFILEINFO_ISINTERLEAVED; + + /* already written frames to any stream, ... */ + if (This->ckLastRecord.dwFlags & MMIO_DIRTY) { + /* close last record */ + if (mmioAscend(This->hmmio, &This->ckLastRecord, 0) != 0) + return AVIERR_FILEWRITE; + + AVIFILE_AddRecord(This); + + if (This->fInfo.dwSuggestedBufferSize < This->ckLastRecord.cksize + 3 * sizeof(DWORD)) + This->fInfo.dwSuggestedBufferSize = This->ckLastRecord.cksize + 3 * sizeof(DWORD); + } + + /* write out a new record into file, but don't close it */ + This->ckLastRecord.cksize = 0; + This->ckLastRecord.fccType = listtypeAVIRECORD; + if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1) + return AVIERR_FILEWRITE; + if (mmioCreateChunk(This->hmmio, &This->ckLastRecord, MMIO_CREATELIST) != 0) + return AVIERR_FILEWRITE; + This->dwNextFramePos += 3 * sizeof(DWORD); + + return AVIERR_OK; } static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType, @@ -1273,7 +1306,7 @@ static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream *iface, DWORD fcc, /* ckid,size => 2 * sizeof(DWORD) */ dwPos += 2 * sizeof(DWORD) + size; - if (size >= This->paf->dwMoviChunkPos) + if (size >= This->paf->dwMoviChunkPos - 2 * sizeof(DWORD)) return AVIERR_UNSUPPORTED; /* not enough space left */ } @@ -1375,8 +1408,33 @@ static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, DW This->idxFrames[This->lLastFrame].dwChunkLength = size; /* update AVISTREAMINFO structure if necessary */ - if (This->sInfo.dwLength < This->lLastFrame) - This->sInfo.dwLength = This->lLastFrame; + if (This->sInfo.dwLength <= This->lLastFrame) + This->sInfo.dwLength = This->lLastFrame + 1; + + return AVIERR_OK; +} + +static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This) +{ + /* pre-conditions */ + assert(This != NULL && This->ppStreams[0] != NULL); + + if (This->idxRecords == NULL || This->cbIdxRecords == 0) { + This->cbIdxRecords += 1024 * sizeof(AVIINDEXENTRY); + This->idxRecords = GlobalAllocPtr(GHND, This->cbIdxRecords); + if (This->idxRecords == NULL) + return AVIERR_MEMORY; + } + + assert(This->nIdxRecords < This->cbIdxRecords/sizeof(AVIINDEXENTRY)); + + This->idxRecords[This->nIdxRecords].ckid = listtypeAVIRECORD; + This->idxRecords[This->nIdxRecords].dwFlags = AVIIF_LIST; + This->idxRecords[This->nIdxRecords].dwChunkOffset = + This->ckLastRecord.dwDataOffset - 2 * sizeof(DWORD); + This->idxRecords[This->nIdxRecords].dwChunkLength = + This->ckLastRecord.cksize; + This->nIdxRecords++; return AVIERR_OK; } @@ -1751,7 +1809,7 @@ static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This) if (FAILED(hr)) return hr; - This->dwMoviChunkPos = ckLIST1.dwDataOffset - 2 * sizeof(DWORD); + This->dwMoviChunkPos = ckLIST1.dwDataOffset; This->dwIdxChunkPos = ckLIST1.cksize + ckLIST1.dwDataOffset; if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK) return AVIERR_FILEREAD; @@ -1874,6 +1932,15 @@ static HRESULT AVIFILE_LoadIndex(IAVIFileImpl *This, DWORD size, DWORD offset) if (lp != NULL) GlobalFreePtr(lp); + /* checking ... */ + for (n = 0; n < This->fInfo.dwStreams; n++) { + IAVIStreamImpl *pStream = This->ppStreams[n]; + + if (pStream->sInfo.dwLength != pStream->lLastFrame+1) + ERR("stream %lu length mismatch: dwLength=%lu found=%ld\n", + n, pStream->sInfo.dwLength, pStream->lLastFrame); + } + return hr; } @@ -2005,6 +2072,13 @@ static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This) if (This->dwMoviChunkPos == 0) AVIFILE_ComputeMoviStart(This); + /* written one record to much? */ + if (This->ckLastRecord.dwFlags & MMIO_DIRTY) { + This->dwNextFramePos -= 3 * sizeof(DWORD); + if (This->nIdxRecords > 0) + This->nIdxRecords--; + } + AVIFILE_UpdateInfo(This); assert(This->fInfo.dwScale != 0); @@ -2021,12 +2095,7 @@ static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This) MainAVIHdr.dwSuggestedBufferSize = This->fInfo.dwSuggestedBufferSize; MainAVIHdr.dwWidth = This->fInfo.dwWidth; MainAVIHdr.dwHeight = This->fInfo.dwHeight; - for (nStream = 0; nStream < MainAVIHdr.dwStreams; nStream++) { - pStream = This->ppStreams[nStream]; - - if (MainAVIHdr.dwInitialFrames < pStream->sInfo.dwInitialFrames) - MainAVIHdr.dwInitialFrames = pStream->sInfo.dwInitialFrames; - } + MainAVIHdr.dwInitialFrames = This->dwInitialFrames; /* now begin writing ... */ mmioSeek(This->hmmio, 0, SEEK_SET); @@ -2237,6 +2306,8 @@ static HRESULT AVIFILE_SaveIndex(IAVIFileImpl *This) else stepsize = AVIStreamTimeToSample((PAVISTREAM)This->ppStreams[0], 1000000); + assert(stepsize > 0); + for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { if (lInitialFrames < This->ppStreams[nStream]->sInfo.dwInitialFrames) lInitialFrames = This->ppStreams[nStream]->sInfo.dwInitialFrames; @@ -2256,7 +2327,7 @@ static HRESULT AVIFILE_SaveIndex(IAVIFileImpl *This) if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx)) return AVIERR_FILEWRITE; - for (nStream = 0; nStream < This->fInfo.dwStreams; n++) { + for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) { pStream = This->ppStreams[nStream]; /* heave we reached start of this stream? */ @@ -2307,7 +2378,7 @@ static HRESULT AVIFILE_SaveIndex(IAVIFileImpl *This) if (pStream->lLastFrame == -1) pStream->lLastFrame = 0; - for (n = 0; n < pStream->lLastFrame; n++) { + for (n = 0; n <= pStream->lLastFrame; n++) { if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) && (pStream->sInfo.dwFormatChangeCount != 0)) { DWORD pos; @@ -2387,6 +2458,7 @@ static void AVIFILE_UpdateInfo(IAVIFileImpl *This) This->fInfo.dwScale = 0; This->fInfo.dwRate = 0; This->fInfo.dwLength = 0; + This->dwInitialFrames = 0; for (i = 0; i < This->fInfo.dwStreams; i++) { AVISTREAMINFOW *psi; @@ -2411,6 +2483,9 @@ static void AVIFILE_UpdateInfo(IAVIFileImpl *This) This->fInfo.dwLength = n; } + if (This->dwInitialFrames < psi->dwInitialFrames) + This->dwInitialFrames = psi->dwInitialFrames; + if (This->fInfo.dwSuggestedBufferSize < psi->dwSuggestedBufferSize) This->fInfo.dwSuggestedBufferSize = psi->dwSuggestedBufferSize; @@ -2463,5 +2538,6 @@ static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block, This->paf->fDirty = TRUE; This->paf->dwNextFramePos = mmioSeek(This->paf->hmmio, 0, SEEK_CUR); - return AVIFILE_AddFrame(This, ckid, size, ck.dwDataOffset, flags); + return AVIFILE_AddFrame(This, ckid, size, + ck.dwDataOffset - 2 * sizeof(DWORD), flags); } diff --git a/include/vfw.h b/include/vfw.h index b7e1eb8b4e8..90ee7e4d03e 100644 --- a/include/vfw.h +++ b/include/vfw.h @@ -43,6 +43,7 @@ typedef struct IAVIStream IAVIStream,*PAVISTREAM; typedef struct IAVIFile IAVIFile,*PAVIFILE; typedef struct IGetFrame IGetFrame,*PGETFRAME; typedef struct IAVIEditStream IAVIEditStream, *PAVIEDITSTREAM; +typedef struct IAVIStreaming IAVIStreaming; /* Installable Compressor Manager */ @@ -1094,6 +1095,41 @@ LONG WINAPI AVIStreamTimeToSample(PAVISTREAM pstream, LONG lTime); #define AVIStreamStartTime(pavi) \ AVIStreamSampleToTime(pavi, AVIStreamStart(pavi)) +#define AVIStreamNextSample(pavi, pos) \ + AVIStreamFindSample(pavi, pos + 1, FIND_NEXT | FIND_ANY) +#define AVIStreamPrevSample(pavi, pos) \ + AVIStreamFindSample(pavi, pos - 1, FIND_PREV | FIND_ANY) +#define AVIStreamNearestSample(pavi, pos) \ + AVIStreamFindSample(pavi, pos, FIND_PREV | FIND_ANY) +#define AVStreamNextKeyFrame(pavi,pos) \ + AVIStreamFindSample(pavi, pos + 1, FIND_NEXT | FIND_KEY) +#define AVStreamPrevKeyFrame(pavi,pos) \ + AVIStreamFindSample(pavi, pos - 1, FIND_NEXT | FIND_KEY) +#define AVIStreamNearestKeyFrame(pavi,pos) \ + AVIStreamFindSample(pavi, pos, FIND_PREV | FIND_KEY) +#define AVIStreamIsKeyFrame(pavi, pos) \ + (AVIStreamNearestKeyFrame(pavi, pos) == pos) + +/***************************************************************************** + * IAVIStreaming interface + */ +#define INTERFACE IAVIStreaming +#define IAVIStreaming_METHODS \ + IUnknown_METHODS \ + STDMETHOD(Begin)(IAVIStreaming*iface,LONG lStart,LONG lEnd,LONG lRate) PURE; \ + STDMETHOD(End)(IAVIStreaming*iface) PURE; +#undef INTERFACE + +#ifdef COBJMACROS +/*** IUnknown methods ***/ +#define IAVIStreaming_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IAVIStreaming_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IAVIStreaming_Release(p) (p)->lpVtbl->Release(p) +/*** IAVIStreaming methods ***/ +#define IAVIStreaming_Begin(p,a,b,c) (p)->lpVtbl->Begin(p,a,b,c) +#define IAVIStreaming_End(p) (p)->lpVtbl->End(p) +#endif + /***************************************************************************** * IAVIEditStream interface */