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