[AVIFIL32]
[reactos.git] / reactos / dll / win32 / avifil32 / avifile.c
1 /*
2 * Copyright 1999 Marcus Meissner
3 * Copyright 2002-2003 Michael Günnewig
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18 */
19
20 /* TODO:
21 * - IAVIStreaming interface is missing for the IAVIStreamImpl
22 * - IAVIStream_fnFindSample: FIND_INDEX isn't supported.
23 * - IAVIStream_fnReadFormat: formatchanges aren't read in.
24 * - IAVIStream_fnDelete: a stub.
25 * - IAVIStream_fnSetInfo: a stub.
26 * - make thread safe
27 *
28 * KNOWN Bugs:
29 * - native version can hangup when reading a file generated with this DLL.
30 * When index is missing it works, but index seems to be okay.
31 */
32
33 #include "avifile_private.h"
34
35 #ifndef IDX_PER_BLOCK
36 #define IDX_PER_BLOCK 2730
37 #endif
38
39 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID *obj);
40 static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream*iface);
41 static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface);
42 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2);
43 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size);
44 static LONG WINAPI IAVIStream_fnFindSample(IAVIStream*iface,LONG pos,LONG flags);
45 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG *formatsize);
46 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize);
47 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG *bytesread,LONG *samplesread);
48 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG *sampwritten,LONG *byteswritten);
49 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream*iface,LONG start,LONG samples);
50 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG *lpread);
51 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG size);
52 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen);
53
54 static const struct IAVIStreamVtbl iavist = {
55 IAVIStream_fnQueryInterface,
56 IAVIStream_fnAddRef,
57 IAVIStream_fnRelease,
58 IAVIStream_fnCreate,
59 IAVIStream_fnInfo,
60 IAVIStream_fnFindSample,
61 IAVIStream_fnReadFormat,
62 IAVIStream_fnSetFormat,
63 IAVIStream_fnRead,
64 IAVIStream_fnWrite,
65 IAVIStream_fnDelete,
66 IAVIStream_fnReadData,
67 IAVIStream_fnWriteData,
68 IAVIStream_fnSetInfo
69 };
70
71 typedef struct _IAVIFileImpl IAVIFileImpl;
72
73 typedef struct _IAVIStreamImpl {
74 /* IUnknown stuff */
75 const IAVIStreamVtbl *lpVtbl;
76 LONG ref;
77
78 /* IAVIStream stuff */
79 IAVIFileImpl *paf;
80 DWORD nStream; /* the n-th stream in file */
81 AVISTREAMINFOW sInfo;
82
83 LPVOID lpFormat;
84 DWORD cbFormat;
85
86 LPVOID lpHandlerData;
87 DWORD cbHandlerData;
88
89 EXTRACHUNKS extra;
90
91 LPDWORD lpBuffer;
92 DWORD cbBuffer; /* size of lpBuffer */
93 DWORD dwCurrentFrame; /* frame/block currently in lpBuffer */
94
95 LONG lLastFrame; /* last correct index in idxFrames */
96 AVIINDEXENTRY *idxFrames;
97 DWORD nIdxFrames; /* upper index limit of idxFrames */
98 AVIINDEXENTRY *idxFmtChanges;
99 DWORD nIdxFmtChanges; /* upper index limit of idxFmtChanges */
100 } IAVIStreamImpl;
101
102 struct _IAVIFileImpl {
103 IUnknown IUnknown_inner;
104 IAVIFile IAVIFile_iface;
105 IPersistFile IPersistFile_iface;
106 IUnknown *outer_unk;
107 LONG ref;
108
109 AVIFILEINFOW fInfo;
110 IAVIStreamImpl *ppStreams[MAX_AVISTREAMS];
111
112 EXTRACHUNKS fileextra;
113
114 DWORD dwMoviChunkPos; /* some stuff for saving ... */
115 DWORD dwIdxChunkPos;
116 DWORD dwNextFramePos;
117 DWORD dwInitialFrames;
118
119 MMCKINFO ckLastRecord;
120 AVIINDEXENTRY *idxRecords; /* won't be updated while loading */
121 DWORD nIdxRecords; /* current fill level */
122 DWORD cbIdxRecords; /* size of idxRecords */
123
124 /* IPersistFile stuff ... */
125 HMMIO hmmio;
126 LPWSTR szFileName;
127 UINT uMode;
128 BOOL fDirty;
129 };
130
131 static inline IAVIFileImpl *impl_from_IUnknown(IUnknown *iface)
132 {
133 return CONTAINING_RECORD(iface, IAVIFileImpl, IUnknown_inner);
134 }
135
136 static inline IAVIFileImpl *impl_from_IAVIFile(IAVIFile *iface)
137 {
138 return CONTAINING_RECORD(iface, IAVIFileImpl, IAVIFile_iface);
139 }
140
141 static inline IAVIFileImpl *impl_from_IPersistFile(IPersistFile *iface)
142 {
143 return CONTAINING_RECORD(iface, IAVIFileImpl, IPersistFile_iface);
144 }
145
146 /***********************************************************************/
147
148 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size,
149 DWORD offset, DWORD flags);
150 static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This);
151 static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This);
152 static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr,
153 const AVISTREAMINFOW *asi);
154 static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This);
155 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This);
156 static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset);
157 static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp,
158 LONG count, DWORD pos, BOOL *bAbsolute);
159 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD start,
160 LPVOID buffer, DWORD size);
161 static void AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos,
162 LPLONG offset);
163 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This);
164 static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This);
165 static ULONG AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fccType,
166 LONG lSkip);
167 static void AVIFILE_UpdateInfo(IAVIFileImpl *This);
168 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
169 FOURCC ckid, DWORD flags, LPCVOID buffer,
170 LONG size);
171
172 static HRESULT WINAPI IUnknown_fnQueryInterface(IUnknown *iface, REFIID riid, void **ppv)
173 {
174 IAVIFileImpl *This = impl_from_IUnknown(iface);
175
176 TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppv);
177
178 if (!ppv) {
179 WARN("invalid parameter\n");
180 return E_INVALIDARG;
181 }
182 *ppv = NULL;
183
184 if (IsEqualIID(riid, &IID_IUnknown))
185 *ppv = &This->IUnknown_inner;
186 else if (IsEqualIID(riid, &IID_IAVIFile))
187 *ppv = &This->IAVIFile_iface;
188 else if (IsEqualGUID(riid, &IID_IPersistFile))
189 *ppv = &This->IPersistFile_iface;
190 else {
191 WARN("unknown IID %s\n", debugstr_guid(riid));
192 return E_NOINTERFACE;
193 }
194
195 /* Violation of the COM aggregation ref counting rule */
196 IUnknown_AddRef(&This->IUnknown_inner);
197 return S_OK;
198 }
199
200 static ULONG WINAPI IUnknown_fnAddRef(IUnknown *iface)
201 {
202 IAVIFileImpl *This = impl_from_IUnknown(iface);
203 ULONG ref = InterlockedIncrement(&This->ref);
204
205 TRACE("(%p) ref=%d\n", This, ref);
206
207 return ref;
208 }
209
210 static ULONG WINAPI IUnknown_fnRelease(IUnknown *iface)
211 {
212 IAVIFileImpl *This = impl_from_IUnknown(iface);
213 ULONG ref = InterlockedDecrement(&This->ref);
214 UINT i;
215
216 TRACE("(%p) ref=%d\n", This, ref);
217
218 if (!ref) {
219 if (This->fDirty)
220 AVIFILE_SaveFile(This);
221
222 for (i = 0; i < This->fInfo.dwStreams; i++) {
223 if (This->ppStreams[i] != NULL) {
224 if (This->ppStreams[i]->ref != 0)
225 ERR(": someone has still %u reference to stream %u (%p)!\n",
226 This->ppStreams[i]->ref, i, This->ppStreams[i]);
227 AVIFILE_DestructAVIStream(This->ppStreams[i]);
228 HeapFree(GetProcessHeap(), 0, This->ppStreams[i]);
229 This->ppStreams[i] = NULL;
230 }
231 }
232
233 if (This->idxRecords != NULL) {
234 HeapFree(GetProcessHeap(), 0, This->idxRecords);
235 This->idxRecords = NULL;
236 This->nIdxRecords = 0;
237 }
238
239 if (This->fileextra.lp != NULL) {
240 HeapFree(GetProcessHeap(), 0, This->fileextra.lp);
241 This->fileextra.lp = NULL;
242 This->fileextra.cb = 0;
243 }
244
245 HeapFree(GetProcessHeap(), 0, This->szFileName);
246 This->szFileName = NULL;
247
248 if (This->hmmio != NULL) {
249 mmioClose(This->hmmio, 0);
250 This->hmmio = NULL;
251 }
252
253 HeapFree(GetProcessHeap(), 0, This);
254 }
255 return ref;
256 }
257
258 static const IUnknownVtbl unk_vtbl =
259 {
260 IUnknown_fnQueryInterface,
261 IUnknown_fnAddRef,
262 IUnknown_fnRelease
263 };
264
265 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID riid, void **ppv)
266 {
267 IAVIFileImpl *This = impl_from_IAVIFile(iface);
268
269 return IUnknown_QueryInterface(This->outer_unk, riid, ppv);
270 }
271
272 static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile *iface)
273 {
274 IAVIFileImpl *This = impl_from_IAVIFile(iface);
275
276 return IUnknown_AddRef(This->outer_unk);
277 }
278
279 static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface)
280 {
281 IAVIFileImpl *This = impl_from_IAVIFile(iface);
282
283 return IUnknown_Release(This->outer_unk);
284 }
285
286 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, AVIFILEINFOW *afi, LONG size)
287 {
288 IAVIFileImpl *This = impl_from_IAVIFile(iface);
289
290 TRACE("(%p,%p,%d)\n",iface,afi,size);
291
292 if (afi == NULL)
293 return AVIERR_BADPARAM;
294 if (size < 0)
295 return AVIERR_BADSIZE;
296
297 AVIFILE_UpdateInfo(This);
298
299 memcpy(afi, &This->fInfo, min((DWORD)size, sizeof(This->fInfo)));
300
301 if ((DWORD)size < sizeof(This->fInfo))
302 return AVIERR_BUFFERTOOSMALL;
303 return AVIERR_OK;
304 }
305
306 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, IAVIStream **avis, DWORD fccType,
307 LONG lParam)
308 {
309 IAVIFileImpl *This = impl_from_IAVIFile(iface);
310 ULONG nStream;
311
312 TRACE("(%p,%p,0x%08X,%d)\n", iface, avis, fccType, lParam);
313
314 if (avis == NULL || lParam < 0)
315 return AVIERR_BADPARAM;
316
317 nStream = AVIFILE_SearchStream(This, fccType, lParam);
318
319 /* Does the requested stream exist? */
320 if (nStream < This->fInfo.dwStreams &&
321 This->ppStreams[nStream] != NULL) {
322 *avis = (PAVISTREAM)This->ppStreams[nStream];
323 IAVIStream_AddRef(*avis);
324
325 return AVIERR_OK;
326 }
327
328 /* Sorry, but the specified stream doesn't exist */
329 return AVIERR_NODATA;
330 }
331
332 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile *iface, IAVIStream **avis,
333 AVISTREAMINFOW *asi)
334 {
335 IAVIFileImpl *This = impl_from_IAVIFile(iface);
336 DWORD n;
337
338 TRACE("(%p,%p,%p)\n", iface, avis, asi);
339
340 /* check parameters */
341 if (avis == NULL || asi == NULL)
342 return AVIERR_BADPARAM;
343
344 *avis = NULL;
345
346 /* Does the user have write permission? */
347 if ((This->uMode & MMIO_RWMODE) == 0)
348 return AVIERR_READONLY;
349
350 /* Can we add another stream? */
351 n = This->fInfo.dwStreams;
352 if (n >= MAX_AVISTREAMS || This->dwMoviChunkPos != 0) {
353 /* already reached max nr of streams
354 * or have already written frames to disk */
355 return AVIERR_UNSUPPORTED;
356 }
357
358 /* check AVISTREAMINFO for some really needed things */
359 if (asi->fccType == 0 || asi->dwScale == 0 || asi->dwRate == 0)
360 return AVIERR_BADFORMAT;
361
362 /* now it seems to be save to add the stream */
363 assert(This->ppStreams[n] == NULL);
364 This->ppStreams[n] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
365 sizeof(IAVIStreamImpl));
366 if (This->ppStreams[n] == NULL)
367 return AVIERR_MEMORY;
368
369 /* initialize the new allocated stream */
370 AVIFILE_ConstructAVIStream(This, n, asi);
371
372 This->fInfo.dwStreams++;
373 This->fDirty = TRUE;
374
375 /* update our AVIFILEINFO structure */
376 AVIFILE_UpdateInfo(This);
377
378 /* return it */
379 *avis = (PAVISTREAM)This->ppStreams[n];
380 IAVIStream_AddRef(*avis);
381
382 return AVIERR_OK;
383 }
384
385 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid, void *lpData, LONG size)
386 {
387 IAVIFileImpl *This = impl_from_IAVIFile(iface);
388
389 TRACE("(%p,0x%08X,%p,%d)\n", iface, ckid, lpData, size);
390
391 /* check parameters */
392 if (lpData == NULL)
393 return AVIERR_BADPARAM;
394 if (size < 0)
395 return AVIERR_BADSIZE;
396
397 /* Do we have write permission? */
398 if ((This->uMode & MMIO_RWMODE) == 0)
399 return AVIERR_READONLY;
400
401 This->fDirty = TRUE;
402
403 return WriteExtraChunk(&This->fileextra, ckid, lpData, size);
404 }
405
406 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile *iface, DWORD ckid, void *lpData, LONG *size)
407 {
408 IAVIFileImpl *This = impl_from_IAVIFile(iface);
409
410 TRACE("(%p,0x%08X,%p,%p)\n", iface, ckid, lpData, size);
411
412 return ReadExtraChunk(&This->fileextra, ckid, lpData, size);
413 }
414
415 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface)
416 {
417 IAVIFileImpl *This = impl_from_IAVIFile(iface);
418
419 TRACE("(%p)\n",iface);
420
421 if ((This->uMode & MMIO_RWMODE) == 0)
422 return AVIERR_READONLY;
423
424 This->fDirty = TRUE;
425
426 /* no frames written to any stream? -- compute start of 'movi'-chunk */
427 if (This->dwMoviChunkPos == 0)
428 AVIFILE_ComputeMoviStart(This);
429
430 This->fInfo.dwFlags |= AVIFILEINFO_ISINTERLEAVED;
431
432 /* already written frames to any stream, ... */
433 if (This->ckLastRecord.dwFlags & MMIO_DIRTY) {
434 /* close last record */
435 if (mmioAscend(This->hmmio, &This->ckLastRecord, 0) != 0)
436 return AVIERR_FILEWRITE;
437
438 AVIFILE_AddRecord(This);
439
440 if (This->fInfo.dwSuggestedBufferSize < This->ckLastRecord.cksize + 3 * sizeof(DWORD))
441 This->fInfo.dwSuggestedBufferSize = This->ckLastRecord.cksize + 3 * sizeof(DWORD);
442 }
443
444 /* write out a new record into file, but don't close it */
445 This->ckLastRecord.cksize = 0;
446 This->ckLastRecord.fccType = listtypeAVIRECORD;
447 if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1)
448 return AVIERR_FILEWRITE;
449 if (mmioCreateChunk(This->hmmio, &This->ckLastRecord, MMIO_CREATELIST) != 0)
450 return AVIERR_FILEWRITE;
451 This->dwNextFramePos += 3 * sizeof(DWORD);
452
453 return AVIERR_OK;
454 }
455
456 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType, LONG lParam)
457 {
458 IAVIFileImpl *This = impl_from_IAVIFile(iface);
459 ULONG nStream;
460
461 TRACE("(%p,0x%08X,%d)\n", iface, fccType, lParam);
462
463 /* check parameter */
464 if (lParam < 0)
465 return AVIERR_BADPARAM;
466
467 /* Have user write permissions? */
468 if ((This->uMode & MMIO_RWMODE) == 0)
469 return AVIERR_READONLY;
470
471 nStream = AVIFILE_SearchStream(This, fccType, lParam);
472
473 /* Does the requested stream exist? */
474 if (nStream < This->fInfo.dwStreams &&
475 This->ppStreams[nStream] != NULL) {
476 /* ... so delete it now */
477 HeapFree(GetProcessHeap(), 0, This->ppStreams[nStream]);
478
479 if (This->fInfo.dwStreams - nStream > 0)
480 memcpy(This->ppStreams + nStream, This->ppStreams + nStream + 1,
481 (This->fInfo.dwStreams - nStream) * sizeof(IAVIStreamImpl*));
482
483 This->ppStreams[This->fInfo.dwStreams] = NULL;
484 This->fInfo.dwStreams--;
485 This->fDirty = TRUE;
486
487 /* This->fInfo will be updated further when asked for */
488 return AVIERR_OK;
489 } else
490 return AVIERR_NODATA;
491 }
492
493 static const struct IAVIFileVtbl avif_vt = {
494 IAVIFile_fnQueryInterface,
495 IAVIFile_fnAddRef,
496 IAVIFile_fnRelease,
497 IAVIFile_fnInfo,
498 IAVIFile_fnGetStream,
499 IAVIFile_fnCreateStream,
500 IAVIFile_fnWriteData,
501 IAVIFile_fnReadData,
502 IAVIFile_fnEndRecord,
503 IAVIFile_fnDeleteStream
504 };
505
506
507 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface, REFIID riid, void **ppv)
508 {
509 IAVIFileImpl *This = impl_from_IPersistFile(iface);
510
511 return IUnknown_QueryInterface(This->outer_unk, riid, ppv);
512 }
513
514 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile *iface)
515 {
516 IAVIFileImpl *This = impl_from_IPersistFile(iface);
517
518 return IUnknown_AddRef(This->outer_unk);
519 }
520
521 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile *iface)
522 {
523 IAVIFileImpl *This = impl_from_IPersistFile(iface);
524
525 return IUnknown_Release(This->outer_unk);
526 }
527
528 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface, LPCLSID pClassID)
529 {
530 TRACE("(%p,%p)\n", iface, pClassID);
531
532 if (pClassID == NULL)
533 return AVIERR_BADPARAM;
534
535 *pClassID = CLSID_AVIFile;
536
537 return AVIERR_OK;
538 }
539
540 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface)
541 {
542 IAVIFileImpl *This = impl_from_IPersistFile(iface);
543
544 TRACE("(%p)\n", iface);
545
546 return (This->fDirty ? S_OK : S_FALSE);
547 }
548
549 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface, LPCOLESTR pszFileName, DWORD dwMode)
550 {
551 IAVIFileImpl *This = impl_from_IPersistFile(iface);
552 int len;
553
554 TRACE("(%p,%s,0x%08X)\n", iface, debugstr_w(pszFileName), dwMode);
555
556 /* check parameter */
557 if (pszFileName == NULL)
558 return AVIERR_BADPARAM;
559
560 if (This->hmmio != NULL)
561 return AVIERR_ERROR; /* No reuse of this object for another file! */
562
563 /* remember mode and name */
564 This->uMode = dwMode;
565
566 len = lstrlenW(pszFileName) + 1;
567 This->szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
568 if (This->szFileName == NULL)
569 return AVIERR_MEMORY;
570 lstrcpyW(This->szFileName, pszFileName);
571
572 /* try to open the file */
573 This->hmmio = mmioOpenW(This->szFileName, NULL, MMIO_ALLOCBUF | dwMode);
574 if (This->hmmio == NULL) {
575 /* mmioOpenW not in native DLLs of Win9x -- try mmioOpenA */
576 LPSTR szFileName;
577
578 len = WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, NULL, 0, NULL, NULL);
579 szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(CHAR));
580 if (szFileName == NULL)
581 return AVIERR_MEMORY;
582
583 WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, szFileName, len, NULL, NULL);
584
585 This->hmmio = mmioOpenA(szFileName, NULL, MMIO_ALLOCBUF | dwMode);
586 HeapFree(GetProcessHeap(), 0, szFileName);
587 if (This->hmmio == NULL)
588 return AVIERR_FILEOPEN;
589 }
590
591 /* should we create a new file? */
592 if (dwMode & OF_CREATE) {
593 memset(& This->fInfo, 0, sizeof(This->fInfo));
594 This->fInfo.dwFlags = AVIFILEINFO_HASINDEX | AVIFILEINFO_TRUSTCKTYPE;
595
596 return AVIERR_OK;
597 } else
598 return AVIFILE_LoadFile(This);
599 }
600
601 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface, LPCOLESTR pszFileName,
602 BOOL fRemember)
603 {
604 TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember);
605
606 /* We write directly to disk, so nothing to do. */
607
608 return AVIERR_OK;
609 }
610
611 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface, LPCOLESTR pszFileName)
612 {
613 TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName));
614
615 /* We write directly to disk, so nothing to do. */
616
617 return AVIERR_OK;
618 }
619
620 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface, LPOLESTR *ppszFileName)
621 {
622 IAVIFileImpl *This = impl_from_IPersistFile(iface);
623
624 TRACE("(%p,%p)\n", iface, ppszFileName);
625
626 if (ppszFileName == NULL)
627 return AVIERR_BADPARAM;
628
629 *ppszFileName = NULL;
630
631 if (This->szFileName != NULL) {
632 int len = lstrlenW(This->szFileName) + 1;
633
634 *ppszFileName = CoTaskMemAlloc(len * sizeof(WCHAR));
635 if (*ppszFileName == NULL)
636 return AVIERR_MEMORY;
637
638 strcpyW(*ppszFileName, This->szFileName);
639 }
640
641 return AVIERR_OK;
642 }
643
644 static const struct IPersistFileVtbl pf_vt = {
645 IPersistFile_fnQueryInterface,
646 IPersistFile_fnAddRef,
647 IPersistFile_fnRelease,
648 IPersistFile_fnGetClassID,
649 IPersistFile_fnIsDirty,
650 IPersistFile_fnLoad,
651 IPersistFile_fnSave,
652 IPersistFile_fnSaveCompleted,
653 IPersistFile_fnGetCurFile
654 };
655
656 HRESULT AVIFILE_CreateAVIFile(IUnknown *pUnkOuter, REFIID riid, void **ppv)
657 {
658 IAVIFileImpl *obj;
659 HRESULT hr;
660
661 *ppv = NULL;
662 obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIFileImpl));
663 if (!obj)
664 return AVIERR_MEMORY;
665
666 obj->IUnknown_inner.lpVtbl = &unk_vtbl;
667 obj->IAVIFile_iface.lpVtbl = &avif_vt;
668 obj->IPersistFile_iface.lpVtbl = &pf_vt;
669 obj->ref = 1;
670 if (pUnkOuter)
671 obj->outer_unk = pUnkOuter;
672 else
673 obj->outer_unk = &obj->IUnknown_inner;
674
675 hr = IUnknown_QueryInterface(&obj->IUnknown_inner, riid, ppv);
676 IUnknown_Release(&obj->IUnknown_inner);
677
678 return hr;
679 }
680
681
682 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface,
683 REFIID refiid, LPVOID *obj)
684 {
685 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
686
687 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(refiid), obj);
688
689 if (IsEqualGUID(&IID_IUnknown, refiid) ||
690 IsEqualGUID(&IID_IAVIStream, refiid)) {
691 *obj = This;
692 IAVIStream_AddRef(iface);
693
694 return S_OK;
695 }
696 /* FIXME: IAVIStreaming interface */
697
698 return OLE_E_ENUM_NOMORE;
699 }
700
701 static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface)
702 {
703 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
704 ULONG ref = InterlockedIncrement(&This->ref);
705
706 TRACE("(%p) -> %d\n", iface, ref);
707
708 /* also add ref to parent, so that it doesn't kill us */
709 if (This->paf != NULL)
710 IAVIFile_AddRef(&This->paf->IAVIFile_iface);
711
712 return ref;
713 }
714
715 static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface)
716 {
717 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
718 ULONG ref = InterlockedDecrement(&This->ref);
719
720 TRACE("(%p) -> %d\n", iface, ref);
721
722 if (This->paf != NULL)
723 IAVIFile_Release(&This->paf->IAVIFile_iface);
724
725 return ref;
726 }
727
728 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1,
729 LPARAM lParam2)
730 {
731 TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2);
732
733 /* This IAVIStream interface needs an AVIFile */
734 return AVIERR_UNSUPPORTED;
735 }
736
737 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi,
738 LONG size)
739 {
740 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
741
742 TRACE("(%p,%p,%d)\n", iface, psi, size);
743
744 if (psi == NULL)
745 return AVIERR_BADPARAM;
746 if (size < 0)
747 return AVIERR_BADSIZE;
748
749 memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo)));
750
751 if ((DWORD)size < sizeof(This->sInfo))
752 return AVIERR_BUFFERTOOSMALL;
753 return AVIERR_OK;
754 }
755
756 static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos,
757 LONG flags)
758 {
759 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
760
761 LONG offset = 0;
762
763 TRACE("(%p,%d,0x%08X)\n",iface,pos,flags);
764
765 if (flags & FIND_FROM_START) {
766 pos = This->sInfo.dwStart;
767 flags &= ~(FIND_FROM_START|FIND_PREV);
768 flags |= FIND_NEXT;
769 }
770
771 if (This->sInfo.dwSampleSize != 0) {
772 /* convert samples into block number with offset */
773 AVIFILE_SamplesToBlock(This, &pos, &offset);
774 }
775
776 if (flags & FIND_TYPE) {
777 if (flags & FIND_KEY) {
778 while (0 <= pos && pos <= This->lLastFrame) {
779 if (This->idxFrames[pos].dwFlags & AVIIF_KEYFRAME)
780 goto RETURN_FOUND;
781
782 if (flags & FIND_NEXT)
783 pos++;
784 else
785 pos--;
786 };
787 } else if (flags & FIND_ANY) {
788 while (0 <= pos && pos <= This->lLastFrame) {
789 if (This->idxFrames[pos].dwChunkLength > 0)
790 goto RETURN_FOUND;
791
792 if (flags & FIND_NEXT)
793 pos++;
794 else
795 pos--;
796
797 };
798 } else if ((flags & FIND_FORMAT) && This->idxFmtChanges != NULL &&
799 This->sInfo.fccType == streamtypeVIDEO) {
800 if (flags & FIND_NEXT) {
801 ULONG n;
802
803 for (n = 0; n < This->sInfo.dwFormatChangeCount; n++)
804 if (This->idxFmtChanges[n].ckid >= pos) {
805 pos = This->idxFmtChanges[n].ckid;
806 goto RETURN_FOUND;
807 }
808 } else {
809 LONG n;
810
811 for (n = (LONG)This->sInfo.dwFormatChangeCount; n >= 0; n--) {
812 if (This->idxFmtChanges[n].ckid <= pos) {
813 pos = This->idxFmtChanges[n].ckid;
814 goto RETURN_FOUND;
815 }
816 }
817
818 if (pos > (LONG)This->sInfo.dwStart)
819 return 0; /* format changes always for first frame */
820 }
821 }
822
823 return -1;
824 }
825
826 RETURN_FOUND:
827 if (pos < (LONG)This->sInfo.dwStart)
828 return -1;
829
830 switch (flags & FIND_RET) {
831 case FIND_LENGTH:
832 /* physical size */
833 pos = This->idxFrames[pos].dwChunkLength;
834 break;
835 case FIND_OFFSET:
836 /* physical position */
837 pos = This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD)
838 + offset * This->sInfo.dwSampleSize;
839 break;
840 case FIND_SIZE:
841 /* logical size */
842 if (This->sInfo.dwSampleSize)
843 pos = This->sInfo.dwSampleSize;
844 else
845 pos = 1;
846 break;
847 case FIND_INDEX:
848 FIXME(": FIND_INDEX flag is not supported!\n");
849 /* This is an index in the index-table on disc. */
850 break;
851 }; /* else logical position */
852
853 return pos;
854 }
855
856 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos,
857 LPVOID format, LONG *formatsize)
858 {
859 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
860
861 TRACE("(%p,%d,%p,%p)\n", iface, pos, format, formatsize);
862
863 if (formatsize == NULL)
864 return AVIERR_BADPARAM;
865
866 /* only interested in needed buffersize? */
867 if (format == NULL || *formatsize <= 0) {
868 *formatsize = This->cbFormat;
869
870 return AVIERR_OK;
871 }
872
873 /* copy initial format (only as much as will fit) */
874 memcpy(format, This->lpFormat, min(*(DWORD*)formatsize, This->cbFormat));
875 if (*(DWORD*)formatsize < This->cbFormat) {
876 *formatsize = This->cbFormat;
877 return AVIERR_BUFFERTOOSMALL;
878 }
879
880 /* Could format change? When yes will it change? */
881 if ((This->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
882 pos > This->sInfo.dwStart) {
883 LONG lLastFmt;
884
885 lLastFmt = IAVIStream_fnFindSample(iface, pos, FIND_FORMAT|FIND_PREV);
886 if (lLastFmt > 0) {
887 FIXME(": need to read formatchange for %d -- unimplemented!\n",lLastFmt);
888 }
889 }
890
891 *formatsize = This->cbFormat;
892 return AVIERR_OK;
893 }
894
895 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream *iface, LONG pos,
896 LPVOID format, LONG formatsize)
897 {
898 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
899
900 LPBITMAPINFOHEADER lpbiNew = format;
901
902 TRACE("(%p,%d,%p,%d)\n", iface, pos, format, formatsize);
903
904 /* check parameters */
905 if (format == NULL || formatsize <= 0)
906 return AVIERR_BADPARAM;
907
908 /* Do we have write permission? */
909 if ((This->paf->uMode & MMIO_RWMODE) == 0)
910 return AVIERR_READONLY;
911
912 /* can only set format before frame is written! */
913 if (This->lLastFrame > pos)
914 return AVIERR_UNSUPPORTED;
915
916 /* initial format or a formatchange? */
917 if (This->lpFormat == NULL) {
918 /* initial format */
919 if (This->paf->dwMoviChunkPos != 0)
920 return AVIERR_ERROR; /* user has used API in wrong sequence! */
921
922 This->lpFormat = HeapAlloc(GetProcessHeap(), 0, formatsize);
923 if (This->lpFormat == NULL)
924 return AVIERR_MEMORY;
925 This->cbFormat = formatsize;
926
927 memcpy(This->lpFormat, format, formatsize);
928
929 /* update some infos about stream */
930 if (This->sInfo.fccType == streamtypeVIDEO) {
931 LONG lDim;
932
933 lDim = This->sInfo.rcFrame.right - This->sInfo.rcFrame.left;
934 if (lDim < lpbiNew->biWidth)
935 This->sInfo.rcFrame.right = This->sInfo.rcFrame.left + lpbiNew->biWidth;
936 lDim = This->sInfo.rcFrame.bottom - This->sInfo.rcFrame.top;
937 if (lDim < lpbiNew->biHeight)
938 This->sInfo.rcFrame.bottom = This->sInfo.rcFrame.top + lpbiNew->biHeight;
939 } else if (This->sInfo.fccType == streamtypeAUDIO)
940 This->sInfo.dwSampleSize = ((LPWAVEFORMATEX)This->lpFormat)->nBlockAlign;
941
942 return AVIERR_OK;
943 } else {
944 MMCKINFO ck;
945 LPBITMAPINFOHEADER lpbiOld = This->lpFormat;
946 RGBQUAD *rgbNew = (RGBQUAD*)((LPBYTE)lpbiNew + lpbiNew->biSize);
947 AVIPALCHANGE *lppc = NULL;
948 UINT n;
949
950 /* perhaps format change, check it ... */
951 if (This->cbFormat != formatsize)
952 return AVIERR_UNSUPPORTED;
953
954 /* no format change, only the initial one */
955 if (memcmp(This->lpFormat, format, formatsize) == 0)
956 return AVIERR_OK;
957
958 /* check that's only the palette, which changes */
959 if (lpbiOld->biSize != lpbiNew->biSize ||
960 lpbiOld->biWidth != lpbiNew->biWidth ||
961 lpbiOld->biHeight != lpbiNew->biHeight ||
962 lpbiOld->biPlanes != lpbiNew->biPlanes ||
963 lpbiOld->biBitCount != lpbiNew->biBitCount ||
964 lpbiOld->biCompression != lpbiNew->biCompression ||
965 lpbiOld->biClrUsed != lpbiNew->biClrUsed)
966 return AVIERR_UNSUPPORTED;
967
968 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
969
970 /* simply say all colors have changed */
971 ck.ckid = MAKEAVICKID(cktypePALchange, This->nStream);
972 ck.cksize = 2 * sizeof(WORD) + lpbiOld->biClrUsed * sizeof(PALETTEENTRY);
973 lppc = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
974 if (lppc == NULL)
975 return AVIERR_MEMORY;
976
977 lppc->bFirstEntry = 0;
978 lppc->bNumEntries = (lpbiOld->biClrUsed < 256 ? lpbiOld->biClrUsed : 0);
979 lppc->wFlags = 0;
980 for (n = 0; n < lpbiOld->biClrUsed; n++) {
981 lppc->peNew[n].peRed = rgbNew[n].rgbRed;
982 lppc->peNew[n].peGreen = rgbNew[n].rgbGreen;
983 lppc->peNew[n].peBlue = rgbNew[n].rgbBlue;
984 lppc->peNew[n].peFlags = 0;
985 }
986
987 if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1 ||
988 mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK ||
989 mmioWrite(This->paf->hmmio, (HPSTR)lppc, ck.cksize) != ck.cksize ||
990 mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
991 {
992 HeapFree(GetProcessHeap(), 0, lppc);
993 return AVIERR_FILEWRITE;
994 }
995
996 This->paf->dwNextFramePos += ck.cksize + 2 * sizeof(DWORD);
997
998 HeapFree(GetProcessHeap(), 0, lppc);
999
1000 return AVIFILE_AddFrame(This, cktypePALchange, n, ck.dwDataOffset, 0);
1001 }
1002 }
1003
1004 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start,
1005 LONG samples, LPVOID buffer,
1006 LONG buffersize, LPLONG bytesread,
1007 LPLONG samplesread)
1008 {
1009 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
1010
1011 DWORD size;
1012 HRESULT hr;
1013
1014 TRACE("(%p,%d,%d,%p,%d,%p,%p)\n", iface, start, samples, buffer,
1015 buffersize, bytesread, samplesread);
1016
1017 /* clear return parameters if given */
1018 if (bytesread != NULL)
1019 *bytesread = 0;
1020 if (samplesread != NULL)
1021 *samplesread = 0;
1022
1023 /* check parameters */
1024 if ((LONG)This->sInfo.dwStart > start)
1025 return AVIERR_NODATA; /* couldn't read before start of stream */
1026 if (This->sInfo.dwStart + This->sInfo.dwLength < (DWORD)start)
1027 return AVIERR_NODATA; /* start is past end of stream */
1028
1029 /* should we read as much as possible? */
1030 if (samples == -1) {
1031 /* User should know how much we have read */
1032 if (bytesread == NULL && samplesread == NULL)
1033 return AVIERR_BADPARAM;
1034
1035 if (This->sInfo.dwSampleSize != 0)
1036 samples = buffersize / This->sInfo.dwSampleSize;
1037 else
1038 samples = 1;
1039 }
1040
1041 /* limit to end of stream */
1042 if ((LONG)This->sInfo.dwLength < samples)
1043 samples = This->sInfo.dwLength;
1044 if ((start - This->sInfo.dwStart) > (This->sInfo.dwLength - samples))
1045 samples = This->sInfo.dwLength - (start - This->sInfo.dwStart);
1046
1047 /* nothing to read? Then leave ... */
1048 if (samples == 0)
1049 return AVIERR_OK;
1050
1051 if (This->sInfo.dwSampleSize != 0) {
1052 /* fixed samplesize -- we can read over frame/block boundaries */
1053 LONG block = start;
1054 LONG offset = 0;
1055
1056 if (!buffer)
1057 {
1058 if (bytesread)
1059 *bytesread = samples*This->sInfo.dwSampleSize;
1060 if (samplesread)
1061 *samplesread = samples;
1062 return AVIERR_OK;
1063 }
1064
1065 /* convert start sample to block,offset pair */
1066 AVIFILE_SamplesToBlock(This, &block, &offset);
1067
1068 /* convert samples to bytes */
1069 samples *= This->sInfo.dwSampleSize;
1070
1071 while (samples > 0 && buffersize > 0) {
1072 LONG blocksize;
1073 if (block != This->dwCurrentFrame) {
1074 hr = AVIFILE_ReadBlock(This, block, NULL, 0);
1075 if (FAILED(hr))
1076 return hr;
1077 }
1078
1079 size = min((DWORD)samples, (DWORD)buffersize);
1080 blocksize = This->lpBuffer[1];
1081 TRACE("blocksize = %u\n",blocksize);
1082 size = min(size, blocksize - offset);
1083 memcpy(buffer, ((BYTE*)&This->lpBuffer[2]) + offset, size);
1084
1085 block++;
1086 offset = 0;
1087 buffer = ((LPBYTE)buffer)+size;
1088 samples -= size;
1089 buffersize -= size;
1090
1091 /* fill out return parameters if given */
1092 if (bytesread != NULL)
1093 *bytesread += size;
1094 if (samplesread != NULL)
1095 *samplesread += size / This->sInfo.dwSampleSize;
1096 }
1097
1098 if (samples == 0)
1099 return AVIERR_OK;
1100 else
1101 return AVIERR_BUFFERTOOSMALL;
1102 } else {
1103 /* variable samplesize -- we can only read one full frame/block */
1104 if (samples > 1)
1105 samples = 1;
1106
1107 assert(start <= This->lLastFrame);
1108 size = This->idxFrames[start].dwChunkLength;
1109 if (buffer != NULL && buffersize >= size) {
1110 hr = AVIFILE_ReadBlock(This, start, buffer, size);
1111 if (FAILED(hr))
1112 return hr;
1113 } else if (buffer != NULL)
1114 return AVIERR_BUFFERTOOSMALL;
1115
1116 /* fill out return parameters if given */
1117 if (bytesread != NULL)
1118 *bytesread = size;
1119 if (samplesread != NULL)
1120 *samplesread = samples;
1121
1122 return AVIERR_OK;
1123 }
1124 }
1125
1126 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream *iface, LONG start,
1127 LONG samples, LPVOID buffer,
1128 LONG buffersize, DWORD flags,
1129 LPLONG sampwritten,
1130 LPLONG byteswritten)
1131 {
1132 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
1133
1134 FOURCC ckid;
1135 HRESULT hr;
1136
1137 TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n", iface, start, samples,
1138 buffer, buffersize, flags, sampwritten, byteswritten);
1139
1140 /* clear return parameters if given */
1141 if (sampwritten != NULL)
1142 *sampwritten = 0;
1143 if (byteswritten != NULL)
1144 *byteswritten = 0;
1145
1146 /* check parameters */
1147 if (buffer == NULL && (buffersize > 0 || samples > 0))
1148 return AVIERR_BADPARAM;
1149
1150 /* Have we write permission? */
1151 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1152 return AVIERR_READONLY;
1153
1154 switch (This->sInfo.fccType) {
1155 case streamtypeAUDIO:
1156 ckid = MAKEAVICKID(cktypeWAVEbytes, This->nStream);
1157 break;
1158 default:
1159 if ((flags & AVIIF_KEYFRAME) && buffersize != 0)
1160 ckid = MAKEAVICKID(cktypeDIBbits, This->nStream);
1161 else
1162 ckid = MAKEAVICKID(cktypeDIBcompressed, This->nStream);
1163 break;
1164 };
1165
1166 /* append to end of stream? */
1167 if (start == -1) {
1168 if (This->lLastFrame == -1)
1169 start = This->sInfo.dwStart;
1170 else
1171 start = This->sInfo.dwLength;
1172 } else if (This->lLastFrame == -1)
1173 This->sInfo.dwStart = start;
1174
1175 if (This->sInfo.dwSampleSize != 0) {
1176 /* fixed sample size -- audio like */
1177 if (samples * This->sInfo.dwSampleSize != buffersize)
1178 return AVIERR_BADPARAM;
1179
1180 /* Couldn't skip audio-like data -- User must supply appropriate silence */
1181 if (This->sInfo.dwLength != start)
1182 return AVIERR_UNSUPPORTED;
1183
1184 /* Convert position to frame/block */
1185 start = This->lLastFrame + 1;
1186
1187 if ((This->paf->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) == 0) {
1188 FIXME(": not interleaved, could collect audio data!\n");
1189 }
1190 } else {
1191 /* variable sample size -- video like */
1192 if (samples > 1)
1193 return AVIERR_UNSUPPORTED;
1194
1195 /* must we fill up with empty frames? */
1196 if (This->lLastFrame != -1) {
1197 FOURCC ckid2 = MAKEAVICKID(cktypeDIBcompressed, This->nStream);
1198
1199 while (start > This->lLastFrame + 1) {
1200 hr = AVIFILE_WriteBlock(This, This->lLastFrame + 1, ckid2, 0, NULL, 0);
1201 if (FAILED(hr))
1202 return hr;
1203 }
1204 }
1205 }
1206
1207 /* write the block now */
1208 hr = AVIFILE_WriteBlock(This, start, ckid, flags, buffer, buffersize);
1209 if (SUCCEEDED(hr)) {
1210 /* fill out return parameters if given */
1211 if (sampwritten != NULL)
1212 *sampwritten = samples;
1213 if (byteswritten != NULL)
1214 *byteswritten = buffersize;
1215 }
1216
1217 return hr;
1218 }
1219
1220 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream *iface, LONG start,
1221 LONG samples)
1222 {
1223 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
1224
1225 FIXME("(%p,%d,%d): stub\n", iface, start, samples);
1226
1227 /* check parameters */
1228 if (start < 0 || samples < 0)
1229 return AVIERR_BADPARAM;
1230
1231 /* Delete before start of stream? */
1232 if (start + samples < This->sInfo.dwStart)
1233 return AVIERR_OK;
1234
1235 /* Delete after end of stream? */
1236 if (start > This->sInfo.dwLength)
1237 return AVIERR_OK;
1238
1239 /* For the rest we need write permissions */
1240 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1241 return AVIERR_READONLY;
1242
1243 /* 1. overwrite the data with JUNK
1244 *
1245 * if ISINTERLEAVED {
1246 * 2. concat all neighboured JUNK-blocks in this record to one
1247 * 3. if this record only contains JUNK and is at end set dwNextFramePos
1248 * to start of this record, repeat this.
1249 * } else {
1250 * 2. concat all neighboured JUNK-blocks.
1251 * 3. if the JUNK block is at the end, then set dwNextFramePos to
1252 * start of this block.
1253 * }
1254 */
1255
1256 return AVIERR_UNSUPPORTED;
1257 }
1258
1259 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream *iface, DWORD fcc,
1260 LPVOID lp, LPLONG lpread)
1261 {
1262 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
1263
1264 TRACE("(%p,0x%08X,%p,%p)\n", iface, fcc, lp, lpread);
1265
1266 if (fcc == ckidSTREAMHANDLERDATA) {
1267 if (This->lpHandlerData != NULL && This->cbHandlerData > 0) {
1268 if (lp == NULL || *lpread <= 0) {
1269 *lpread = This->cbHandlerData;
1270 return AVIERR_OK;
1271 }
1272
1273 memcpy(lp, This->lpHandlerData, min(This->cbHandlerData, *lpread));
1274 if (*lpread < This->cbHandlerData)
1275 return AVIERR_BUFFERTOOSMALL;
1276 return AVIERR_OK;
1277 } else
1278 return AVIERR_NODATA;
1279 } else
1280 return ReadExtraChunk(&This->extra, fcc, lp, lpread);
1281 }
1282
1283 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream *iface, DWORD fcc,
1284 LPVOID lp, LONG size)
1285 {
1286 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
1287
1288 TRACE("(%p,0x%08x,%p,%d)\n", iface, fcc, lp, size);
1289
1290 /* check parameters */
1291 if (lp == NULL)
1292 return AVIERR_BADPARAM;
1293 if (size <= 0)
1294 return AVIERR_BADSIZE;
1295
1296 /* need write permission */
1297 if ((This->paf->uMode & MMIO_RWMODE) == 0)
1298 return AVIERR_READONLY;
1299
1300 /* already written something to this file? */
1301 if (This->paf->dwMoviChunkPos != 0) {
1302 /* the data will be inserted before the 'movi' chunk, so check for
1303 * enough space */
1304 DWORD dwPos = AVIFILE_ComputeMoviStart(This->paf);
1305
1306 /* ckid,size => 2 * sizeof(DWORD) */
1307 dwPos += 2 * sizeof(DWORD) + size;
1308 if (dwPos >= This->paf->dwMoviChunkPos - 2 * sizeof(DWORD))
1309 return AVIERR_UNSUPPORTED; /* not enough space left */
1310 }
1311
1312 This->paf->fDirty = TRUE;
1313
1314 if (fcc == ckidSTREAMHANDLERDATA) {
1315 if (This->lpHandlerData != NULL) {
1316 FIXME(": handler data already set -- overwirte?\n");
1317 return AVIERR_UNSUPPORTED;
1318 }
1319
1320 This->lpHandlerData = HeapAlloc(GetProcessHeap(), 0, size);
1321 if (This->lpHandlerData == NULL)
1322 return AVIERR_MEMORY;
1323 This->cbHandlerData = size;
1324 memcpy(This->lpHandlerData, lp, size);
1325
1326 return AVIERR_OK;
1327 } else
1328 return WriteExtraChunk(&This->extra, fcc, lp, size);
1329 }
1330
1331 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream *iface,
1332 LPAVISTREAMINFOW info, LONG infolen)
1333 {
1334 FIXME("(%p,%p,%d): stub\n", iface, info, infolen);
1335
1336 return E_FAIL;
1337 }
1338
1339 /***********************************************************************/
1340
1341 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, DWORD offset, DWORD flags)
1342 {
1343 UINT n;
1344
1345 /* pre-conditions */
1346 assert(This != NULL);
1347
1348 switch (TWOCCFromFOURCC(ckid)) {
1349 case cktypeDIBbits:
1350 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1351 flags |= AVIIF_KEYFRAME;
1352 break;
1353 case cktypeDIBcompressed:
1354 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1355 flags &= ~AVIIF_KEYFRAME;
1356 break;
1357 case cktypePALchange:
1358 if (This->sInfo.fccType != streamtypeVIDEO) {
1359 ERR(": found palette change in non-video stream!\n");
1360 return AVIERR_BADFORMAT;
1361 }
1362
1363 if (This->idxFmtChanges == NULL || This->nIdxFmtChanges <= This->sInfo.dwFormatChangeCount) {
1364 DWORD new_count = This->nIdxFmtChanges + 16;
1365 void *new_buffer;
1366
1367 if (This->idxFmtChanges == NULL) {
1368 This->idxFmtChanges =
1369 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, new_count * sizeof(AVIINDEXENTRY));
1370 if (!This->idxFmtChanges) return AVIERR_MEMORY;
1371 } else {
1372 new_buffer = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxFmtChanges,
1373 new_count * sizeof(AVIINDEXENTRY));
1374 if (!new_buffer) return AVIERR_MEMORY;
1375 This->idxFmtChanges = new_buffer;
1376 }
1377 This->nIdxFmtChanges = new_count;
1378 }
1379
1380 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
1381 n = ++This->sInfo.dwFormatChangeCount;
1382 This->idxFmtChanges[n].ckid = This->lLastFrame;
1383 This->idxFmtChanges[n].dwFlags = 0;
1384 This->idxFmtChanges[n].dwChunkOffset = offset;
1385 This->idxFmtChanges[n].dwChunkLength = size;
1386
1387 return AVIERR_OK;
1388 case cktypeWAVEbytes:
1389 if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
1390 flags |= AVIIF_KEYFRAME;
1391 break;
1392 default:
1393 WARN(": unknown TWOCC 0x%04X found\n", TWOCCFromFOURCC(ckid));
1394 break;
1395 };
1396
1397 /* first frame is always a keyframe */
1398 if (This->lLastFrame == -1)
1399 flags |= AVIIF_KEYFRAME;
1400
1401 if (This->sInfo.dwSuggestedBufferSize < size)
1402 This->sInfo.dwSuggestedBufferSize = size;
1403
1404 /* get memory for index */
1405 if (This->idxFrames == NULL || This->lLastFrame + 1 >= This->nIdxFrames) {
1406 This->nIdxFrames += 512;
1407 if (This->idxFrames == NULL)
1408 This->idxFrames = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->nIdxFrames * sizeof(AVIINDEXENTRY));
1409 else
1410 This->idxFrames = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxFrames,
1411 This->nIdxFrames * sizeof(AVIINDEXENTRY));
1412 if (This->idxFrames == NULL)
1413 return AVIERR_MEMORY;
1414 }
1415
1416 This->lLastFrame++;
1417 This->idxFrames[This->lLastFrame].ckid = ckid;
1418 This->idxFrames[This->lLastFrame].dwFlags = flags;
1419 This->idxFrames[This->lLastFrame].dwChunkOffset = offset;
1420 This->idxFrames[This->lLastFrame].dwChunkLength = size;
1421
1422 /* update AVISTREAMINFO structure if necessary */
1423 if (This->sInfo.dwLength <= This->lLastFrame)
1424 This->sInfo.dwLength = This->lLastFrame + 1;
1425
1426 return AVIERR_OK;
1427 }
1428
1429 static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This)
1430 {
1431 /* pre-conditions */
1432 assert(This != NULL && This->ppStreams[0] != NULL);
1433
1434 if (This->idxRecords == NULL || This->cbIdxRecords == 0) {
1435 This->cbIdxRecords += 1024 * sizeof(AVIINDEXENTRY);
1436 This->idxRecords = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->cbIdxRecords);
1437 if (This->idxRecords == NULL)
1438 return AVIERR_MEMORY;
1439 }
1440
1441 assert(This->nIdxRecords < This->cbIdxRecords/sizeof(AVIINDEXENTRY));
1442
1443 This->idxRecords[This->nIdxRecords].ckid = listtypeAVIRECORD;
1444 This->idxRecords[This->nIdxRecords].dwFlags = AVIIF_LIST;
1445 This->idxRecords[This->nIdxRecords].dwChunkOffset =
1446 This->ckLastRecord.dwDataOffset - 2 * sizeof(DWORD);
1447 This->idxRecords[This->nIdxRecords].dwChunkLength =
1448 This->ckLastRecord.cksize;
1449 This->nIdxRecords++;
1450
1451 return AVIERR_OK;
1452 }
1453
1454 static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This)
1455 {
1456 DWORD dwPos;
1457 DWORD nStream;
1458
1459 /* RIFF,hdrl,movi,avih => (3 * 3 + 2) * sizeof(DWORD) = 11 * sizeof(DWORD) */
1460 dwPos = 11 * sizeof(DWORD) + sizeof(MainAVIHeader);
1461
1462 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
1463 IAVIStreamImpl *pStream = This->ppStreams[nStream];
1464
1465 /* strl,strh,strf => (3 + 2 * 2) * sizeof(DWORD) = 7 * sizeof(DWORD) */
1466 dwPos += 7 * sizeof(DWORD) + sizeof(AVIStreamHeader);
1467 dwPos += ((pStream->cbFormat + 1) & ~1U);
1468 if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0)
1469 dwPos += 2 * sizeof(DWORD) + ((pStream->cbHandlerData + 1) & ~1U);
1470 if (lstrlenW(pStream->sInfo.szName) > 0)
1471 dwPos += 2 * sizeof(DWORD) + ((lstrlenW(pStream->sInfo.szName) + 1) & ~1U);
1472 }
1473
1474 if (This->dwMoviChunkPos == 0) {
1475 This->dwNextFramePos = dwPos;
1476
1477 /* pad to multiple of AVI_HEADERSIZE only if we are more than 8 bytes away from it */
1478 if (((dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1)) - dwPos > 2 * sizeof(DWORD))
1479 This->dwNextFramePos = (dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1);
1480
1481 This->dwMoviChunkPos = This->dwNextFramePos - sizeof(DWORD);
1482 }
1483
1484 return dwPos;
1485 }
1486
1487 static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr, const AVISTREAMINFOW *asi)
1488 {
1489 IAVIStreamImpl *pstream;
1490
1491 /* pre-conditions */
1492 assert(paf != NULL);
1493 assert(nr < MAX_AVISTREAMS);
1494 assert(paf->ppStreams[nr] != NULL);
1495
1496 pstream = paf->ppStreams[nr];
1497
1498 pstream->lpVtbl = &iavist;
1499 pstream->ref = 0;
1500 pstream->paf = paf;
1501 pstream->nStream = nr;
1502 pstream->dwCurrentFrame = (DWORD)-1;
1503 pstream->lLastFrame = -1;
1504
1505 if (asi != NULL) {
1506 memcpy(&pstream->sInfo, asi, sizeof(pstream->sInfo));
1507
1508 if (asi->dwLength > 0) {
1509 /* pre-allocate mem for frame-index structure */
1510 pstream->idxFrames =
1511 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, asi->dwLength * sizeof(AVIINDEXENTRY));
1512 if (pstream->idxFrames != NULL)
1513 pstream->nIdxFrames = asi->dwLength;
1514 }
1515 if (asi->dwFormatChangeCount > 0) {
1516 /* pre-allocate mem for formatchange-index structure */
1517 pstream->idxFmtChanges =
1518 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, asi->dwFormatChangeCount * sizeof(AVIINDEXENTRY));
1519 if (pstream->idxFmtChanges != NULL)
1520 pstream->nIdxFmtChanges = asi->dwFormatChangeCount;
1521 }
1522
1523 /* These values will be computed */
1524 pstream->sInfo.dwLength = 0;
1525 pstream->sInfo.dwSuggestedBufferSize = 0;
1526 pstream->sInfo.dwFormatChangeCount = 0;
1527 pstream->sInfo.dwEditCount = 1;
1528 if (pstream->sInfo.dwSampleSize > 0)
1529 SetRectEmpty(&pstream->sInfo.rcFrame);
1530 }
1531
1532 pstream->sInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
1533 }
1534
1535 static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This)
1536 {
1537 /* pre-conditions */
1538 assert(This != NULL);
1539
1540 This->dwCurrentFrame = (DWORD)-1;
1541 This->lLastFrame = -1;
1542 This->paf = NULL;
1543 if (This->idxFrames != NULL) {
1544 HeapFree(GetProcessHeap(), 0, This->idxFrames);
1545 This->idxFrames = NULL;
1546 This->nIdxFrames = 0;
1547 }
1548 HeapFree(GetProcessHeap(), 0, This->idxFmtChanges);
1549 This->idxFmtChanges = NULL;
1550 if (This->lpBuffer != NULL) {
1551 HeapFree(GetProcessHeap(), 0, This->lpBuffer);
1552 This->lpBuffer = NULL;
1553 This->cbBuffer = 0;
1554 }
1555 if (This->lpHandlerData != NULL) {
1556 HeapFree(GetProcessHeap(), 0, This->lpHandlerData);
1557 This->lpHandlerData = NULL;
1558 This->cbHandlerData = 0;
1559 }
1560 if (This->extra.lp != NULL) {
1561 HeapFree(GetProcessHeap(), 0, This->extra.lp);
1562 This->extra.lp = NULL;
1563 This->extra.cb = 0;
1564 }
1565 if (This->lpFormat != NULL) {
1566 HeapFree(GetProcessHeap(), 0, This->lpFormat);
1567 This->lpFormat = NULL;
1568 This->cbFormat = 0;
1569 }
1570 }
1571
1572 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This)
1573 {
1574 MainAVIHeader MainAVIHdr;
1575 MMCKINFO ckRIFF;
1576 MMCKINFO ckLIST1;
1577 MMCKINFO ckLIST2;
1578 MMCKINFO ck;
1579 IAVIStreamImpl *pStream;
1580 DWORD nStream;
1581 HRESULT hr;
1582
1583 if (This->hmmio == NULL)
1584 return AVIERR_FILEOPEN;
1585
1586 /* initialize stream ptr's */
1587 memset(This->ppStreams, 0, sizeof(This->ppStreams));
1588
1589 /* try to get "RIFF" chunk -- must not be at beginning of file! */
1590 ckRIFF.fccType = formtypeAVI;
1591 if (mmioDescend(This->hmmio, &ckRIFF, NULL, MMIO_FINDRIFF) != S_OK) {
1592 ERR(": not an AVI!\n");
1593 return AVIERR_FILEREAD;
1594 }
1595
1596 /* get "LIST" "hdrl" */
1597 ckLIST1.fccType = listtypeAVIHEADER;
1598 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF, MMIO_FINDLIST);
1599 if (FAILED(hr))
1600 return hr;
1601
1602 /* get "avih" chunk */
1603 ck.ckid = ckidAVIMAINHDR;
1604 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, MMIO_FINDCHUNK);
1605 if (FAILED(hr))
1606 return hr;
1607
1608 if (ck.cksize != sizeof(MainAVIHdr)) {
1609 ERR(": invalid size of %d for MainAVIHeader!\n", ck.cksize);
1610 return AVIERR_BADFORMAT;
1611 }
1612 if (mmioRead(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
1613 return AVIERR_FILEREAD;
1614
1615 /* check for MAX_AVISTREAMS limit */
1616 if (MainAVIHdr.dwStreams > MAX_AVISTREAMS) {
1617 WARN("file contains %u streams, but only supports %d -- change MAX_AVISTREAMS!\n", MainAVIHdr.dwStreams, MAX_AVISTREAMS);
1618 return AVIERR_UNSUPPORTED;
1619 }
1620
1621 /* adjust permissions if copyrighted material in file */
1622 if (MainAVIHdr.dwFlags & AVIFILEINFO_COPYRIGHTED) {
1623 This->uMode &= ~MMIO_RWMODE;
1624 This->uMode |= MMIO_READ;
1625 }
1626
1627 /* convert MainAVIHeader into AVIFILINFOW */
1628 memset(&This->fInfo, 0, sizeof(This->fInfo));
1629 This->fInfo.dwRate = MainAVIHdr.dwMicroSecPerFrame;
1630 This->fInfo.dwScale = 1000000;
1631 This->fInfo.dwMaxBytesPerSec = MainAVIHdr.dwMaxBytesPerSec;
1632 This->fInfo.dwFlags = MainAVIHdr.dwFlags;
1633 This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
1634 This->fInfo.dwLength = MainAVIHdr.dwTotalFrames;
1635 This->fInfo.dwStreams = MainAVIHdr.dwStreams;
1636 This->fInfo.dwSuggestedBufferSize = 0;
1637 This->fInfo.dwWidth = MainAVIHdr.dwWidth;
1638 This->fInfo.dwHeight = MainAVIHdr.dwHeight;
1639 LoadStringW(AVIFILE_hModule, IDS_AVIFILETYPE, This->fInfo.szFileType,
1640 sizeof(This->fInfo.szFileType)/sizeof(This->fInfo.szFileType[0]));
1641
1642 /* go back to into header list */
1643 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1644 return AVIERR_FILEREAD;
1645
1646 /* foreach stream exists a "LIST","strl" chunk */
1647 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
1648 /* get next nested chunk in this "LIST","strl" */
1649 if (mmioDescend(This->hmmio, &ckLIST2, &ckLIST1, 0) != S_OK)
1650 return AVIERR_FILEREAD;
1651
1652 /* nested chunk must be of type "LIST","strl" -- when not normally JUNK */
1653 if (ckLIST2.ckid == FOURCC_LIST &&
1654 ckLIST2.fccType == listtypeSTREAMHEADER) {
1655 pStream = This->ppStreams[nStream] =
1656 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIStreamImpl));
1657 if (pStream == NULL)
1658 return AVIERR_MEMORY;
1659 AVIFILE_ConstructAVIStream(This, nStream, NULL);
1660
1661 ck.ckid = 0;
1662 while (mmioDescend(This->hmmio, &ck, &ckLIST2, 0) == S_OK) {
1663 switch (ck.ckid) {
1664 case ckidSTREAMHANDLERDATA:
1665 if (pStream->lpHandlerData != NULL)
1666 return AVIERR_BADFORMAT;
1667 pStream->lpHandlerData = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
1668 if (pStream->lpHandlerData == NULL)
1669 return AVIERR_MEMORY;
1670 pStream->cbHandlerData = ck.cksize;
1671
1672 if (mmioRead(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize)
1673 return AVIERR_FILEREAD;
1674 break;
1675 case ckidSTREAMFORMAT:
1676 if (pStream->lpFormat != NULL)
1677 return AVIERR_BADFORMAT;
1678 if (ck.cksize == 0)
1679 break;
1680
1681 pStream->lpFormat = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
1682 if (pStream->lpFormat == NULL)
1683 return AVIERR_MEMORY;
1684 pStream->cbFormat = ck.cksize;
1685
1686 if (mmioRead(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize)
1687 return AVIERR_FILEREAD;
1688
1689 if (pStream->sInfo.fccType == streamtypeVIDEO) {
1690 LPBITMAPINFOHEADER lpbi = pStream->lpFormat;
1691
1692 /* some corrections to the video format */
1693 if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
1694 lpbi->biClrUsed = 1u << lpbi->biBitCount;
1695 if (lpbi->biCompression == BI_RGB && lpbi->biSizeImage == 0)
1696 lpbi->biSizeImage = DIBWIDTHBYTES(*lpbi) * lpbi->biHeight;
1697 if (lpbi->biCompression != BI_RGB && lpbi->biBitCount == 8) {
1698 if (pStream->sInfo.fccHandler == mmioFOURCC('R','L','E','0') ||
1699 pStream->sInfo.fccHandler == mmioFOURCC('R','L','E',' '))
1700 lpbi->biCompression = BI_RLE8;
1701 }
1702 if (lpbi->biCompression == BI_RGB &&
1703 (pStream->sInfo.fccHandler == 0 ||
1704 pStream->sInfo.fccHandler == mmioFOURCC('N','O','N','E')))
1705 pStream->sInfo.fccHandler = comptypeDIB;
1706
1707 /* init rcFrame if it's empty */
1708 if (IsRectEmpty(&pStream->sInfo.rcFrame))
1709 SetRect(&pStream->sInfo.rcFrame, 0, 0, lpbi->biWidth, lpbi->biHeight);
1710 }
1711 break;
1712 case ckidSTREAMHEADER:
1713 {
1714 static const WCHAR streamTypeFmt[] = {'%','4','.','4','h','s',0};
1715 static const WCHAR streamNameFmt[] = {'%','s',' ','%','s',' ','#','%','d',0};
1716
1717 AVIStreamHeader streamHdr;
1718 WCHAR szType[25];
1719 UINT count;
1720 LONG n = ck.cksize;
1721
1722 if (ck.cksize > sizeof(streamHdr))
1723 n = sizeof(streamHdr);
1724
1725 if (mmioRead(This->hmmio, (HPSTR)&streamHdr, n) != n)
1726 return AVIERR_FILEREAD;
1727
1728 pStream->sInfo.fccType = streamHdr.fccType;
1729 pStream->sInfo.fccHandler = streamHdr.fccHandler;
1730 pStream->sInfo.dwFlags = streamHdr.dwFlags;
1731 pStream->sInfo.wPriority = streamHdr.wPriority;
1732 pStream->sInfo.wLanguage = streamHdr.wLanguage;
1733 pStream->sInfo.dwInitialFrames = streamHdr.dwInitialFrames;
1734 pStream->sInfo.dwScale = streamHdr.dwScale;
1735 pStream->sInfo.dwRate = streamHdr.dwRate;
1736 pStream->sInfo.dwStart = streamHdr.dwStart;
1737 pStream->sInfo.dwLength = streamHdr.dwLength;
1738 pStream->sInfo.dwSuggestedBufferSize = 0;
1739 pStream->sInfo.dwQuality = streamHdr.dwQuality;
1740 pStream->sInfo.dwSampleSize = streamHdr.dwSampleSize;
1741 pStream->sInfo.rcFrame.left = streamHdr.rcFrame.left;
1742 pStream->sInfo.rcFrame.top = streamHdr.rcFrame.top;
1743 pStream->sInfo.rcFrame.right = streamHdr.rcFrame.right;
1744 pStream->sInfo.rcFrame.bottom = streamHdr.rcFrame.bottom;
1745 pStream->sInfo.dwEditCount = 0;
1746 pStream->sInfo.dwFormatChangeCount = 0;
1747
1748 /* generate description for stream like "filename.avi Type #n" */
1749 if (streamHdr.fccType == streamtypeVIDEO)
1750 LoadStringW(AVIFILE_hModule, IDS_VIDEO, szType, sizeof(szType)/sizeof(szType[0]));
1751 else if (streamHdr.fccType == streamtypeAUDIO)
1752 LoadStringW(AVIFILE_hModule, IDS_AUDIO, szType, sizeof(szType)/sizeof(szType[0]));
1753 else
1754 wsprintfW(szType, streamTypeFmt, (char*)&streamHdr.fccType);
1755
1756 /* get count of this streamtype up to this stream */
1757 count = 0;
1758 for (n = nStream; 0 <= n; n--) {
1759 if (This->ppStreams[n]->sInfo.fccHandler == streamHdr.fccType)
1760 count++;
1761 }
1762
1763 memset(pStream->sInfo.szName, 0, sizeof(pStream->sInfo.szName));
1764
1765 /* FIXME: avoid overflow -- better use wsnprintfW, which doesn't exists ! */
1766 wsprintfW(pStream->sInfo.szName, streamNameFmt,
1767 AVIFILE_BasenameW(This->szFileName), szType, count);
1768 }
1769 break;
1770 case ckidSTREAMNAME:
1771 { /* streamname will be saved as ASCII string */
1772 LPSTR str = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
1773 if (str == NULL)
1774 return AVIERR_MEMORY;
1775
1776 if (mmioRead(This->hmmio, str, ck.cksize) != ck.cksize)
1777 {
1778 HeapFree(GetProcessHeap(), 0, str);
1779 return AVIERR_FILEREAD;
1780 }
1781
1782 MultiByteToWideChar(CP_ACP, 0, str, -1, pStream->sInfo.szName,
1783 sizeof(pStream->sInfo.szName)/sizeof(pStream->sInfo.szName[0]));
1784
1785 HeapFree(GetProcessHeap(), 0, str);
1786 }
1787 break;
1788 case ckidAVIPADDING:
1789 case mmioFOURCC('p','a','d','d'):
1790 break;
1791 default:
1792 WARN(": found extra chunk 0x%08X\n", ck.ckid);
1793 hr = ReadChunkIntoExtra(&pStream->extra, This->hmmio, &ck);
1794 if (FAILED(hr))
1795 return hr;
1796 };
1797 if (pStream->lpFormat != NULL && pStream->sInfo.fccType == streamtypeAUDIO)
1798 {
1799 WAVEFORMATEX *wfx = pStream->lpFormat; /* wfx->nBlockAlign = wfx->nChannels * wfx->wBitsPerSample / 8; could be added */
1800 pStream->sInfo.dwSampleSize = wfx->nBlockAlign; /* to deal with corrupt wfx->nBlockAlign but Windows doesn't do this */
1801 TRACE("Block size reset to %u, chan=%u bpp=%u\n", wfx->nBlockAlign, wfx->nChannels, wfx->wBitsPerSample);
1802 pStream->sInfo.dwScale = 1;
1803 pStream->sInfo.dwRate = wfx->nSamplesPerSec;
1804 }
1805 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
1806 return AVIERR_FILEREAD;
1807 }
1808 } else {
1809 /* nested chunks in "LIST","hdrl" which are not of type "LIST","strl" */
1810 hr = ReadChunkIntoExtra(&This->fileextra, This->hmmio, &ckLIST2);
1811 if (FAILED(hr))
1812 return hr;
1813 }
1814 if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
1815 return AVIERR_FILEREAD;
1816 }
1817
1818 /* read any extra headers in "LIST","hdrl" */
1819 FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, 0);
1820 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
1821 return AVIERR_FILEREAD;
1822
1823 /* search "LIST","movi" chunk in "RIFF","AVI " */
1824 ckLIST1.fccType = listtypeAVIMOVIE;
1825 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF,
1826 MMIO_FINDLIST);
1827 if (FAILED(hr))
1828 return hr;
1829
1830 This->dwMoviChunkPos = ckLIST1.dwDataOffset;
1831 This->dwIdxChunkPos = ckLIST1.cksize + ckLIST1.dwDataOffset;
1832 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
1833 return AVIERR_FILEREAD;
1834
1835 /* try to find an index */
1836 ck.ckid = ckidAVINEWINDEX;
1837 hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio,
1838 &ck, &ckRIFF, MMIO_FINDCHUNK);
1839 if (SUCCEEDED(hr) && ck.cksize > 0) {
1840 if (FAILED(AVIFILE_LoadIndex(This, ck.cksize, ckLIST1.dwDataOffset)))
1841 This->fInfo.dwFlags &= ~AVIFILEINFO_HASINDEX;
1842 }
1843
1844 /* when we haven't found an index or it's bad, then build one
1845 * by parsing 'movi' chunk */
1846 if ((This->fInfo.dwFlags & AVIFILEINFO_HASINDEX) == 0) {
1847 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++)
1848 This->ppStreams[nStream]->lLastFrame = -1;
1849
1850 if (mmioSeek(This->hmmio, ckLIST1.dwDataOffset + sizeof(DWORD), SEEK_SET) == -1) {
1851 ERR(": Oops, can't seek back to 'movi' chunk!\n");
1852 return AVIERR_FILEREAD;
1853 }
1854
1855 /* seek through the 'movi' list until end */
1856 while (mmioDescend(This->hmmio, &ck, &ckLIST1, 0) == S_OK) {
1857 if (ck.ckid != FOURCC_LIST) {
1858 if (mmioAscend(This->hmmio, &ck, 0) == S_OK) {
1859 nStream = StreamFromFOURCC(ck.ckid);
1860
1861 if (nStream > This->fInfo.dwStreams)
1862 return AVIERR_BADFORMAT;
1863
1864 AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
1865 ck.dwDataOffset - 2 * sizeof(DWORD), 0);
1866 } else {
1867 nStream = StreamFromFOURCC(ck.ckid);
1868 WARN(": file seems to be truncated!\n");
1869 if (nStream <= This->fInfo.dwStreams &&
1870 This->ppStreams[nStream]->sInfo.dwSampleSize > 0) {
1871 ck.cksize = mmioSeek(This->hmmio, 0, SEEK_END);
1872 if (ck.cksize != -1) {
1873 ck.cksize -= ck.dwDataOffset;
1874 ck.cksize &= ~(This->ppStreams[nStream]->sInfo.dwSampleSize - 1);
1875
1876 AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
1877 ck.dwDataOffset - 2 * sizeof(DWORD), 0);
1878 }
1879 }
1880 }
1881 }
1882 }
1883 }
1884
1885 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++)
1886 {
1887 DWORD sugbuf = This->ppStreams[nStream]->sInfo.dwSuggestedBufferSize;
1888 if (This->fInfo.dwSuggestedBufferSize < sugbuf)
1889 This->fInfo.dwSuggestedBufferSize = sugbuf;
1890 }
1891
1892 /* find other chunks */
1893 FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckRIFF, 0);
1894
1895 return AVIERR_OK;
1896 }
1897
1898 static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset)
1899 {
1900 AVIINDEXENTRY *lp;
1901 DWORD pos, n;
1902 HRESULT hr = AVIERR_OK;
1903 BOOL bAbsolute = TRUE;
1904
1905 lp = HeapAlloc(GetProcessHeap(), 0, IDX_PER_BLOCK * sizeof(AVIINDEXENTRY));
1906 if (lp == NULL)
1907 return AVIERR_MEMORY;
1908
1909 /* adjust limits for index tables, so that inserting will be faster */
1910 for (n = 0; n < This->fInfo.dwStreams; n++) {
1911 IAVIStreamImpl *pStream = This->ppStreams[n];
1912
1913 pStream->lLastFrame = -1;
1914
1915 if (pStream->idxFrames != NULL) {
1916 HeapFree(GetProcessHeap(), 0, pStream->idxFrames);
1917 pStream->idxFrames = NULL;
1918 pStream->nIdxFrames = 0;
1919 }
1920
1921 if (pStream->sInfo.dwSampleSize != 0) {
1922 if (n > 0 && This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
1923 pStream->nIdxFrames = This->ppStreams[0]->nIdxFrames;
1924 } else if (pStream->sInfo.dwSuggestedBufferSize) {
1925 pStream->nIdxFrames =
1926 pStream->sInfo.dwLength / pStream->sInfo.dwSuggestedBufferSize;
1927 }
1928 } else
1929 pStream->nIdxFrames = pStream->sInfo.dwLength;
1930
1931 pStream->idxFrames =
1932 HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pStream->nIdxFrames * sizeof(AVIINDEXENTRY));
1933 if (pStream->idxFrames == NULL && pStream->nIdxFrames > 0) {
1934 pStream->nIdxFrames = 0;
1935 HeapFree(GetProcessHeap(), 0, lp);
1936 return AVIERR_MEMORY;
1937 }
1938 }
1939
1940 pos = (DWORD)-1;
1941 while (size != 0) {
1942 LONG read = min(IDX_PER_BLOCK * sizeof(AVIINDEXENTRY), size);
1943
1944 if (mmioRead(This->hmmio, (HPSTR)lp, read) != read) {
1945 hr = AVIERR_FILEREAD;
1946 break;
1947 }
1948 size -= read;
1949
1950 if (pos == (DWORD)-1)
1951 pos = offset - lp->dwChunkOffset + sizeof(DWORD);
1952
1953 AVIFILE_ParseIndex(This, lp, read / sizeof(AVIINDEXENTRY),
1954 pos, &bAbsolute);
1955 }
1956
1957 HeapFree(GetProcessHeap(), 0, lp);
1958
1959 /* checking ... */
1960 for (n = 0; n < This->fInfo.dwStreams; n++) {
1961 IAVIStreamImpl *pStream = This->ppStreams[n];
1962
1963 if (pStream->sInfo.dwSampleSize == 0 &&
1964 pStream->sInfo.dwLength != pStream->lLastFrame+1)
1965 ERR("stream %u length mismatch: dwLength=%u found=%d\n",
1966 n, pStream->sInfo.dwLength, pStream->lLastFrame);
1967 }
1968
1969 return hr;
1970 }
1971
1972 static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp,
1973 LONG count, DWORD pos, BOOL *bAbsolute)
1974 {
1975 if (lp == NULL)
1976 return AVIERR_BADPARAM;
1977
1978 for (; count > 0; count--, lp++) {
1979 WORD nStream = StreamFromFOURCC(lp->ckid);
1980
1981 if (lp->ckid == listtypeAVIRECORD || nStream == 0x7F)
1982 continue; /* skip these */
1983
1984 if (nStream > This->fInfo.dwStreams)
1985 return AVIERR_BADFORMAT;
1986
1987 if (*bAbsolute && lp->dwChunkOffset < This->dwMoviChunkPos)
1988 *bAbsolute = FALSE;
1989
1990 if (*bAbsolute)
1991 lp->dwChunkOffset += sizeof(DWORD);
1992 else
1993 lp->dwChunkOffset += pos;
1994
1995 if (FAILED(AVIFILE_AddFrame(This->ppStreams[nStream], lp->ckid, lp->dwChunkLength, lp->dwChunkOffset, lp->dwFlags)))
1996 return AVIERR_MEMORY;
1997 }
1998
1999 return AVIERR_OK;
2000 }
2001
2002 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD pos,
2003 LPVOID buffer, DWORD size)
2004 {
2005 /* pre-conditions */
2006 assert(This != NULL);
2007 assert(This->paf != NULL);
2008 assert(This->paf->hmmio != NULL);
2009 assert(This->sInfo.dwStart <= pos && pos < This->sInfo.dwLength);
2010 assert(pos <= This->lLastFrame);
2011
2012 /* should we read as much as block gives us? */
2013 if (size == 0 || size > This->idxFrames[pos].dwChunkLength)
2014 size = This->idxFrames[pos].dwChunkLength;
2015
2016 /* read into out own buffer or given one? */
2017 if (buffer == NULL) {
2018 /* we also read the chunk */
2019 size += 2 * sizeof(DWORD);
2020
2021 /* check that buffer is big enough -- don't trust dwSuggestedBufferSize */
2022 if (This->lpBuffer == NULL || This->cbBuffer < size) {
2023 DWORD maxSize = max(size, This->sInfo.dwSuggestedBufferSize);
2024
2025 if (This->lpBuffer == NULL) {
2026 This->lpBuffer = HeapAlloc(GetProcessHeap(), 0, maxSize);
2027 if (!This->lpBuffer) return AVIERR_MEMORY;
2028 } else {
2029 void *new_buffer = HeapReAlloc(GetProcessHeap(), 0, This->lpBuffer, maxSize);
2030 if (!new_buffer) return AVIERR_MEMORY;
2031 This->lpBuffer = new_buffer;
2032 }
2033 This->cbBuffer = maxSize;
2034 }
2035
2036 /* now read the complete chunk into our buffer */
2037 if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset, SEEK_SET) == -1)
2038 return AVIERR_FILEREAD;
2039 if (mmioRead(This->paf->hmmio, (HPSTR)This->lpBuffer, size) != size)
2040 return AVIERR_FILEREAD;
2041
2042 /* check if it was the correct block which we have read */
2043 if (This->lpBuffer[0] != This->idxFrames[pos].ckid ||
2044 This->lpBuffer[1] != This->idxFrames[pos].dwChunkLength) {
2045 ERR(": block %d not found at 0x%08X\n", pos, This->idxFrames[pos].dwChunkOffset);
2046 ERR(": Index says: '%4.4s'(0x%08X) size 0x%08X\n",
2047 (char*)&This->idxFrames[pos].ckid, This->idxFrames[pos].ckid,
2048 This->idxFrames[pos].dwChunkLength);
2049 ERR(": Data says: '%4.4s'(0x%08X) size 0x%08X\n",
2050 (char*)&This->lpBuffer[0], This->lpBuffer[0], This->lpBuffer[1]);
2051 return AVIERR_FILEREAD;
2052 }
2053 } else {
2054 if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD), SEEK_SET) == -1)
2055 return AVIERR_FILEREAD;
2056 if (mmioRead(This->paf->hmmio, buffer, size) != size)
2057 return AVIERR_FILEREAD;
2058 }
2059
2060 return AVIERR_OK;
2061 }
2062
2063 static void AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos, LPLONG offset)
2064 {
2065 LONG block;
2066
2067 /* pre-conditions */
2068 assert(This != NULL);
2069 assert(pos != NULL);
2070 assert(offset != NULL);
2071 assert(This->sInfo.dwSampleSize != 0);
2072 assert(*pos >= This->sInfo.dwStart);
2073
2074 /* convert start sample to start bytes */
2075 (*offset) = (*pos) - This->sInfo.dwStart;
2076 (*offset) *= This->sInfo.dwSampleSize;
2077
2078 /* convert bytes to block number */
2079 for (block = 0; block <= This->lLastFrame; block++) {
2080 if (This->idxFrames[block].dwChunkLength <= *offset)
2081 (*offset) -= This->idxFrames[block].dwChunkLength;
2082 else
2083 break;
2084 }
2085
2086 *pos = block;
2087 }
2088
2089 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This)
2090 {
2091 MainAVIHeader MainAVIHdr;
2092 IAVIStreamImpl* pStream;
2093 MMCKINFO ckRIFF;
2094 MMCKINFO ckLIST1;
2095 MMCKINFO ckLIST2;
2096 MMCKINFO ck;
2097 DWORD nStream;
2098 DWORD dwPos;
2099 HRESULT hr;
2100
2101 /* initialize some things */
2102 if (This->dwMoviChunkPos == 0)
2103 AVIFILE_ComputeMoviStart(This);
2104
2105 /* written one record to much? */
2106 if (This->ckLastRecord.dwFlags & MMIO_DIRTY) {
2107 This->dwNextFramePos -= 3 * sizeof(DWORD);
2108 if (This->nIdxRecords > 0)
2109 This->nIdxRecords--;
2110 }
2111
2112 AVIFILE_UpdateInfo(This);
2113
2114 assert(This->fInfo.dwScale != 0);
2115
2116 memset(&MainAVIHdr, 0, sizeof(MainAVIHdr));
2117 MainAVIHdr.dwMicroSecPerFrame = MulDiv(This->fInfo.dwRate, 1000000,
2118 This->fInfo.dwScale);
2119 MainAVIHdr.dwMaxBytesPerSec = This->fInfo.dwMaxBytesPerSec;
2120 MainAVIHdr.dwPaddingGranularity = AVI_HEADERSIZE;
2121 MainAVIHdr.dwFlags = This->fInfo.dwFlags;
2122 MainAVIHdr.dwTotalFrames = This->fInfo.dwLength;
2123 MainAVIHdr.dwInitialFrames = 0;
2124 MainAVIHdr.dwStreams = This->fInfo.dwStreams;
2125 MainAVIHdr.dwSuggestedBufferSize = This->fInfo.dwSuggestedBufferSize;
2126 MainAVIHdr.dwWidth = This->fInfo.dwWidth;
2127 MainAVIHdr.dwHeight = This->fInfo.dwHeight;
2128 MainAVIHdr.dwInitialFrames = This->dwInitialFrames;
2129
2130 /* now begin writing ... */
2131 mmioSeek(This->hmmio, 0, SEEK_SET);
2132
2133 /* RIFF chunk */
2134 ckRIFF.cksize = 0;
2135 ckRIFF.fccType = formtypeAVI;
2136 if (mmioCreateChunk(This->hmmio, &ckRIFF, MMIO_CREATERIFF) != S_OK)
2137 return AVIERR_FILEWRITE;
2138
2139 /* AVI headerlist */
2140 ckLIST1.cksize = 0;
2141 ckLIST1.fccType = listtypeAVIHEADER;
2142 if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK)
2143 return AVIERR_FILEWRITE;
2144
2145 /* MainAVIHeader */
2146 ck.ckid = ckidAVIMAINHDR;
2147 ck.cksize = sizeof(MainAVIHdr);
2148 ck.fccType = 0;
2149 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2150 return AVIERR_FILEWRITE;
2151 if (mmioWrite(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
2152 return AVIERR_FILEWRITE;
2153 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2154 return AVIERR_FILEWRITE;
2155
2156 /* write the headers of each stream into a separate streamheader list */
2157 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2158 AVIStreamHeader strHdr;
2159
2160 pStream = This->ppStreams[nStream];
2161
2162 /* begin the new streamheader list */
2163 ckLIST2.cksize = 0;
2164 ckLIST2.fccType = listtypeSTREAMHEADER;
2165 if (mmioCreateChunk(This->hmmio, &ckLIST2, MMIO_CREATELIST) != S_OK)
2166 return AVIERR_FILEWRITE;
2167
2168 /* create an AVIStreamHeader from the AVSTREAMINFO */
2169 strHdr.fccType = pStream->sInfo.fccType;
2170 strHdr.fccHandler = pStream->sInfo.fccHandler;
2171 strHdr.dwFlags = pStream->sInfo.dwFlags;
2172 strHdr.wPriority = pStream->sInfo.wPriority;
2173 strHdr.wLanguage = pStream->sInfo.wLanguage;
2174 strHdr.dwInitialFrames = pStream->sInfo.dwInitialFrames;
2175 strHdr.dwScale = pStream->sInfo.dwScale;
2176 strHdr.dwRate = pStream->sInfo.dwRate;
2177 strHdr.dwStart = pStream->sInfo.dwStart;
2178 strHdr.dwLength = pStream->sInfo.dwLength;
2179 strHdr.dwSuggestedBufferSize = pStream->sInfo.dwSuggestedBufferSize;
2180 strHdr.dwQuality = pStream->sInfo.dwQuality;
2181 strHdr.dwSampleSize = pStream->sInfo.dwSampleSize;
2182 strHdr.rcFrame.left = pStream->sInfo.rcFrame.left;
2183 strHdr.rcFrame.top = pStream->sInfo.rcFrame.top;
2184 strHdr.rcFrame.right = pStream->sInfo.rcFrame.right;
2185 strHdr.rcFrame.bottom = pStream->sInfo.rcFrame.bottom;
2186
2187 /* now write the AVIStreamHeader */
2188 ck.ckid = ckidSTREAMHEADER;
2189 ck.cksize = sizeof(strHdr);
2190 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2191 return AVIERR_FILEWRITE;
2192 if (mmioWrite(This->hmmio, (HPSTR)&strHdr, ck.cksize) != ck.cksize)
2193 return AVIERR_FILEWRITE;
2194 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2195 return AVIERR_FILEWRITE;
2196
2197 /* ... the hopefully ever present streamformat ... */
2198 ck.ckid = ckidSTREAMFORMAT;
2199 ck.cksize = pStream->cbFormat;
2200 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2201 return AVIERR_FILEWRITE;
2202 if (pStream->lpFormat != NULL && ck.cksize > 0) {
2203 if (mmioWrite(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize)
2204 return AVIERR_FILEWRITE;
2205 }
2206 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2207 return AVIERR_FILEWRITE;
2208
2209 /* ... some optional existing handler data ... */
2210 if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0) {
2211 ck.ckid = ckidSTREAMHANDLERDATA;
2212 ck.cksize = pStream->cbHandlerData;
2213 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2214 return AVIERR_FILEWRITE;
2215 if (mmioWrite(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize)
2216 return AVIERR_FILEWRITE;
2217 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2218 return AVIERR_FILEWRITE;
2219 }
2220
2221 /* ... some optional additional extra chunk for this stream ... */
2222 if (pStream->extra.lp != NULL && pStream->extra.cb > 0) {
2223 /* the chunk header(s) are already in the structure */
2224 if (mmioWrite(This->hmmio, pStream->extra.lp, pStream->extra.cb) != pStream->extra.cb)
2225 return AVIERR_FILEWRITE;
2226 }
2227
2228 /* ... an optional name for this stream ... */
2229 if (lstrlenW(pStream->sInfo.szName) > 0) {
2230 LPSTR str;
2231
2232 ck.ckid = ckidSTREAMNAME;
2233 ck.cksize = lstrlenW(pStream->sInfo.szName) + 1;
2234 if (ck.cksize & 1) /* align */
2235 ck.cksize++;
2236 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2237 return AVIERR_FILEWRITE;
2238
2239 /* the streamname must be saved in ASCII not Unicode */
2240 str = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
2241 if (str == NULL)
2242 return AVIERR_MEMORY;
2243 WideCharToMultiByte(CP_ACP, 0, pStream->sInfo.szName, -1, str,
2244 ck.cksize, NULL, NULL);
2245
2246 if (mmioWrite(This->hmmio, str, ck.cksize) != ck.cksize) {
2247 HeapFree(GetProcessHeap(), 0, str);
2248 return AVIERR_FILEWRITE;
2249 }
2250
2251 HeapFree(GetProcessHeap(), 0, str);
2252 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2253 return AVIERR_FILEWRITE;
2254 }
2255
2256 /* close streamheader list for this stream */
2257 if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
2258 return AVIERR_FILEWRITE;
2259 } /* for (0 <= nStream < MainAVIHdr.dwStreams) */
2260
2261 /* close the aviheader list */
2262 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
2263 return AVIERR_FILEWRITE;
2264
2265 /* check for padding to pre-guessed 'movi'-chunk position */
2266 dwPos = ckLIST1.dwDataOffset + ckLIST1.cksize;
2267 if (This->dwMoviChunkPos - 2 * sizeof(DWORD) > dwPos) {
2268 ck.ckid = ckidAVIPADDING;
2269 ck.cksize = This->dwMoviChunkPos - dwPos - 4 * sizeof(DWORD);
2270 assert((LONG)ck.cksize >= 0);
2271
2272 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2273 return AVIERR_FILEWRITE;
2274 if (mmioSeek(This->hmmio, ck.cksize, SEEK_CUR) == -1)
2275 return AVIERR_FILEWRITE;
2276 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2277 return AVIERR_FILEWRITE;
2278 }
2279
2280 /* now write the 'movi' chunk */
2281 mmioSeek(This->hmmio, This->dwMoviChunkPos - 2 * sizeof(DWORD), SEEK_SET);
2282 ckLIST1.cksize = 0;
2283 ckLIST1.fccType = listtypeAVIMOVIE;
2284 if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK)
2285 return AVIERR_FILEWRITE;
2286 if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1)
2287 return AVIERR_FILEWRITE;
2288 if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
2289 return AVIERR_FILEWRITE;
2290
2291 /* write 'idx1' chunk */
2292 hr = AVIFILE_SaveIndex(This);
2293 if (FAILED(hr))
2294 return hr;
2295
2296 /* write optional extra file chunks */
2297 if (This->fileextra.lp != NULL && This->fileextra.cb > 0) {
2298 /* as for the streams, are the chunk header(s) in the structure */
2299 if (mmioWrite(This->hmmio, This->fileextra.lp, This->fileextra.cb) != This->fileextra.cb)
2300 return AVIERR_FILEWRITE;
2301 }
2302
2303 /* close RIFF chunk */
2304 if (mmioAscend(This->hmmio, &ckRIFF, 0) != S_OK)
2305 return AVIERR_FILEWRITE;
2306
2307 /* add some JUNK at end for bad parsers */
2308 memset(&ckRIFF, 0, sizeof(ckRIFF));
2309 mmioWrite(This->hmmio, (HPSTR)&ckRIFF, sizeof(ckRIFF));
2310 mmioFlush(This->hmmio, 0);
2311
2312 return AVIERR_OK;
2313 }
2314
2315 static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This)
2316 {
2317 IAVIStreamImpl *pStream;
2318 AVIINDEXENTRY idx;
2319 MMCKINFO ck;
2320 DWORD nStream;
2321 LONG n;
2322
2323 ck.ckid = ckidAVINEWINDEX;
2324 ck.cksize = 0;
2325 if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
2326 return AVIERR_FILEWRITE;
2327
2328 if (This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
2329 /* is interleaved -- write block of corresponding frames */
2330 LONG lInitialFrames = 0;
2331 LONG stepsize;
2332 LONG i;
2333
2334 if (This->ppStreams[0]->sInfo.dwSampleSize == 0)
2335 stepsize = 1;
2336 else
2337 stepsize = AVIStreamTimeToSample((PAVISTREAM)This->ppStreams[0], 1000000);
2338
2339 assert(stepsize > 0);
2340
2341 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2342 if (lInitialFrames < This->ppStreams[nStream]->sInfo.dwInitialFrames)
2343 lInitialFrames = This->ppStreams[nStream]->sInfo.dwInitialFrames;
2344 }
2345
2346 for (i = -lInitialFrames; i < (LONG)This->fInfo.dwLength - lInitialFrames;
2347 i += stepsize) {
2348 DWORD nFrame = lInitialFrames + i;
2349
2350 assert(nFrame < This->nIdxRecords);
2351
2352 idx.ckid = listtypeAVIRECORD;
2353 idx.dwFlags = AVIIF_LIST;
2354 idx.dwChunkLength = This->idxRecords[nFrame].dwChunkLength;
2355 idx.dwChunkOffset = This->idxRecords[nFrame].dwChunkOffset
2356 - This->dwMoviChunkPos;
2357 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2358 return AVIERR_FILEWRITE;
2359
2360 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2361 pStream = This->ppStreams[nStream];
2362
2363 /* heave we reached start of this stream? */
2364 if (-(LONG)pStream->sInfo.dwInitialFrames > i)
2365 continue;
2366
2367 if (pStream->sInfo.dwInitialFrames < lInitialFrames)
2368 nFrame -= (lInitialFrames - pStream->sInfo.dwInitialFrames);
2369
2370 /* reached end of this stream? */
2371 if (pStream->lLastFrame <= nFrame)
2372 continue;
2373
2374 if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
2375 pStream->sInfo.dwFormatChangeCount != 0 &&
2376 pStream->idxFmtChanges != NULL) {
2377 DWORD pos;
2378
2379 for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) {
2380 if (pStream->idxFmtChanges[pos].ckid == nFrame) {
2381 idx.dwFlags = AVIIF_NOTIME;
2382 idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream);
2383 idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength;
2384 idx.dwChunkOffset = pStream->idxFmtChanges[pos].dwChunkOffset
2385 - This->dwMoviChunkPos;
2386
2387 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2388 return AVIERR_FILEWRITE;
2389 break;
2390 }
2391 }
2392 } /* if have formatchanges */
2393
2394 idx.ckid = pStream->idxFrames[nFrame].ckid;
2395 idx.dwFlags = pStream->idxFrames[nFrame].dwFlags;
2396 idx.dwChunkLength = pStream->idxFrames[nFrame].dwChunkLength;
2397 idx.dwChunkOffset = pStream->idxFrames[nFrame].dwChunkOffset
2398 - This->dwMoviChunkPos;
2399 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2400 return AVIERR_FILEWRITE;
2401 }
2402 }
2403 } else {
2404 /* not interleaved -- write index for each stream at once */
2405 for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2406 pStream = This->ppStreams[nStream];
2407
2408 for (n = 0; n <= pStream->lLastFrame; n++) {
2409 if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
2410 (pStream->sInfo.dwFormatChangeCount != 0)) {
2411 DWORD pos;
2412
2413 for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) {
2414 if (pStream->idxFmtChanges[pos].ckid == n) {
2415 idx.dwFlags = AVIIF_NOTIME;
2416 idx.ckid = MAKEAVICKID(cktypePALchange, pStream->nStream);
2417 idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength;
2418 idx.dwChunkOffset =
2419 pStream->idxFmtChanges[pos].dwChunkOffset - This->dwMoviChunkPos;
2420 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2421 return AVIERR_FILEWRITE;
2422 break;
2423 }
2424 }
2425 } /* if have formatchanges */
2426
2427 idx.ckid = pStream->idxFrames[n].ckid;
2428 idx.dwFlags = pStream->idxFrames[n].dwFlags;
2429 idx.dwChunkLength = pStream->idxFrames[n].dwChunkLength;
2430 idx.dwChunkOffset = pStream->idxFrames[n].dwChunkOffset
2431 - This->dwMoviChunkPos;
2432
2433 if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
2434 return AVIERR_FILEWRITE;
2435 }
2436 }
2437 } /* if not interleaved */
2438
2439 if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
2440 return AVIERR_FILEWRITE;
2441
2442 return AVIERR_OK;
2443 }
2444
2445 static ULONG AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fcc, LONG lSkip)
2446 {
2447 UINT i;
2448 UINT nStream;
2449
2450 /* pre-condition */
2451 assert(lSkip >= 0);
2452
2453 if (fcc != 0) {
2454 /* search the number of the specified stream */
2455 nStream = (ULONG)-1;
2456 for (i = 0; i < This->fInfo.dwStreams; i++) {
2457 assert(This->ppStreams[i] != NULL);
2458
2459 if (This->ppStreams[i]->sInfo.fccType == fcc) {
2460 if (lSkip == 0) {
2461 nStream = i;
2462 break;
2463 } else
2464 lSkip--;
2465 }
2466 }
2467 } else
2468 nStream = lSkip;
2469
2470 return nStream;
2471 }
2472
2473 static void AVIFILE_UpdateInfo(IAVIFileImpl *This)
2474 {
2475 UINT i;
2476
2477 /* pre-conditions */
2478 assert(This != NULL);
2479
2480 This->fInfo.dwMaxBytesPerSec = 0;
2481 This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
2482 This->fInfo.dwSuggestedBufferSize = 0;
2483 This->fInfo.dwWidth = 0;
2484 This->fInfo.dwHeight = 0;
2485 This->fInfo.dwScale = 0;
2486 This->fInfo.dwRate = 0;
2487 This->fInfo.dwLength = 0;
2488 This->dwInitialFrames = 0;
2489
2490 for (i = 0; i < This->fInfo.dwStreams; i++) {
2491 AVISTREAMINFOW *psi;
2492 DWORD n;
2493
2494 /* pre-conditions */
2495 assert(This->ppStreams[i] != NULL);
2496
2497 psi = &This->ppStreams[i]->sInfo;
2498 assert(psi->dwScale != 0);
2499 assert(psi->dwRate != 0);
2500
2501 if (i == 0) {
2502 /* use first stream timings as base */
2503 This->fInfo.dwScale = psi->dwScale;
2504 This->fInfo.dwRate = psi->dwRate;
2505 This->fInfo.dwLength = psi->dwLength;
2506 } else {
2507 n = AVIStreamSampleToSample((PAVISTREAM)This->ppStreams[0],
2508 (PAVISTREAM)This->ppStreams[i],psi->dwLength);
2509 if (This->fInfo.dwLength < n)
2510 This->fInfo.dwLength = n;
2511 }
2512
2513 if (This->dwInitialFrames < psi->dwInitialFrames)
2514 This->dwInitialFrames = psi->dwInitialFrames;
2515
2516 if (This->fInfo.dwSuggestedBufferSize < psi->dwSuggestedBufferSize)
2517 This->fInfo.dwSuggestedBufferSize = psi->dwSuggestedBufferSize;
2518
2519 if (psi->dwSampleSize != 0) {
2520 /* fixed sample size -- exact computation */
2521 This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSampleSize, psi->dwRate,
2522 psi->dwScale);
2523 } else {
2524 /* variable sample size -- only upper limit */
2525 This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSuggestedBufferSize,
2526 psi->dwRate, psi->dwScale);
2527
2528 /* update dimensions */
2529 n = psi->rcFrame.right - psi->rcFrame.left;
2530 if (This->fInfo.dwWidth < n)
2531 This->fInfo.dwWidth = n;
2532 n = psi->rcFrame.bottom - psi->rcFrame.top;
2533 if (This->fInfo.dwHeight < n)
2534 This->fInfo.dwHeight = n;
2535 }
2536 }
2537 }
2538
2539 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
2540 FOURCC ckid, DWORD flags, LPCVOID buffer,
2541 LONG size)
2542 {
2543 MMCKINFO ck;
2544
2545 ck.ckid = ckid;
2546 ck.cksize = size;
2547 ck.fccType = 0;
2548
2549 /* if no frame/block is already written, we must compute start of movi chunk */
2550 if (This->paf->dwMoviChunkPos == 0)
2551 AVIFILE_ComputeMoviStart(This->paf);
2552
2553 if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1)
2554 return AVIERR_FILEWRITE;
2555
2556 if (mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK)
2557 return AVIERR_FILEWRITE;
2558 if (buffer != NULL && size > 0) {
2559 if (mmioWrite(This->paf->hmmio, buffer, size) != size)
2560 return AVIERR_FILEWRITE;
2561 }
2562 if (mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
2563 return AVIERR_FILEWRITE;
2564
2565 This->paf->fDirty = TRUE;
2566 This->paf->dwNextFramePos = mmioSeek(This->paf->hmmio, 0, SEEK_CUR);
2567
2568 return AVIFILE_AddFrame(This, ckid, size,
2569 ck.dwDataOffset - 2 * sizeof(DWORD), flags);
2570 }