2 * Copyright 1999 Marcus Meissner
3 * Copyright 2001 Hidenori TAKESHIMA <hidenori@a2.ctktv.ne.jp>
5 * FIXME - implements editing/writing.
17 #include "debugtools.h"
18 #include "avifile_private.h"
20 DEFAULT_DEBUG_CHANNEL(avifile);
22 #define AVIFILE_STREAMS_MAX 4
25 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile* iface,REFIID refiid,LPVOID *obj);
26 static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile* iface);
27 static ULONG WINAPI IAVIFile_fnRelease(IAVIFile* iface);
28 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile*iface,AVIFILEINFOW*afi,LONG size);
29 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile*iface,PAVISTREAM*avis,DWORD fccType,LONG lParam);
30 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile*iface,PAVISTREAM*avis,AVISTREAMINFOW*asi);
31 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG size);
32 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG *size);
33 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile*iface);
34 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile*iface,DWORD fccType,LONG lParam);
36 struct ICOM_VTABLE(IAVIFile) iavift = {
37 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
38 IAVIFile_fnQueryInterface,
43 IAVIFile_fnCreateStream,
47 IAVIFile_fnDeleteStream,
48 /* IAVIFILE_fnOpen */ /* FIXME? */
52 typedef struct IAVIFileImpl
54 ICOM_VFIELD(IAVIFile);
62 DWORD dwAVIFileLength;
63 DWORD dwAVIFileEditCount;
65 IAVIStream* pStreams[AVIFILE_STREAMS_MAX];
66 AVIStreamHeader strhdrs[AVIFILE_STREAMS_MAX];
68 DWORD dwCountOfIndexEntry;
69 AVIINDEXENTRY* pIndexEntry;
70 AVIINDEXENTRY* pStreamIndexEntry[AVIFILE_STREAMS_MAX+1];
74 /****************************************************************************
78 static HRESULT AVIFILE_IAVIFile_ReadNextChunkHeader(
79 IAVIFileImpl* This, FOURCC* pfcc, DWORD* pdwSize )
84 if ( ( !ReadFile( This->hf, buf, 8, &dwRead, NULL ) ) ||
86 return AVIERR_FILEREAD;
87 *pfcc = mmioFOURCC(buf[0],buf[1],buf[2],buf[3]);
88 *pdwSize = ( ((DWORD)buf[4]) ) |
89 ( ((DWORD)buf[5]) << 8 ) |
90 ( ((DWORD)buf[6]) << 16 ) |
91 ( ((DWORD)buf[7]) << 24 );
96 static HRESULT AVIFILE_IAVIFile_SkipChunkData(
97 IAVIFileImpl* This, DWORD dwChunkSize )
102 if ( dwChunkSize == 0 )
105 SetLastError(NO_ERROR);
106 dwRes = SetFilePointer( This->hf, (LONG)dwChunkSize,
107 &lHigh, FILE_CURRENT );
108 if ( dwRes == (DWORD)0xffffffff && GetLastError() != NO_ERROR )
109 return AVIERR_FILEREAD;
114 static HRESULT AVIFILE_IAVIFile_ReadChunkData(
115 IAVIFileImpl* This, DWORD dwChunkSize,
116 LPVOID lpvBuf, DWORD dwBufSize, LPDWORD lpdwRead )
118 if ( dwBufSize > dwChunkSize )
119 dwBufSize = dwChunkSize;
120 if ( ( !ReadFile( This->hf, lpvBuf, dwBufSize, lpdwRead, NULL ) ) ||
121 ( dwBufSize != *lpdwRead ) )
122 return AVIERR_FILEREAD;
124 return AVIFILE_IAVIFile_SkipChunkData( This, dwChunkSize - dwBufSize );
127 static HRESULT AVIFILE_IAVIFile_SeekToSpecifiedChunk(
128 IAVIFileImpl* This, FOURCC fccType, DWORD* pdwLen )
137 hr = AVIFILE_IAVIFile_ReadNextChunkHeader(
138 This, &fcc, pdwLen );
141 if ( fcc == fccType )
144 if ( fcc == FOURCC_LIST )
146 if ( ( !ReadFile( This->hf, buf, 4, &dwRead, NULL ) ) ||
148 return AVIERR_FILEREAD;
152 hr = AVIFILE_IAVIFile_SkipChunkData(
161 WINE_AVISTREAM_DATA* AVIFILE_Alloc_IAVIStreamData( DWORD dwFmtLen )
163 WINE_AVISTREAM_DATA* pData;
165 pData = (WINE_AVISTREAM_DATA*)
166 HeapAlloc( AVIFILE_data.hHeap,HEAP_ZERO_MEMORY,
167 sizeof(WINE_AVISTREAM_DATA) );
172 pData->pbFmt = (BYTE*)
173 HeapAlloc( AVIFILE_data.hHeap,HEAP_ZERO_MEMORY,
174 sizeof(BYTE)*dwFmtLen );
175 if ( pData->pbFmt == NULL )
177 AVIFILE_Free_IAVIStreamData( pData );
181 pData->dwFmtLen = dwFmtLen;
186 void AVIFILE_Free_IAVIStreamData( WINE_AVISTREAM_DATA* pData )
190 if ( pData->pbFmt != NULL )
191 HeapFree( AVIFILE_data.hHeap,0,pData->pbFmt );
192 HeapFree( AVIFILE_data.hHeap,0,pData );
196 static void AVIFILE_IAVIFile_InitIndexTable(
198 AVIINDEXENTRY* pIndexBuf,
199 AVIINDEXENTRY* pIndexData,
200 DWORD dwCountOfIndexEntry )
207 for ( ; dwStreamIndex < (AVIFILE_STREAMS_MAX+1); dwStreamIndex ++ )
208 This->pStreamIndexEntry[dwStreamIndex] = NULL;
211 for ( ; dwStreamIndex < This->hdr.dwStreams; dwStreamIndex ++ )
213 ckid = mmioFOURCC('0','0'+dwStreamIndex,0,0);
214 TRACE( "testing ckid %c%c%c%c\n",
215 (int)(ckid>> 0)&0xff,
216 (int)(ckid>> 8)&0xff,
217 (int)(ckid>>16)&0xff,
218 (int)(ckid>>24)&0xff );
219 This->pStreamIndexEntry[dwStreamIndex] = pIndexBuf;
220 FIXME( "pIndexBuf = %p\n", pIndexBuf );
221 for ( dwIndex = 0; dwIndex < dwCountOfIndexEntry; dwIndex++ )
223 TRACE( "ckid %c%c%c%c\n",
224 (int)(pIndexData[dwIndex].ckid>> 0)&0xff,
225 (int)(pIndexData[dwIndex].ckid>> 8)&0xff,
226 (int)(pIndexData[dwIndex].ckid>>16)&0xff,
227 (int)(pIndexData[dwIndex].ckid>>24)&0xff );
229 if ( (pIndexData[dwIndex].ckid & mmioFOURCC(0xff,0xff,0,0))
232 memcpy( pIndexBuf, &pIndexData[dwIndex],
233 sizeof(AVIINDEXENTRY) );
237 FIXME( "pIndexBuf = %p\n", pIndexBuf );
239 This->pStreamIndexEntry[This->hdr.dwStreams] = pIndexBuf;
243 /****************************************************************************
244 * Create an IAVIFile object.
247 static HRESULT AVIFILE_IAVIFile_Construct( IAVIFileImpl* This );
248 static void AVIFILE_IAVIFile_Destruct( IAVIFileImpl* This );
250 HRESULT AVIFILE_CreateIAVIFile(void** ppobj)
255 TRACE("(%p)\n",ppobj);
257 This = (IAVIFileImpl*)HeapAlloc(AVIFILE_data.hHeap,HEAP_ZERO_MEMORY,
258 sizeof(IAVIFileImpl));
260 return AVIERR_MEMORY;
262 ICOM_VTBL(This) = &iavift;
263 hr = AVIFILE_IAVIFile_Construct( This );
266 AVIFILE_IAVIFile_Destruct( This );
270 TRACE("new -> %p\n",This);
271 *ppobj = (LPVOID)This;
276 /****************************************************************************
280 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile* iface,REFIID refiid,LPVOID *obj) {
281 ICOM_THIS(IAVIFileImpl,iface);
283 TRACE("(%p)->QueryInterface(%s,%p)\n",This,debugstr_guid(refiid),obj);
284 if ( IsEqualGUID(&IID_IUnknown,refiid) ||
285 IsEqualGUID(&IID_IAVIFile,refiid) )
288 IAVIFile_AddRef(iface);
291 return OLE_E_ENUM_NOMORE;
294 static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile* iface) {
295 ICOM_THIS(IAVIFileImpl,iface);
297 TRACE("(%p)->AddRef()\n",iface);
298 return ++(This->ref);
301 static ULONG WINAPI IAVIFile_fnRelease(IAVIFile* iface) {
302 ICOM_THIS(IAVIFileImpl,iface);
304 TRACE("(%p)->Release()\n",iface);
305 if ( (--(This->ref)) > 0 )
308 AVIFILE_IAVIFile_Destruct(This);
309 HeapFree(AVIFILE_data.hHeap,0,iface);
313 /****************************************************************************
317 static HRESULT AVIFILE_IAVIFile_Construct( IAVIFileImpl* This )
321 This->hf = INVALID_HANDLE_VALUE;
322 This->dwAVIFileCaps = 0;
323 This->dwAVIFileScale = 0;
324 This->dwAVIFileRate = 0;
325 This->dwAVIFileLength = 0;
326 This->dwAVIFileEditCount = 0;
327 for ( dwIndex = 0; dwIndex < AVIFILE_STREAMS_MAX; dwIndex++ )
328 This->pStreams[dwIndex] = NULL;
329 This->dwCountOfIndexEntry = 0;
330 This->pIndexEntry = NULL;
332 AVIFILE_data.dwClassObjRef ++;
337 static void AVIFILE_IAVIFile_Destruct( IAVIFileImpl* This )
341 if ( This->pIndexEntry != NULL )
343 HeapFree(AVIFILE_data.hHeap,0,This->pIndexEntry);
344 This->pIndexEntry = NULL;
347 for ( dwIndex = 0; dwIndex < AVIFILE_STREAMS_MAX; dwIndex++ )
349 if ( This->pStreams[dwIndex] != NULL )
351 IAVIStream_Release( This->pStreams[dwIndex] );
352 This->pStreams[dwIndex] = NULL;
356 if ( This->hf != INVALID_HANDLE_VALUE )
357 CloseHandle( This->hf );
359 AVIFILE_data.dwClassObjRef --;
362 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile*iface,AVIFILEINFOW*afi,LONG size)
364 ICOM_THIS(IAVIFileImpl,iface);
367 FIXME("(%p)->Info(%p,%ld)\n",iface,afi,size);
369 memset( &fiw, 0, sizeof(fiw) );
370 fiw.dwMaxBytesPerSec = This->hdr.dwMaxBytesPerSec;
371 fiw.dwFlags = This->hdr.dwFlags;
372 fiw.dwCaps = This->dwAVIFileCaps;
373 fiw.dwStreams = This->hdr.dwStreams;
374 fiw.dwSuggestedBufferSize = This->hdr.dwSuggestedBufferSize;
375 fiw.dwWidth = This->hdr.dwWidth;
376 fiw.dwHeight = This->hdr.dwHeight;
377 fiw.dwScale = This->dwAVIFileScale; /* FIXME */
378 fiw.dwRate = This->dwAVIFileRate; /* FIXME */
379 fiw.dwLength = This->dwAVIFileLength; /* FIXME */
380 fiw.dwEditCount = This->dwAVIFileEditCount; /* FIXME */
381 /* fiw.szFileType[64]; */
383 if ( size > sizeof(AVIFILEINFOW) )
384 size = sizeof(AVIFILEINFOW);
385 memcpy( afi, &fiw, size );
390 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile*iface,PAVISTREAM*avis,DWORD fccType,LONG lParam)
392 ICOM_THIS(IAVIFileImpl,iface);
394 FIXME("(%p)->GetStream(%p,0x%08lx,%ld)\n",iface,avis,fccType,lParam);
397 if ( lParam < 0 || lParam >= This->hdr.dwStreams )
399 *avis = This->pStreams[lParam];
400 IAVIStream_AddRef( *avis );
405 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile*iface,PAVISTREAM*avis,AVISTREAMINFOW*asi)
407 ICOM_THIS(IAVIFileImpl,iface);
409 FIXME("(%p,%p,%p)\n",This,avis,asi);
413 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG size)
415 ICOM_THIS(IAVIFileImpl,iface);
417 FIXME("(%p)->WriteData(0x%08lx,%p,%ld)\n",This,ckid,lpData,size);
418 /* FIXME: write data to file */
422 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG *size)
424 ICOM_THIS(IAVIFileImpl,iface);
426 FIXME("(%p)->ReadData(0x%08lx,%p,%p)\n",This,ckid,lpData,size);
427 /* FIXME: read at most size bytes from file */
432 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile*iface)
434 ICOM_THIS(IAVIFileImpl,iface);
436 FIXME("(%p)->EndRecord()\n",This);
437 /* FIXME: end record? */
441 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile*iface,DWORD fccType,LONG lParam)
443 ICOM_THIS(IAVIFileImpl,iface);
445 FIXME("(%p)->DeleteStream(0x%08lx,%ld)\n",This,fccType,lParam);
446 /* FIXME: delete stream? */
450 /*****************************************************************************
451 * AVIFILE_IAVIFile_Open (internal)
453 HRESULT AVIFILE_IAVIFile_Open( PAVIFILE paf, LPCWSTR szFile, UINT uMode )
455 ICOM_THIS(IAVIFileImpl,paf);
466 FIXME("(%p)->Open(%p,%u)\n",This,szFile,uMode);
468 if ( This->hf != INVALID_HANDLE_VALUE )
470 CloseHandle( This->hf );
471 This->hf = INVALID_HANDLE_VALUE;
474 switch ( uMode & 0x3 )
476 case OF_READ: /* 0x0 */
477 dwAcc = GENERIC_READ;
478 dwCreate = OPEN_EXISTING;
479 This->dwAVIFileCaps = AVIFILECAPS_CANREAD;
481 case OF_WRITE: /* 0x1 */
482 dwAcc = GENERIC_WRITE;
483 dwCreate = OPEN_ALWAYS;
484 This->dwAVIFileCaps = AVIFILECAPS_CANWRITE;
486 case OF_READWRITE: /* 0x2 */
487 dwAcc = GENERIC_READ|GENERIC_WRITE;
488 dwCreate = OPEN_ALWAYS;
489 This->dwAVIFileCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
495 if ( This->dwAVIFileCaps & AVIFILECAPS_CANWRITE )
497 FIXME( "editing AVI is currently not supported!\n" );
501 switch ( uMode & 0x70 )
503 case OF_SHARE_COMPAT: /* 0x00 */
504 dwShared = FILE_SHARE_READ|FILE_SHARE_WRITE;
506 case OF_SHARE_EXCLUSIVE: /* 0x10 */
509 case OF_SHARE_DENY_WRITE: /* 0x20 */
510 dwShared = FILE_SHARE_READ;
512 case OF_SHARE_DENY_READ: /* 0x30 */
513 dwShared = FILE_SHARE_WRITE;
515 case OF_SHARE_DENY_NONE: /* 0x40 */
516 dwShared = FILE_SHARE_READ|FILE_SHARE_WRITE;
521 if ( uMode & OF_CREATE )
522 dwCreate = CREATE_ALWAYS;
524 This->hf = CreateFileW( szFile, dwAcc, dwShared, NULL,
525 dwCreate, FILE_ATTRIBUTE_NORMAL,
527 if ( This->hf == INVALID_HANDLE_VALUE )
528 return AVIERR_FILEOPEN;
530 if ( dwAcc & GENERIC_READ )
532 if ( !ReadFile( This->hf, buf, 12, &dwRead, NULL ) )
533 return AVIERR_FILEREAD;
536 if ( mmioFOURCC(buf[0],buf[1],buf[2],buf[3]) != FOURCC_RIFF )
537 return AVIERR_BADFORMAT;
539 fccFileType = mmioFOURCC(buf[8],buf[9],buf[10],buf[11]);
540 if ( fccFileType != formtypeAVI )
541 return AVIERR_BADFORMAT;
543 /* get AVI main header. */
544 hr = AVIFILE_IAVIFile_SeekToSpecifiedChunk(
545 This, ckidAVIMAINHDR, &dwLen );
548 if ( dwLen < (sizeof(DWORD)*10) )
549 return AVIERR_BADFORMAT;
550 hr = AVIFILE_IAVIFile_ReadChunkData(
552 &(This->hdr), sizeof(MainAVIHeader), &dwLen );
553 if ( This->hdr.dwStreams == 0 ||
554 This->hdr.dwStreams > AVIFILE_STREAMS_MAX )
555 return AVIERR_BADFORMAT;
557 /* get stream headers. */
559 while ( dwIndex < This->hdr.dwStreams )
561 WINE_AVISTREAM_DATA* pData;
563 hr = AVIFILE_IAVIFile_SeekToSpecifiedChunk(
564 This, ckidSTREAMHEADER, &dwLen );
567 if ( dwLen < (sizeof(DWORD)*12) )
568 return AVIERR_BADFORMAT;
569 hr = AVIFILE_IAVIFile_ReadChunkData(
571 &This->strhdrs[dwIndex],
572 sizeof(AVIStreamHeader), &dwLen );
574 hr = AVIFILE_IAVIFile_SeekToSpecifiedChunk(
575 This, ckidSTREAMFORMAT, &dwLen );
578 pData = AVIFILE_Alloc_IAVIStreamData( dwLen );
580 return AVIERR_MEMORY;
581 hr = AVIFILE_IAVIFile_ReadChunkData(
583 pData->pbFmt, dwLen, &dwLen );
586 AVIFILE_Free_IAVIStreamData( pData );
589 pData->dwStreamIndex = dwIndex;
590 pData->pstrhdr = &This->strhdrs[dwIndex];
592 hr = AVIStreamCreate(&This->pStreams[dwIndex],
593 (LONG)paf, (LONG)(pData), NULL );
596 AVIFILE_Free_IAVIStreamData( pData );
600 if ( (This->strhdrs[dwIndex].fccType
601 == mmioFOURCC('v','i','d','s')) ||
602 (This->strhdrs[dwIndex].fccType
603 == mmioFOURCC('V','I','D','S')) )
605 This->dwAVIFileScale =
606 This->strhdrs[dwIndex].dwScale;
607 This->dwAVIFileRate =
608 This->strhdrs[dwIndex].dwRate;
609 This->dwAVIFileLength =
610 This->strhdrs[dwIndex].dwLength;
613 if ( This->dwAVIFileScale == 0 )
615 This->dwAVIFileScale =
616 This->strhdrs[dwIndex].dwScale;
617 This->dwAVIFileRate =
618 This->strhdrs[dwIndex].dwRate;
619 This->dwAVIFileLength =
620 This->strhdrs[dwIndex].dwLength;
629 hr = AVIFILE_IAVIFile_SeekToSpecifiedChunk(
630 This, FOURCC_LIST, &dwLen );
634 return AVIERR_BADFORMAT;
636 This->dwMoviTop = SetFilePointer( This->hf,0,NULL,FILE_CURRENT );
637 if ( This->dwMoviTop == 0xffffffff )
638 return AVIERR_BADFORMAT;
640 if ( ( !ReadFile(This->hf, buf, 4, &dwRead, NULL) ) ||
642 return AVIERR_FILEREAD;
644 hr = AVIFILE_IAVIFile_SkipChunkData(
648 if ( mmioFOURCC(buf[0],buf[1],buf[2],buf[3])
649 == mmioFOURCC('m', 'o', 'v', 'i') )
654 hr = AVIFILE_IAVIFile_SeekToSpecifiedChunk(
655 This, ckidAVINEWINDEX, &dwLen );
659 This->dwCountOfIndexEntry = dwLen / sizeof(AVIINDEXENTRY);
660 This->pIndexEntry = (AVIINDEXENTRY*)
661 HeapAlloc(AVIFILE_data.hHeap,HEAP_ZERO_MEMORY,
662 sizeof(AVIINDEXENTRY) *
663 This->dwCountOfIndexEntry * 2 );
664 if ( This->pIndexEntry == NULL )
665 return AVIERR_MEMORY;
666 hr = AVIFILE_IAVIFile_ReadChunkData(
668 This->pIndexEntry + This->dwCountOfIndexEntry,
669 sizeof(AVIINDEXENTRY) *
670 This->dwCountOfIndexEntry, &dwLen );
673 AVIFILE_IAVIFile_InitIndexTable(
674 This, This->pIndexEntry,
675 This->pIndexEntry + This->dwCountOfIndexEntry,
676 This->dwCountOfIndexEntry );
680 /* FIXME - create the handle has GENERIC_WRITE access. */
681 return AVIERR_FILEREAD;
686 return AVIERR_FILEOPEN; /* FIXME */
692 /*****************************************************************************
693 * AVIFILE_IAVIFile_GetIndexTable (internal)
695 HRESULT AVIFILE_IAVIFile_GetIndexTable( PAVIFILE paf, DWORD dwStreamIndex,
696 AVIINDEXENTRY** ppIndexEntry,
697 DWORD* pdwCountOfIndexEntry )
699 ICOM_THIS(IAVIFileImpl,paf);
701 if ( dwStreamIndex < 0 || dwStreamIndex >= This->hdr.dwStreams )
703 FIXME( "invalid stream index %lu\n", dwStreamIndex );
706 FIXME( "cur %p, next %p\n",
707 This->pStreamIndexEntry[dwStreamIndex],
708 This->pStreamIndexEntry[dwStreamIndex+1] );
709 *ppIndexEntry = This->pStreamIndexEntry[dwStreamIndex];
710 *pdwCountOfIndexEntry =
711 This->pStreamIndexEntry[dwStreamIndex+1] -
712 This->pStreamIndexEntry[dwStreamIndex];
717 /*****************************************************************************
718 * AVIFILE_IAVIFile_ReadMovieData (internal)
720 HRESULT AVIFILE_IAVIFile_ReadMovieData( PAVIFILE paf, DWORD dwOffset,
721 DWORD dwLength, LPVOID lpvBuf )
723 ICOM_THIS(IAVIFileImpl,paf);
729 SetLastError(NO_ERROR);
730 dwRes = SetFilePointer( This->hf, (LONG)(dwOffset+This->dwMoviTop),
731 &lHigh, FILE_BEGIN );
732 if ( dwRes == (DWORD)0xffffffff && GetLastError() != NO_ERROR )
733 return AVIERR_FILEREAD;
735 if ( ( !ReadFile(This->hf, lpvBuf, dwLength, &dwRes, NULL) ) ||
736 ( dwLength != dwRes ) )
738 FIXME( "error in ReadFile()\n" );
739 return AVIERR_FILEREAD;