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