* Slap *some* sense into our header inclusions.
[reactos.git] / reactos / dll / win32 / avifil32 / editstream.c
1 /*
2 * Copyright 2003 Michael Günnewig
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19 #define WIN32_NO_STATUS
20 #define _INC_WINDOWS
21 #define COM_NO_WINDOWS_H
22
23 #include <assert.h>
24 #include <stdarg.h>
25
26 #include <windef.h>
27 #include <winbase.h>
28 //#include "winuser.h"
29 #include <wingdi.h>
30 //#include "winerror.h"
31 //#include "mmsystem.h"
32 #include <vfw.h>
33
34 #include "avifile_private.h"
35 //#include "extrachunk.h"
36
37 #include <wine/debug.h>
38 #include <initguid.h>
39
40 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
41
42 /***********************************************************************/
43
44 /* internal interface to get access to table of stream in an editable stream */
45
46 DEFINE_AVIGUID(IID_IEditStreamInternal, 0x0002000A,0,0);
47
48 typedef struct _EditStreamTable {
49 PAVISTREAM pStream; /* stream which contains the data */
50 DWORD dwStart; /* where starts the part which is also our */
51 DWORD dwLength; /* how many is also in this stream */
52 } EditStreamTable;
53
54 #define EditStreamEnd(This,streamNr) ((This)->pStreams[streamNr].dwStart + \
55 (This)->pStreams[streamNr].dwLength)
56
57 typedef struct _IAVIEditStreamImpl IAVIEditStreamImpl;
58
59 struct _IAVIEditStreamImpl {
60 IAVIEditStream IAVIEditStream_iface;
61 IAVIStream IAVIStream_iface;
62
63 LONG ref;
64
65 AVISTREAMINFOW sInfo;
66
67 EditStreamTable *pStreams;
68 DWORD nStreams; /* current fill level of pStreams table */
69 DWORD nTableSize; /* size of pStreams table */
70
71 BOOL bDecompress;
72 PAVISTREAM pCurStream;
73 PGETFRAME pg; /* IGetFrame for pCurStream */
74 LPBITMAPINFOHEADER lpFrame; /* frame of pCurStream */
75 };
76
77 static inline IAVIEditStreamImpl *impl_from_IAVIEditStream(IAVIEditStream *iface)
78 {
79 return CONTAINING_RECORD(iface, IAVIEditStreamImpl, IAVIEditStream_iface);
80 }
81
82 static inline IAVIEditStreamImpl *impl_from_IAVIStream(IAVIStream *iface)
83 {
84 return CONTAINING_RECORD(iface, IAVIEditStreamImpl, IAVIStream_iface);
85 }
86
87 /***********************************************************************/
88
89 static HRESULT AVIFILE_FindStreamInTable(IAVIEditStreamImpl* const This,
90 DWORD pos,PAVISTREAM *ppStream,
91 DWORD* streamPos,
92 DWORD* streamNr,BOOL bFindSample)
93 {
94 DWORD n;
95
96 TRACE("(%p,%u,%p,%p,%p,%d)\n",This,pos,ppStream,streamPos,
97 streamNr,bFindSample);
98
99 if (pos < This->sInfo.dwStart)
100 return AVIERR_BADPARAM;
101
102 pos -= This->sInfo.dwStart;
103 for (n = 0; n < This->nStreams; n++) {
104 if (pos < This->pStreams[n].dwLength) {
105 *ppStream = This->pStreams[n].pStream;
106 *streamPos = This->pStreams[n].dwStart + pos;
107 if (streamNr != NULL)
108 *streamNr = n;
109
110 return AVIERR_OK;
111 }
112 pos -= This->pStreams[n].dwLength;
113 }
114 if (pos == 0 && bFindSample) {
115 *ppStream = This->pStreams[--n].pStream;
116 *streamPos = EditStreamEnd(This, n);
117 if (streamNr != NULL)
118 *streamNr = n;
119
120 TRACE(" -- pos=0 && b=1 -> (%p,%u,%u)\n",*ppStream, *streamPos, n);
121 return AVIERR_OK;
122 } else {
123 *ppStream = NULL;
124 *streamPos = 0;
125 if (streamNr != NULL)
126 *streamNr = 0;
127
128 TRACE(" -> ERROR (NULL,0,0)\n");
129 return AVIERR_BADPARAM;
130 }
131 }
132
133 static LPVOID AVIFILE_ReadFrame(IAVIEditStreamImpl* const This,
134 PAVISTREAM pstream, LONG pos)
135 {
136 PGETFRAME pg;
137
138 TRACE("(%p,%p,%d)\n",This,pstream,pos);
139
140 if (pstream == NULL)
141 return NULL;
142
143 /* if stream changes make sure that only palette changes */
144 if (This->pCurStream != pstream) {
145 pg = AVIStreamGetFrameOpen(pstream, NULL);
146 if (pg == NULL)
147 return NULL;
148 if (This->pg != NULL) {
149 if (IGetFrame_SetFormat(pg, This->lpFrame, NULL, 0, 0, -1, -1) != S_OK) {
150 AVIStreamGetFrameClose(pg);
151 ERR(": IGetFrame_SetFormat failed\n");
152 return NULL;
153 }
154 AVIStreamGetFrameClose(This->pg);
155 }
156 This->pg = pg;
157 This->pCurStream = pstream;
158 }
159
160 /* now get the decompressed frame */
161 This->lpFrame = AVIStreamGetFrame(This->pg, pos);
162 if (This->lpFrame != NULL)
163 This->sInfo.dwSuggestedBufferSize = This->lpFrame->biSizeImage;
164
165 return This->lpFrame;
166 }
167
168 static HRESULT AVIFILE_RemoveStream(IAVIEditStreamImpl* const This, DWORD nr)
169 {
170 assert(This != NULL);
171 assert(nr < This->nStreams);
172
173 /* remove part nr */
174 IAVIStream_Release(This->pStreams[nr].pStream);
175 This->nStreams--;
176 if (This->nStreams - nr > 0) {
177 memmove(This->pStreams + nr, This->pStreams + nr + 1,
178 (This->nStreams - nr) * sizeof(EditStreamTable));
179 }
180 This->pStreams[This->nStreams].pStream = NULL;
181 This->pStreams[This->nStreams].dwStart = 0;
182 This->pStreams[This->nStreams].dwLength = 0;
183
184 /* try to merge the part before the deleted one and the one after it */
185 if (0 < nr && 0 < This->nStreams &&
186 This->pStreams[nr - 1].pStream == This->pStreams[nr].pStream) {
187 if (EditStreamEnd(This, nr - 1) == This->pStreams[nr].dwStart) {
188 This->pStreams[nr - 1].dwLength += This->pStreams[nr].dwLength;
189 return AVIFILE_RemoveStream(This, nr);
190 }
191 }
192
193 return AVIERR_OK;
194 }
195
196 static BOOL AVIFILE_FormatsEqual(PAVISTREAM avi1, PAVISTREAM avi2)
197 {
198 LPVOID fmt1 = NULL, fmt2 = NULL;
199 LONG size1, size2, start1, start2;
200 BOOL status = FALSE;
201
202 assert(avi1 != NULL && avi2 != NULL);
203
204 /* get stream starts and check format sizes */
205 start1 = AVIStreamStart(avi1);
206 start2 = AVIStreamStart(avi2);
207 if (FAILED(AVIStreamFormatSize(avi1, start1, &size1)))
208 return FALSE;
209 if (FAILED(AVIStreamFormatSize(avi2, start2, &size2)))
210 return FALSE;
211 if (size1 != size2)
212 return FALSE;
213
214 /* sizes match, now get formats and compare them */
215 fmt1 = HeapAlloc(GetProcessHeap(), 0, size1);
216 if (fmt1 == NULL)
217 return FALSE;
218 if (SUCCEEDED(AVIStreamReadFormat(avi1, start1, fmt1, &size1))) {
219 fmt2 = HeapAlloc(GetProcessHeap(), 0, size1);
220 if (fmt2 != NULL) {
221 if (SUCCEEDED(AVIStreamReadFormat(avi2, start2, fmt2, &size1)))
222 status = (memcmp(fmt1, fmt2, size1) == 0);
223 }
224 }
225
226 HeapFree(GetProcessHeap(), 0, fmt2);
227 HeapFree(GetProcessHeap(), 0, fmt1);
228
229 return status;
230 }
231
232 /***********************************************************************/
233
234 static HRESULT WINAPI IAVIEditStream_fnQueryInterface(IAVIEditStream*iface,REFIID refiid,LPVOID *obj)
235 {
236 IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
237
238 TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj);
239
240 if (IsEqualGUID(&IID_IUnknown, refiid) ||
241 IsEqualGUID(&IID_IAVIEditStream, refiid) ||
242 IsEqualGUID(&IID_IEditStreamInternal, refiid)) {
243 *obj = iface;
244 IAVIEditStream_AddRef(iface);
245
246 return S_OK;
247 } else if (IsEqualGUID(&IID_IAVIStream, refiid)) {
248 *obj = &This->IAVIStream_iface;
249 IAVIEditStream_AddRef(iface);
250
251 return S_OK;
252 }
253
254 return E_NOINTERFACE;
255 }
256
257 static ULONG WINAPI IAVIEditStream_fnAddRef(IAVIEditStream*iface)
258 {
259 IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
260 ULONG ref = InterlockedIncrement(&This->ref);
261
262 TRACE("(%p) -> %d\n", iface, ref);
263
264 return ref;
265 }
266
267 static ULONG WINAPI IAVIEditStream_fnRelease(IAVIEditStream*iface)
268 {
269 IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
270 DWORD i;
271 ULONG ref = InterlockedDecrement(&This->ref);
272
273 TRACE("(%p) -> %d\n", iface, ref);
274
275 if (!ref) {
276 /* release memory */
277 if (This->pg != NULL)
278 AVIStreamGetFrameClose(This->pg);
279 if (This->pStreams != NULL) {
280 for (i = 0; i < This->nStreams; i++) {
281 if (This->pStreams[i].pStream != NULL)
282 IAVIStream_Release(This->pStreams[i].pStream);
283 }
284 HeapFree(GetProcessHeap(), 0, This->pStreams);
285 }
286
287 HeapFree(GetProcessHeap(), 0, This);
288 return 0;
289 }
290 return ref;
291 }
292
293 static HRESULT WINAPI IAVIEditStream_fnCut(IAVIEditStream*iface,LONG*plStart,
294 LONG*plLength,PAVISTREAM*ppResult)
295 {
296 IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
297 PAVISTREAM stream;
298 DWORD start, len, streamPos, streamNr;
299 HRESULT hr;
300
301 TRACE("(%p,%p,%p,%p)\n",iface,plStart,plLength,ppResult);
302
303 if (ppResult != NULL)
304 *ppResult = NULL;
305 if (plStart == NULL || plLength == NULL || *plStart < 0)
306 return AVIERR_BADPARAM;
307
308 /* if asked for cut part copy it before deleting */
309 if (ppResult != NULL) {
310 hr = IAVIEditStream_Copy(iface, plStart, plLength, ppResult);
311 if (FAILED(hr))
312 return hr;
313 }
314
315 start = *plStart;
316 len = *plLength;
317
318 /* now delete the requested part */
319 while (len > 0) {
320 hr = AVIFILE_FindStreamInTable(This, start, &stream,
321 &streamPos, &streamNr, FALSE);
322 if (FAILED(hr))
323 return hr;
324 if (This->pStreams[streamNr].dwStart == streamPos) {
325 /* deleting from start of part */
326 if (len < This->pStreams[streamNr].dwLength) {
327 start += len;
328 This->pStreams[streamNr].dwStart += len;
329 This->pStreams[streamNr].dwLength -= len;
330 This->sInfo.dwLength -= len;
331 len = 0;
332
333 /* we must return decompressed data now */
334 This->bDecompress = TRUE;
335 } else {
336 /* deleting hole part */
337 len -= This->pStreams[streamNr].dwLength;
338 AVIFILE_RemoveStream(This,streamNr);
339 }
340 } else if (EditStreamEnd(This, streamNr) <= streamPos + len) {
341 /* deleting at end of a part */
342 DWORD count = EditStreamEnd(This, streamNr) - streamPos;
343 This->sInfo.dwLength -= count;
344 len -= count;
345 This->pStreams[streamNr].dwLength =
346 streamPos - This->pStreams[streamNr].dwStart;
347 } else {
348 /* splitting */
349 if (This->nStreams + 1 >= This->nTableSize) {
350 This->pStreams = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->pStreams,
351 (This->nTableSize + 32) * sizeof(EditStreamTable));
352 if (This->pStreams == NULL)
353 return AVIERR_MEMORY;
354 This->nTableSize += 32;
355 }
356 memmove(This->pStreams + streamNr + 1, This->pStreams + streamNr,
357 (This->nStreams - streamNr) * sizeof(EditStreamTable));
358 This->nStreams++;
359
360 IAVIStream_AddRef(This->pStreams[streamNr + 1].pStream);
361 This->pStreams[streamNr + 1].dwStart = streamPos + len;
362 This->pStreams[streamNr + 1].dwLength =
363 EditStreamEnd(This, streamNr) - This->pStreams[streamNr + 1].dwStart;
364
365 This->pStreams[streamNr].dwLength =
366 streamPos - This->pStreams[streamNr].dwStart;
367 This->sInfo.dwLength -= len;
368 len = 0;
369 }
370 }
371
372 This->sInfo.dwEditCount++;
373
374 return AVIERR_OK;
375 }
376
377 static HRESULT WINAPI IAVIEditStream_fnCopy(IAVIEditStream*iface,LONG*plStart,
378 LONG*plLength,PAVISTREAM*ppResult)
379 {
380 IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
381 IAVIEditStreamImpl* pEdit;
382 HRESULT hr;
383 LONG start = 0;
384
385 TRACE("(%p,%p,%p,%p)\n",iface,plStart,plLength,ppResult);
386
387 if (ppResult == NULL)
388 return AVIERR_BADPARAM;
389 *ppResult = NULL;
390 if (plStart == NULL || plLength == NULL || *plStart < 0 || *plLength < 0)
391 return AVIERR_BADPARAM;
392
393 /* check bounds */
394 if (*(LPDWORD)plLength > This->sInfo.dwLength)
395 *(LPDWORD)plLength = This->sInfo.dwLength;
396 if (*(LPDWORD)plStart < This->sInfo.dwStart) {
397 *(LPDWORD)plLength -= This->sInfo.dwStart - *(LPDWORD)plStart;
398 *(LPDWORD)plStart = This->sInfo.dwStart;
399 if (*plLength < 0)
400 return AVIERR_BADPARAM;
401 }
402 if (*(LPDWORD)plStart + *(LPDWORD)plLength > This->sInfo.dwStart + This->sInfo.dwLength)
403 *(LPDWORD)plLength = This->sInfo.dwStart + This->sInfo.dwLength -
404 *(LPDWORD)plStart;
405
406 pEdit = (IAVIEditStreamImpl*)AVIFILE_CreateEditStream(NULL);
407 if (pEdit == NULL)
408 return AVIERR_MEMORY;
409
410 hr = IAVIEditStream_Paste((PAVIEDITSTREAM)pEdit, &start, plLength, &This->IAVIStream_iface,
411 *plStart, *plStart + *plLength);
412 *plStart = start;
413 if (FAILED(hr))
414 IAVIEditStream_Release((PAVIEDITSTREAM)pEdit);
415 else
416 *ppResult = &This->IAVIStream_iface;
417
418 return hr;
419 }
420
421 static HRESULT WINAPI IAVIEditStream_fnPaste(IAVIEditStream*iface,LONG*plStart,
422 LONG*plLength,PAVISTREAM pSource,
423 LONG lStart,LONG lLength)
424 {
425 IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
426 AVISTREAMINFOW srcInfo;
427 IAVIEditStreamImpl *pEdit = NULL;
428 PAVISTREAM pStream;
429 DWORD startPos, endPos, streamNr, nStreams;
430 ULONG n;
431
432 TRACE("(%p,%p,%p,%p,%d,%d)\n",iface,plStart,plLength,
433 pSource,lStart,lLength);
434
435 if (pSource == NULL)
436 return AVIERR_BADHANDLE;
437 if (plStart == NULL || *plStart < 0)
438 return AVIERR_BADPARAM;
439 if (This->sInfo.dwStart + This->sInfo.dwLength < *plStart)
440 return AVIERR_BADPARAM; /* Can't paste with holes */
441 if (FAILED(IAVIStream_Info(pSource, &srcInfo, sizeof(srcInfo))))
442 return AVIERR_ERROR;
443 if (lStart < srcInfo.dwStart || lStart >= srcInfo.dwStart + srcInfo.dwLength)
444 return AVIERR_BADPARAM;
445 if (This->sInfo.fccType == 0) {
446 /* This stream is empty */
447 IAVIStream_Info(pSource, &This->sInfo, sizeof(This->sInfo));
448 This->sInfo.dwStart = *plStart;
449 This->sInfo.dwLength = 0;
450 }
451 if (This->sInfo.fccType != srcInfo.fccType)
452 return AVIERR_UNSUPPORTED; /* different stream types */
453 if (lLength == -1) /* Copy the hole stream */
454 lLength = srcInfo.dwLength;
455 if (lStart + lLength > srcInfo.dwStart + srcInfo.dwLength)
456 lLength = srcInfo.dwStart + srcInfo.dwLength - lStart;
457 if (lLength + *plStart >= 0x80000000)
458 return AVIERR_MEMORY;
459
460 /* streamtype specific tests */
461 if (srcInfo.fccType == streamtypeVIDEO) {
462 LONG size;
463
464 size = srcInfo.rcFrame.right - srcInfo.rcFrame.left;
465 if (size != This->sInfo.rcFrame.right - This->sInfo.rcFrame.left)
466 return AVIERR_UNSUPPORTED; /* FIXME: Can't GetFrame convert it? */
467 size = srcInfo.rcFrame.bottom - srcInfo.rcFrame.top;
468 if (size != This->sInfo.rcFrame.bottom - This->sInfo.rcFrame.top)
469 return AVIERR_UNSUPPORTED; /* FIXME: Can't GetFrame convert it? */
470 } else if (srcInfo.fccType == streamtypeAUDIO) {
471 if (!AVIFILE_FormatsEqual(&This->IAVIStream_iface, pSource))
472 return AVIERR_UNSUPPORTED;
473 } else {
474 /* FIXME: streamtypeMIDI and streamtypeTEXT */
475 return AVIERR_UNSUPPORTED;
476 }
477
478 /* try to get an IEditStreamInternal interface */
479 if (SUCCEEDED(IAVIStream_QueryInterface(pSource, &IID_IEditStreamInternal, (LPVOID*)&pEdit)))
480 IAVIEditStream_Release(&pEdit->IAVIEditStream_iface); /* pSource holds a reference */
481
482 /* for video must check for change of format */
483 if (This->sInfo.fccType == streamtypeVIDEO) {
484 if (! This->bDecompress) {
485 /* Need to decompress if any of the following conditions matches:
486 * - pSource is an editable stream which decompresses
487 * - the nearest keyframe of pSource isn't lStart
488 * - the nearest keyframe of this stream isn't *plStart
489 * - the format of pSource doesn't match this one
490 */
491 if ((pEdit != NULL && pEdit->bDecompress) ||
492 AVIStreamNearestKeyFrame(pSource, lStart) != lStart ||
493 AVIStreamNearestKeyFrame(&This->IAVIStream_iface, *plStart) != *plStart ||
494 (This->nStreams > 0 && !AVIFILE_FormatsEqual(&This->IAVIStream_iface, pSource))) {
495 /* Use first stream part to get format to convert everything to */
496 AVIFILE_ReadFrame(This, This->pStreams[0].pStream,
497 This->pStreams[0].dwStart);
498
499 /* Check if we could convert the source streams to the desired format... */
500 if (pEdit != NULL) {
501 if (FAILED(AVIFILE_FindStreamInTable(pEdit, lStart, &pStream,
502 &startPos, &streamNr, TRUE)))
503 return AVIERR_INTERNAL;
504 for (n = lStart; n < lStart + lLength; streamNr++) {
505 if (AVIFILE_ReadFrame(This, pEdit->pStreams[streamNr].pStream, startPos) == NULL)
506 return AVIERR_BADFORMAT;
507 startPos = pEdit->pStreams[streamNr].dwStart;
508 n += pEdit->pStreams[streamNr].dwLength;
509 }
510 } else if (AVIFILE_ReadFrame(This, pSource, lStart) == NULL)
511 return AVIERR_BADFORMAT;
512
513 This->bDecompress = TRUE;
514 This->sInfo.fccHandler = 0;
515 }
516 } else if (AVIFILE_ReadFrame(This, pSource, lStart) == NULL)
517 return AVIERR_BADFORMAT; /* Can't convert source to own format */
518 } /* FIXME: something special for the other formats? */
519
520 /* Make sure we have enough memory for parts */
521 if (pEdit != NULL) {
522 DWORD nLastStream;
523
524 AVIFILE_FindStreamInTable(pEdit, lStart + lLength, &pStream,
525 &endPos, &nLastStream, TRUE);
526 AVIFILE_FindStreamInTable(pEdit, lStart, &pStream,
527 &startPos, &streamNr, FALSE);
528 if (nLastStream == streamNr)
529 nLastStream++;
530
531 nStreams = nLastStream - streamNr;
532 } else
533 nStreams = 1;
534 if (This->nStreams + nStreams + 1 > This->nTableSize) {
535 n = This->nStreams + nStreams + 33;
536
537 This->pStreams = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->pStreams, n * sizeof(EditStreamTable));
538 if (This->pStreams == NULL)
539 return AVIERR_MEMORY;
540 This->nTableSize = n;
541 }
542
543 if (plLength != NULL)
544 *plLength = lLength;
545
546 /* now do the real work */
547 if (This->sInfo.dwStart + This->sInfo.dwLength > *plStart) {
548 AVIFILE_FindStreamInTable(This, *plStart, &pStream,
549 &startPos, &streamNr, FALSE);
550 if (startPos != This->pStreams[streamNr].dwStart) {
551 /* split stream streamNr at startPos */
552 memmove(This->pStreams + streamNr + nStreams + 1,
553 This->pStreams + streamNr,
554 (This->nStreams + nStreams - streamNr + 1) * sizeof(EditStreamTable));
555
556 This->pStreams[streamNr + 2].dwLength =
557 EditStreamEnd(This, streamNr + 2) - startPos;
558 This->pStreams[streamNr + 2].dwStart = startPos;
559 This->pStreams[streamNr].dwLength =
560 startPos - This->pStreams[streamNr].dwStart;
561 IAVIStream_AddRef(This->pStreams[streamNr].pStream);
562 streamNr++;
563 } else {
564 /* insert before stream at streamNr */
565 memmove(This->pStreams + streamNr + nStreams, This->pStreams + streamNr,
566 (This->nStreams + nStreams - streamNr) * sizeof(EditStreamTable));
567 }
568 } else /* append the streams */
569 streamNr = This->nStreams;
570
571 if (pEdit != NULL) {
572 /* insert the parts of the editable stream instead of itself */
573 AVIFILE_FindStreamInTable(pEdit, lStart + lLength, &pStream,
574 &endPos, NULL, FALSE);
575 AVIFILE_FindStreamInTable(pEdit, lStart, &pStream, &startPos, &n, FALSE);
576
577 memcpy(This->pStreams + streamNr, pEdit->pStreams + n,
578 nStreams * sizeof(EditStreamTable));
579 if (This->pStreams[streamNr].dwStart < startPos) {
580 This->pStreams[streamNr].dwLength =
581 EditStreamEnd(This, streamNr) - startPos;
582 This->pStreams[streamNr].dwStart = startPos;
583 }
584 if (endPos < EditStreamEnd(This, streamNr + nStreams))
585 This->pStreams[streamNr + nStreams].dwLength =
586 endPos - This->pStreams[streamNr + nStreams].dwStart;
587 } else {
588 /* a simple stream */
589 This->pStreams[streamNr].pStream = pSource;
590 This->pStreams[streamNr].dwStart = lStart;
591 This->pStreams[streamNr].dwLength = lLength;
592 }
593
594 for (n = 0; n < nStreams; n++) {
595 IAVIStream_AddRef(This->pStreams[streamNr + n].pStream);
596 if (0 < streamNr + n &&
597 This->pStreams[streamNr + n - 1].pStream != This->pStreams[streamNr + n].pStream) {
598 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
599 This->sInfo.dwFormatChangeCount++;
600 }
601 }
602 This->sInfo.dwEditCount++;
603 This->sInfo.dwLength += lLength;
604 This->nStreams += nStreams;
605
606 return AVIERR_OK;
607 }
608
609 static HRESULT WINAPI IAVIEditStream_fnClone(IAVIEditStream*iface,
610 PAVISTREAM*ppResult)
611 {
612 IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
613 IAVIEditStreamImpl* pEdit;
614 DWORD i;
615
616 TRACE("(%p,%p)\n",iface,ppResult);
617
618 if (ppResult == NULL)
619 return AVIERR_BADPARAM;
620 *ppResult = NULL;
621
622 pEdit = (IAVIEditStreamImpl*)AVIFILE_CreateEditStream(NULL);
623 if (pEdit == NULL)
624 return AVIERR_MEMORY;
625 if (This->nStreams > pEdit->nTableSize) {
626 pEdit->pStreams = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pEdit->pStreams,
627 This->nStreams * sizeof(EditStreamTable));
628 if (pEdit->pStreams == NULL)
629 return AVIERR_MEMORY;
630 pEdit->nTableSize = This->nStreams;
631 }
632 pEdit->nStreams = This->nStreams;
633 memcpy(pEdit->pStreams, This->pStreams,
634 This->nStreams * sizeof(EditStreamTable));
635 memcpy(&pEdit->sInfo,&This->sInfo,sizeof(This->sInfo));
636 for (i = 0; i < This->nStreams; i++) {
637 if (pEdit->pStreams[i].pStream != NULL)
638 IAVIStream_AddRef(pEdit->pStreams[i].pStream);
639 }
640
641 *ppResult = &This->IAVIStream_iface;
642
643 return AVIERR_OK;
644 }
645
646 static HRESULT WINAPI IAVIEditStream_fnSetInfo(IAVIEditStream*iface,
647 LPAVISTREAMINFOW asi,LONG size)
648 {
649 IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
650
651 TRACE("(%p,%p,%d)\n",iface,asi,size);
652
653 /* check parameters */
654 if (size >= 0 && size < sizeof(AVISTREAMINFOW))
655 return AVIERR_BADSIZE;
656
657 This->sInfo.wLanguage = asi->wLanguage;
658 This->sInfo.wPriority = asi->wPriority;
659 This->sInfo.dwStart = asi->dwStart;
660 This->sInfo.dwRate = asi->dwRate;
661 This->sInfo.dwScale = asi->dwScale;
662 This->sInfo.dwQuality = asi->dwQuality;
663 CopyRect(&This->sInfo.rcFrame, &asi->rcFrame);
664 memcpy(This->sInfo.szName, asi->szName, sizeof(asi->szName));
665 This->sInfo.dwEditCount++;
666
667 return AVIERR_OK;
668 }
669
670 static const struct IAVIEditStreamVtbl ieditstream = {
671 IAVIEditStream_fnQueryInterface,
672 IAVIEditStream_fnAddRef,
673 IAVIEditStream_fnRelease,
674 IAVIEditStream_fnCut,
675 IAVIEditStream_fnCopy,
676 IAVIEditStream_fnPaste,
677 IAVIEditStream_fnClone,
678 IAVIEditStream_fnSetInfo
679 };
680
681 static HRESULT WINAPI IEditAVIStream_fnQueryInterface(IAVIStream*iface,
682 REFIID refiid,LPVOID*obj)
683 {
684 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
685 return IAVIEditStream_QueryInterface(&This->IAVIEditStream_iface,refiid,obj);
686 }
687
688 static ULONG WINAPI IEditAVIStream_fnAddRef(IAVIStream*iface)
689 {
690 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
691 return IAVIEditStream_AddRef(&This->IAVIEditStream_iface);
692 }
693
694 static ULONG WINAPI IEditAVIStream_fnRelease(IAVIStream*iface)
695 {
696 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
697 return IAVIEditStream_Release(&This->IAVIEditStream_iface);
698 }
699
700 static HRESULT WINAPI IEditAVIStream_fnCreate(IAVIStream*iface,
701 LPARAM lParam1,LPARAM lParam2)
702 {
703 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
704
705 if (lParam2 != 0)
706 return AVIERR_ERROR;
707
708 if (This->pStreams == NULL) {
709 This->pStreams = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 256 * sizeof(EditStreamTable));
710 if (This->pStreams == NULL)
711 return AVIERR_MEMORY;
712 This->nTableSize = 256;
713 }
714
715 if (lParam1 != 0) {
716 IAVIStream_Info((PAVISTREAM)lParam1, &This->sInfo, sizeof(This->sInfo));
717 IAVIStream_AddRef((PAVISTREAM)lParam1);
718 This->pStreams[0].pStream = (PAVISTREAM)lParam1;
719 This->pStreams[0].dwStart = This->sInfo.dwStart;
720 This->pStreams[0].dwLength = This->sInfo.dwLength;
721 This->nStreams = 1;
722 }
723 return AVIERR_OK;
724 }
725
726 static HRESULT WINAPI IEditAVIStream_fnInfo(IAVIStream*iface,
727 AVISTREAMINFOW *psi,LONG size)
728 {
729 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
730
731 TRACE("(%p,%p,%d)\n",iface,psi,size);
732
733 if (psi == NULL)
734 return AVIERR_BADPARAM;
735 if (size < 0)
736 return AVIERR_BADSIZE;
737
738 if (This->bDecompress)
739 This->sInfo.fccHandler = 0;
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 IEditAVIStream_fnFindSample(IAVIStream*iface,LONG pos,
749 LONG flags)
750 {
751 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
752 PAVISTREAM stream;
753 DWORD streamPos, streamNr;
754
755 TRACE("(%p,%d,0x%08X)\n",iface,pos,flags);
756
757 if (flags & FIND_FROM_START)
758 pos = (LONG)This->sInfo.dwStart;
759
760 /* outside of stream? */
761 if (pos < (LONG)This->sInfo.dwStart ||
762 (LONG)This->sInfo.dwStart + (LONG)This->sInfo.dwLength <= pos)
763 return -1;
764
765 /* map our position to a stream and position in it */
766 if (AVIFILE_FindStreamInTable(This, pos, &stream, &streamPos,
767 &streamNr, TRUE) != S_OK)
768 return -1; /* doesn't exist */
769
770 if (This->bDecompress) {
771 /* only one stream -- format changes only at start */
772 if (flags & FIND_FORMAT)
773 return (flags & FIND_NEXT ? -1 : 0);
774
775 /* FIXME: map positions back to us */
776 return IAVIStream_FindSample(stream, streamPos, flags);
777 } else {
778 /* assume change of format every frame */
779 return pos;
780 }
781 }
782
783 static HRESULT WINAPI IEditAVIStream_fnReadFormat(IAVIStream*iface,LONG pos,
784 LPVOID format,LONG*fmtsize)
785 {
786 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
787 LPBITMAPINFOHEADER lp;
788 PAVISTREAM stream;
789 DWORD n;
790 HRESULT hr;
791
792 TRACE("(%p,%d,%p,%p)\n",iface,pos,format,fmtsize);
793
794 if (fmtsize == NULL || pos < This->sInfo.dwStart ||
795 This->sInfo.dwStart + This->sInfo.dwLength <= pos)
796 return AVIERR_BADPARAM;
797
798 /* find stream corresponding to position */
799 hr = AVIFILE_FindStreamInTable(This, pos, &stream, &n, NULL, FALSE);
800 if (FAILED(hr))
801 return hr;
802
803 if (! This->bDecompress)
804 return IAVIStream_ReadFormat(stream, n, format, fmtsize);
805
806 lp = AVIFILE_ReadFrame(This, stream, n);
807 if (lp == NULL)
808 return AVIERR_ERROR;
809 if (lp->biBitCount <= 8) {
810 n = (lp->biClrUsed > 0 ? lp->biClrUsed : 1 << lp->biBitCount);
811 n *= sizeof(RGBQUAD);
812 } else
813 n = 0;
814 n += lp->biSize;
815
816 memcpy(format, lp, min((LONG)n, *fmtsize));
817 hr = ((LONG)n > *fmtsize ? AVIERR_BUFFERTOOSMALL : AVIERR_OK);
818 *fmtsize = n;
819
820 return hr;
821 }
822
823 static HRESULT WINAPI IEditAVIStream_fnSetFormat(IAVIStream*iface,LONG pos,
824 LPVOID format,LONG formatsize)
825 {
826 TRACE("(%p,%d,%p,%d)\n",iface,pos,format,formatsize);
827
828 return AVIERR_UNSUPPORTED;
829 }
830
831 static HRESULT WINAPI IEditAVIStream_fnRead(IAVIStream*iface,LONG start,
832 LONG samples,LPVOID buffer,
833 LONG buffersize,LONG*bytesread,
834 LONG*samplesread)
835 {
836 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
837 PAVISTREAM stream;
838 DWORD streamPos, streamNr;
839 LONG readBytes, readSamples, count;
840 HRESULT hr;
841
842 TRACE("(%p,%d,%d,%p,%d,%p,%p) -- 0x%08X\n",iface,start,samples,
843 buffer,buffersize,bytesread,samplesread,This->sInfo.fccType);
844
845 /* check parameters */
846 if (bytesread != NULL)
847 *bytesread = 0;
848 if (samplesread != NULL)
849 *samplesread = 0;
850 if (buffersize < 0)
851 return AVIERR_BADSIZE;
852 if ((DWORD)start < This->sInfo.dwStart ||
853 This->sInfo.dwStart + This->sInfo.dwLength < (DWORD)start)
854 return AVIERR_BADPARAM;
855
856 if (! This->bDecompress) {
857 /* audio like data -- sample-based */
858 do {
859 if (samples == 0)
860 return AVIERR_OK; /* nothing at all or already done */
861
862 if (FAILED(AVIFILE_FindStreamInTable(This, start, &stream,
863 &streamPos, &streamNr, FALSE)))
864 return AVIERR_ERROR;
865
866 /* limit to end of the stream */
867 count = samples;
868 if (streamPos + count > EditStreamEnd(This, streamNr))
869 count = EditStreamEnd(This, streamNr) - streamPos;
870
871 hr = IAVIStream_Read(stream, streamPos, count, buffer, buffersize,
872 &readBytes, &readSamples);
873 if (FAILED(hr))
874 return hr;
875 if (readBytes == 0 && readSamples == 0 && count != 0)
876 return AVIERR_FILEREAD; /* for bad stream implementations */
877
878 if (samplesread != NULL)
879 *samplesread += readSamples;
880 if (bytesread != NULL)
881 *bytesread += readBytes;
882 if (buffer != NULL) {
883 buffer = ((LPBYTE)buffer)+readBytes;
884 buffersize -= readBytes;
885 }
886 start += count;
887 samples -= count;
888 } while (This->sInfo.dwStart + This->sInfo.dwLength > start);
889 } else {
890 /* video like data -- frame-based */
891 LPBITMAPINFOHEADER lp;
892
893 if (samples == 0)
894 return AVIERR_OK;
895
896 if (FAILED(AVIFILE_FindStreamInTable(This, start, &stream,
897 &streamPos, &streamNr, FALSE)))
898 return AVIERR_ERROR;
899
900 lp = AVIFILE_ReadFrame(This, stream, streamPos);
901 if (lp == NULL)
902 return AVIERR_ERROR;
903
904 if (buffer != NULL) {
905 /* need size of format to skip */
906 if (lp->biBitCount <= 8) {
907 count = lp->biClrUsed > 0 ? lp->biClrUsed : 1 << lp->biBitCount;
908 count *= sizeof(RGBQUAD);
909 } else
910 count = 0;
911 count += lp->biSize;
912
913 if (buffersize < lp->biSizeImage)
914 return AVIERR_BUFFERTOOSMALL;
915 memcpy(buffer, (LPBYTE)lp + count, lp->biSizeImage);
916 }
917
918 if (bytesread != NULL)
919 *bytesread = lp->biSizeImage;
920 if (samplesread != NULL)
921 *samplesread = 1;
922 }
923
924 return AVIERR_OK;
925 }
926
927 static HRESULT WINAPI IEditAVIStream_fnWrite(IAVIStream*iface,LONG start,
928 LONG samples,LPVOID buffer,
929 LONG buffersize,DWORD flags,
930 LONG*sampwritten,LONG*byteswritten)
931 {
932 TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n",iface,start,samples,buffer,
933 buffersize,flags,sampwritten,byteswritten);
934
935 /* be sure return parameters have correct values */
936 if (sampwritten != NULL)
937 *sampwritten = 0;
938 if (byteswritten != NULL)
939 *byteswritten = 0;
940
941 return AVIERR_UNSUPPORTED;
942 }
943
944 static HRESULT WINAPI IEditAVIStream_fnDelete(IAVIStream*iface,LONG start,
945 LONG samples)
946 {
947 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
948
949 TRACE("(%p,%d,%d)\n",iface,start,samples);
950
951 return IAVIEditStream_Cut(&This->IAVIEditStream_iface,&start,&samples,NULL);
952 }
953
954 static HRESULT WINAPI IEditAVIStream_fnReadData(IAVIStream*iface,DWORD fcc,
955 LPVOID lp,LONG *lpread)
956 {
957 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
958 DWORD n;
959
960 TRACE("(%p,0x%08X,%p,%p)\n",iface,fcc,lp,lpread);
961
962 /* check parameters */
963 if (lp == NULL || lpread == NULL)
964 return AVIERR_BADPARAM;
965
966 /* simply ask every stream and return the first block found */
967 for (n = 0; n < This->nStreams; n++) {
968 HRESULT hr = IAVIStream_ReadData(This->pStreams[n].pStream,fcc,lp,lpread);
969
970 if (SUCCEEDED(hr))
971 return hr;
972 }
973
974 *lpread = 0;
975 return AVIERR_NODATA;
976 }
977
978 static HRESULT WINAPI IEditAVIStream_fnWriteData(IAVIStream*iface,DWORD fcc,
979 LPVOID lp,LONG size)
980 {
981 TRACE("(%p,0x%08X,%p,%d)\n",iface,fcc,lp,size);
982
983 return AVIERR_UNSUPPORTED;
984 }
985
986 static HRESULT WINAPI IEditAVIStream_fnSetInfo(IAVIStream*iface,
987 AVISTREAMINFOW*info,LONG len)
988 {
989 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
990
991 TRACE("(%p,%p,%d)\n",iface,info,len);
992
993 return IAVIEditStream_SetInfo(&This->IAVIEditStream_iface,info,len);
994 }
995
996 static const struct IAVIStreamVtbl ieditstast = {
997 IEditAVIStream_fnQueryInterface,
998 IEditAVIStream_fnAddRef,
999 IEditAVIStream_fnRelease,
1000 IEditAVIStream_fnCreate,
1001 IEditAVIStream_fnInfo,
1002 IEditAVIStream_fnFindSample,
1003 IEditAVIStream_fnReadFormat,
1004 IEditAVIStream_fnSetFormat,
1005 IEditAVIStream_fnRead,
1006 IEditAVIStream_fnWrite,
1007 IEditAVIStream_fnDelete,
1008 IEditAVIStream_fnReadData,
1009 IEditAVIStream_fnWriteData,
1010 IEditAVIStream_fnSetInfo
1011 };
1012
1013 PAVIEDITSTREAM AVIFILE_CreateEditStream(PAVISTREAM pstream)
1014 {
1015 IAVIEditStreamImpl *pedit = NULL;
1016
1017 pedit = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIEditStreamImpl));
1018 if (pedit == NULL)
1019 return NULL;
1020
1021 pedit->IAVIEditStream_iface.lpVtbl = &ieditstream;
1022 pedit->IAVIStream_iface.lpVtbl = &ieditstast;
1023 pedit->ref = 1;
1024
1025 IAVIStream_Create(&pedit->IAVIStream_iface, (LPARAM)pstream, 0);
1026
1027 return (PAVIEDITSTREAM)pedit;
1028 }