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