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