[AVIFIL32] Many bugs fixed, some new fetures, ...

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Changelog:
  - Fixed memory leak in AVISaveOptions.
  - Implemented AVISaveVW method.
  - Semi-stub implementation for CreateEditableStream method.
  - Added support for creation of interleaved AVI files.
  - Fixed creation of index table in AVI files.
  - Added declaration for IAVIStreaming interface.
  - Added some more macros.
  - Fixed some minor bugs.


  Michael Günnewig

Index: dlls/avifil32/api.c
===================================================================
RCS file: /home/wine/wine/dlls/avifil32/api.c,v
retrieving revision 1.17
diff -d -u -r1.17 api.c
--- dlls/avifil32/api.c	30 Jun 2003 02:04:05 -0000	1.17
+++ dlls/avifil32/api.c	1 Jul 2003 19:38:41 -0000
@@ -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 @@
       }
       /* 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 @@
     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);
   }
@@ -1530,19 +1532,442 @@
 }
 
 /***********************************************************************
+ *		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 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;
 }
 
 /***********************************************************************
Index: dlls/avifil32/avifile.c
===================================================================
RCS file: /home/wine/wine/dlls/avifil32/avifile.c,v
retrieving revision 1.34
diff -d -u -r1.34 avifile.c
--- dlls/avifil32/avifile.c	23 Jun 2003 18:10:06 -0000	1.34
+++ dlls/avifil32/avifile.c	1 Jul 2003 19:38:59 -0000
@@ -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 @@
   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 @@
 
 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 @@
 {
   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 @@
 
     /* 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 @@
   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 @@
   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 @@
   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 @@
   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 @@
   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 @@
     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 @@
       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 @@
       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 @@
   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 @@
 	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 @@
   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);
 }
Index: include/vfw.h
===================================================================
RCS file: /home/wine/wine/include/vfw.h,v
retrieving revision 1.35
diff -d -u -r1.35 vfw.h
--- include/vfw.h	23 Jun 2003 18:10:06 -0000	1.35
+++ include/vfw.h	1 Jul 2003 19:39:46 -0000
@@ -43,6 +43,7 @@
 typedef struct IAVIFile IAVIFile,*PAVIFILE;
 typedef struct IGetFrame IGetFrame,*PGETFRAME;
 typedef struct IAVIEditStream IAVIEditStream, *PAVIEDITSTREAM;
+typedef struct IAVIStreaming  IAVIStreaming;
 
 /* Installable Compressor Manager */
 
@@ -1093,6 +1094,41 @@
     AVIStreamTimeToSample(pavi1, AVIStreamSampleToTime(pavi2, samp2))
 #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

[Index of Archives]     [Gimp for Windows]     [Red Hat]     [Samba]     [Yosemite Camping]     [Graphics Cards]     [Wine Home]

  Powered by Linux