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