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
35 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
36
37 /***********************************************************************/
38
39 /* internal interface to get access to table of stream in an editable stream */
40
41 typedef struct _EditStreamTable {
42 PAVISTREAM pStream; /* stream which contains the data */
43 DWORD dwStart; /* where starts the part which is also our */
44 DWORD dwLength; /* how many is also in this stream */
45 } EditStreamTable;
46
47 #define INTERFACE IEditStreamInternal
48 DECLARE_INTERFACE_(IEditStreamInternal,IUnknown)
49 {
50 /*** IUnknown methods ***/
51 STDMETHOD_(HRESULT,QueryInterface)(THIS_ REFIID riid, void** ppvObject) PURE;
52 STDMETHOD_(ULONG,AddRef)(THIS) PURE;
53 STDMETHOD_(ULONG,Release)(THIS) PURE;
54 /*** IEditStreamInternal methods ***/
55 STDMETHOD(GetEditStreamImpl)(THIS_ LPVOID*) PURE;
56 };
57 #undef INTERFACE
58
59 #define EditStreamEnd(This,streamNr) ((This)->pStreams[streamNr].dwStart + \
60 (This)->pStreams[streamNr].dwLength)
61
62 /***********************************************************************/
63
64 static HRESULT WINAPI IAVIEditStream_fnQueryInterface(IAVIEditStream*iface,REFIID refiid,LPVOID *obj);
65 static ULONG WINAPI IAVIEditStream_fnAddRef(IAVIEditStream*iface);
66 static ULONG WINAPI IAVIEditStream_fnRelease(IAVIEditStream*iface);
67 static HRESULT WINAPI IAVIEditStream_fnCut(IAVIEditStream*iface,LONG*plStart,
68 LONG*plLength,PAVISTREAM*ppResult);
69 static HRESULT WINAPI IAVIEditStream_fnCopy(IAVIEditStream*iface,LONG*plStart,
70 LONG*plLength,PAVISTREAM*ppResult);
71 static HRESULT WINAPI IAVIEditStream_fnPaste(IAVIEditStream*iface,LONG*plStart,
72 LONG*plLength,PAVISTREAM pSource,
73 LONG lStart,LONG lEnd);
74 static HRESULT WINAPI IAVIEditStream_fnClone(IAVIEditStream*iface,
75 PAVISTREAM*ppResult);
76 static HRESULT WINAPI IAVIEditStream_fnSetInfo(IAVIEditStream*iface,
77 LPAVISTREAMINFOW asi,LONG size);
78
79 static const struct IAVIEditStreamVtbl ieditstream = {
80 IAVIEditStream_fnQueryInterface,
81 IAVIEditStream_fnAddRef,
82 IAVIEditStream_fnRelease,
83 IAVIEditStream_fnCut,
84 IAVIEditStream_fnCopy,
85 IAVIEditStream_fnPaste,
86 IAVIEditStream_fnClone,
87 IAVIEditStream_fnSetInfo
88 };
89
90 static HRESULT WINAPI IEditAVIStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID*obj);
91 static ULONG WINAPI IEditAVIStream_fnAddRef(IAVIStream*iface);
92 static ULONG WINAPI IEditAVIStream_fnRelease(IAVIStream*iface);
93 static HRESULT WINAPI IEditAVIStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2);
94 static HRESULT WINAPI IEditAVIStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size);
95 static LONG WINAPI IEditAVIStream_fnFindSample(IAVIStream*iface,LONG pos,
96 LONG flags);
97 static HRESULT WINAPI IEditAVIStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG*formatsize);
98 static HRESULT WINAPI IEditAVIStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize);
99 static HRESULT WINAPI IEditAVIStream_fnRead(IAVIStream*iface,LONG start,
100 LONG samples,LPVOID buffer,
101 LONG buffersize,LONG*bytesread,
102 LONG*samplesread);
103 static HRESULT WINAPI IEditAVIStream_fnWrite(IAVIStream*iface,LONG start,
104 LONG samples,LPVOID buffer,
105 LONG buffersize,DWORD flags,
106 LONG*sampwritten,LONG*byteswritten);
107 static HRESULT WINAPI IEditAVIStream_fnDelete(IAVIStream*iface,LONG start,LONG samples);
108 static HRESULT WINAPI IEditAVIStream_fnReadData(IAVIStream*iface,DWORD fcc,
109 LPVOID lp,LONG *lpread);
110 static HRESULT WINAPI IEditAVIStream_fnWriteData(IAVIStream*iface,DWORD fcc,
111 LPVOID lp,LONG size);
112 static HRESULT WINAPI IEditAVIStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen);
113
114 static const struct IAVIStreamVtbl ieditstast = {
115 IEditAVIStream_fnQueryInterface,
116 IEditAVIStream_fnAddRef,
117 IEditAVIStream_fnRelease,
118 IEditAVIStream_fnCreate,
119 IEditAVIStream_fnInfo,
120 IEditAVIStream_fnFindSample,
121 IEditAVIStream_fnReadFormat,
122 IEditAVIStream_fnSetFormat,
123 IEditAVIStream_fnRead,
124 IEditAVIStream_fnWrite,
125 IEditAVIStream_fnDelete,
126 IEditAVIStream_fnReadData,
127 IEditAVIStream_fnWriteData,
128 IEditAVIStream_fnSetInfo
129 };
130
131 static HRESULT WINAPI IEditStreamInternal_fnQueryInterface(IEditStreamInternal*iface,REFIID refiid,LPVOID*obj);
132 static ULONG WINAPI IEditStreamInternal_fnAddRef(IEditStreamInternal*iface);
133 static ULONG WINAPI IEditStreamInternal_fnRelease(IEditStreamInternal*iface);
134 static HRESULT WINAPI IEditStreamInternal_fnGetEditStreamImpl(IEditStreamInternal*iface,LPVOID*ppimpl);
135
136 static const struct IEditStreamInternalVtbl ieditstreaminternal = {
137 IEditStreamInternal_fnQueryInterface,
138 IEditStreamInternal_fnAddRef,
139 IEditStreamInternal_fnRelease,
140 IEditStreamInternal_fnGetEditStreamImpl
141 };
142
143 typedef struct _IAVIEditStreamImpl IAVIEditStreamImpl;
144
145 typedef struct _IEditAVIStreamImpl {
146 /* IUnknown stuff */
147 const IAVIStreamVtbl *lpVtbl;
148
149 /* IAVIStream stuff */
150 IAVIEditStreamImpl *pae;
151 } IEditAVIStreamImpl;
152
153 typedef struct _IEditStreamInternalImpl {
154 /* IUnknown stuff */
155 const IEditStreamInternalVtbl *lpVtbl;
156
157 /* IEditStreamInternal stuff */
158 IAVIEditStreamImpl *pae;
159 } IEditStreamInternalImpl;
160
161 struct _IAVIEditStreamImpl {
162 /* IUnknown stuff */
163 const IAVIEditStreamVtbl *lpVtbl;
164 LONG ref;
165
166 /* IAVIEditStream stuff */
167 IEditAVIStreamImpl iAVIStream;
168 IEditStreamInternalImpl iEditStreamInternal;
169
170 AVISTREAMINFOW sInfo;
171
172 EditStreamTable *pStreams;
173 DWORD nStreams; /* current fill level of pStreams table */
174 DWORD nTableSize; /* size of pStreams table */
175
176 BOOL bDecompress;
177 PAVISTREAM pCurStream;
178 PGETFRAME pg; /* IGetFrame for pCurStream */
179 LPBITMAPINFOHEADER lpFrame; /* frame of pCurStream */
180 };
181
182 /***********************************************************************/
183
184 PAVIEDITSTREAM AVIFILE_CreateEditStream(PAVISTREAM pstream)
185 {
186 IAVIEditStreamImpl *pedit = NULL;
187
188 pedit = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIEditStreamImpl));
189 if (pedit == NULL)
190 return NULL;
191
192 pedit->lpVtbl = &ieditstream;
193 pedit->iAVIStream.lpVtbl = &ieditstast;
194 pedit->iAVIStream.pae = pedit;
195 pedit->iEditStreamInternal.lpVtbl = &ieditstreaminternal;
196 pedit->iEditStreamInternal.pae = pedit;
197 pedit->ref = 1;
198
199 IAVIStream_Create((PAVISTREAM)&pedit->iAVIStream,(LPARAM)pstream,0);
200
201 return (PAVIEDITSTREAM)pedit;
202 }
203
204 static HRESULT AVIFILE_FindStreamInTable(IAVIEditStreamImpl* const This,
205 DWORD pos,PAVISTREAM *ppStream,
206 DWORD* streamPos,
207 DWORD* streamNr,BOOL bFindSample)
208 {
209 DWORD n;
210
211 TRACE("(%p,%u,%p,%p,%p,%d)\n",This,pos,ppStream,streamPos,
212 streamNr,bFindSample);
213
214 if (pos < This->sInfo.dwStart)
215 return AVIERR_BADPARAM;
216
217 pos -= This->sInfo.dwStart;
218 for (n = 0; n < This->nStreams; n++) {
219 if (pos < This->pStreams[n].dwLength) {
220 *ppStream = This->pStreams[n].pStream;
221 *streamPos = This->pStreams[n].dwStart + pos;
222 if (streamNr != NULL)
223 *streamNr = n;
224
225 return AVIERR_OK;
226 }
227 pos -= This->pStreams[n].dwLength;
228 }
229 if (pos == 0 && bFindSample) {
230 *ppStream = This->pStreams[--n].pStream;
231 *streamPos = EditStreamEnd(This, n);
232 if (streamNr != NULL)
233 *streamNr = n;
234
235 TRACE(" -- pos=0 && b=1 -> (%p,%u,%u)\n",*ppStream, *streamPos, n);
236 return AVIERR_OK;
237 } else {
238 *ppStream = NULL;
239 *streamPos = 0;
240 if (streamNr != NULL)
241 *streamNr = 0;
242
243 TRACE(" -> ERROR (NULL,0,0)\n");
244 return AVIERR_BADPARAM;
245 }
246 }
247
248 static LPVOID AVIFILE_ReadFrame(IAVIEditStreamImpl* const This,
249 PAVISTREAM pstream, LONG pos)
250 {
251 PGETFRAME pg;
252
253 TRACE("(%p,%p,%d)\n",This,pstream,pos);
254
255 if (pstream == NULL)
256 return NULL;
257
258 /* if stream changes make sure that only palette changes */
259 if (This->pCurStream != pstream) {
260 pg = AVIStreamGetFrameOpen(pstream, NULL);
261 if (pg == NULL)
262 return NULL;
263 if (This->pg != NULL) {
264 if (IGetFrame_SetFormat(pg, This->lpFrame, NULL, 0, 0, -1, -1)) {
265 AVIStreamGetFrameClose(pg);
266 ERR(": IGetFrame_SetFormat failed\n");
267 return NULL;
268 }
269 AVIStreamGetFrameClose(This->pg);
270 }
271 This->pg = pg;
272 This->pCurStream = pstream;
273 }
274
275 /* now get the decompressed frame */
276 This->lpFrame = AVIStreamGetFrame(This->pg, pos);
277 if (This->lpFrame != NULL)
278 This->sInfo.dwSuggestedBufferSize = This->lpFrame->biSizeImage;
279
280 return This->lpFrame;
281 }
282
283 static HRESULT AVIFILE_RemoveStream(IAVIEditStreamImpl* const This, DWORD nr)
284 {
285 assert(This != NULL);
286 assert(nr < This->nStreams);
287
288 /* remove part nr */
289 IAVIStream_Release(This->pStreams[nr].pStream);
290 This->nStreams--;
291 if (This->nStreams - nr > 0) {
292 memmove(This->pStreams + nr, This->pStreams + nr + 1,
293 (This->nStreams - nr) * sizeof(EditStreamTable));
294 }
295 This->pStreams[This->nStreams].pStream = NULL;
296 This->pStreams[This->nStreams].dwStart = 0;
297 This->pStreams[This->nStreams].dwLength = 0;
298
299 /* try to merge the part before the deleted one and the one after it */
300 if (0 < nr && 0 < This->nStreams &&
301 This->pStreams[nr - 1].pStream == This->pStreams[nr].pStream) {
302 if (EditStreamEnd(This, nr - 1) == This->pStreams[nr].dwStart) {
303 This->pStreams[nr - 1].dwLength += This->pStreams[nr].dwLength;
304 return AVIFILE_RemoveStream(This, nr);
305 }
306 }
307
308 return AVIERR_OK;
309 }
310
311 static BOOL AVIFILE_FormatsEqual(PAVISTREAM avi1, PAVISTREAM avi2)
312 {
313 LPVOID fmt1 = NULL, fmt2 = NULL;
314 LONG size1, size2, start1, start2;
315 BOOL status = FALSE;
316
317 assert(avi1 != NULL && avi2 != NULL);
318
319 /* get stream starts and check format sizes */
320 start1 = AVIStreamStart(avi1);
321 start2 = AVIStreamStart(avi2);
322 if (FAILED(AVIStreamFormatSize(avi1, start1, &size1)))
323 return FALSE;
324 if (FAILED(AVIStreamFormatSize(avi2, start2, &size2)))
325 return FALSE;
326 if (size1 != size2)
327 return FALSE;
328
329 /* sizes match, now get formats and compare them */
330 fmt1 = HeapAlloc(GetProcessHeap(), 0, size1);
331 if (fmt1 == NULL)
332 return FALSE;
333 if (SUCCEEDED(AVIStreamReadFormat(avi1, start1, fmt1, &size1))) {
334 fmt2 = HeapAlloc(GetProcessHeap(), 0, size1);
335 if (fmt2 != NULL) {
336 if (SUCCEEDED(AVIStreamReadFormat(avi2, start2, fmt2, &size1)))
337 status = (memcmp(fmt1, fmt2, size1) == 0);
338 }
339 }
340
341 HeapFree(GetProcessHeap(), 0, fmt2);
342 HeapFree(GetProcessHeap(), 0, fmt1);
343
344 return status;
345 }
346
347 /***********************************************************************/
348
349 static HRESULT WINAPI IAVIEditStream_fnQueryInterface(IAVIEditStream*iface,REFIID refiid,LPVOID *obj)
350 {
351 IAVIEditStreamImpl *This = (IAVIEditStreamImpl *)iface;
352
353 TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj);
354
355 if (IsEqualGUID(&IID_IUnknown, refiid) ||
356 IsEqualGUID(&IID_IAVIEditStream, refiid)) {
357 *obj = iface;
358 IAVIEditStream_AddRef(iface);
359
360 return S_OK;
361 } else if (IsEqualGUID(&IID_IAVIStream, refiid)) {
362 *obj = &This->iAVIStream;
363 IAVIEditStream_AddRef(iface);
364
365 return S_OK;
366 } else if (IsEqualGUID(&IID_IEditStreamInternal, refiid)) {
367 *obj = &This->iEditStreamInternal;
368 IAVIEditStream_AddRef(iface);
369
370 return S_OK;
371 }
372
373 return OLE_E_ENUM_NOMORE;
374 }
375
376 static ULONG WINAPI IAVIEditStream_fnAddRef(IAVIEditStream*iface)
377 {
378 IAVIEditStreamImpl *This = (IAVIEditStreamImpl *)iface;
379 ULONG ref = InterlockedIncrement(&This->ref);
380
381 TRACE("(%p) -> %d\n", iface, ref);
382
383 return ref;
384 }
385
386 static ULONG WINAPI IAVIEditStream_fnRelease(IAVIEditStream*iface)
387 {
388 IAVIEditStreamImpl *This = (IAVIEditStreamImpl *)iface;
389 DWORD i;
390 ULONG ref = InterlockedDecrement(&This->ref);
391
392 TRACE("(%p) -> %d\n", iface, ref);
393
394 if (!ref) {
395 /* release memory */
396 if (This->pg != NULL)
397 AVIStreamGetFrameClose(This->pg);
398 if (This->pStreams != NULL) {
399 for (i = 0; i < This->nStreams; i++) {
400 if (This->pStreams[i].pStream != NULL)
401 IAVIStream_Release(This->pStreams[i].pStream);
402 }
403 HeapFree(GetProcessHeap(), 0, This->pStreams);
404 }
405
406 HeapFree(GetProcessHeap(), 0, This);
407 return 0;
408 }
409 return ref;
410 }
411
412 static HRESULT WINAPI IAVIEditStream_fnCut(IAVIEditStream*iface,LONG*plStart,
413 LONG*plLength,PAVISTREAM*ppResult)
414 {
415 IAVIEditStreamImpl *This = (IAVIEditStreamImpl *)iface;
416 PAVISTREAM stream;
417 DWORD start, len, streamPos, streamNr;
418 HRESULT hr;
419
420 TRACE("(%p,%p,%p,%p)\n",iface,plStart,plLength,ppResult);
421
422 if (ppResult != NULL)
423 *ppResult = NULL;
424 if (plStart == NULL || plLength == NULL || *plStart < 0)
425 return AVIERR_BADPARAM;
426
427 /* if asked for cut part copy it before deleting */
428 if (ppResult != NULL) {
429 hr = IAVIEditStream_Copy(iface, plStart, plLength, ppResult);
430 if (FAILED(hr))
431 return hr;
432 }
433
434 start = *plStart;
435 len = *plLength;
436
437 /* now delete the requested part */
438 while (len > 0) {
439 hr = AVIFILE_FindStreamInTable(This, start, &stream,
440 &streamPos, &streamNr, FALSE);
441 if (FAILED(hr))
442 return hr;
443 if (This->pStreams[streamNr].dwStart == streamPos) {
444 /* deleting from start of part */
445 if (len < This->pStreams[streamNr].dwLength) {
446 start += len;
447 This->pStreams[streamNr].dwStart += len;
448 This->pStreams[streamNr].dwLength -= len;
449 This->sInfo.dwLength -= len;
450 len = 0;
451
452 /* we must return decompressed data now */
453 This->bDecompress = TRUE;
454 } else {
455 /* deleting hole part */
456 len -= This->pStreams[streamNr].dwLength;
457 AVIFILE_RemoveStream(This,streamNr);
458 }
459 } else if (EditStreamEnd(This, streamNr) <= streamPos + len) {
460 /* deleting at end of a part */
461 DWORD count = EditStreamEnd(This, streamNr) - streamPos;
462 This->sInfo.dwLength -= count;
463 len -= count;
464 This->pStreams[streamNr].dwLength =
465 streamPos - This->pStreams[streamNr].dwStart;
466 } else {
467 /* splitting */
468 if (This->nStreams + 1 >= This->nTableSize) {
469 This->pStreams = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->pStreams,
470 (This->nTableSize + 32) * sizeof(EditStreamTable));
471 if (This->pStreams == NULL)
472 return AVIERR_MEMORY;
473 This->nTableSize += 32;
474 }
475 memmove(This->pStreams + streamNr + 1, This->pStreams + streamNr,
476 (This->nStreams - streamNr) * sizeof(EditStreamTable));
477 This->nStreams++;
478
479 IAVIStream_AddRef(This->pStreams[streamNr + 1].pStream);
480 This->pStreams[streamNr + 1].dwStart = streamPos + len;
481 This->pStreams[streamNr + 1].dwLength =
482 EditStreamEnd(This, streamNr) - This->pStreams[streamNr + 1].dwStart;
483
484 This->pStreams[streamNr].dwLength =
485 streamPos - This->pStreams[streamNr].dwStart;
486 This->sInfo.dwLength -= len;
487 len = 0;
488 }
489 }
490
491 This->sInfo.dwEditCount++;
492
493 return AVIERR_OK;
494 }
495
496 static HRESULT WINAPI IAVIEditStream_fnCopy(IAVIEditStream*iface,LONG*plStart,
497 LONG*plLength,PAVISTREAM*ppResult)
498 {
499 IAVIEditStreamImpl *This = (IAVIEditStreamImpl *)iface;
500 IAVIEditStreamImpl* pEdit;
501 HRESULT hr;
502 LONG start = 0;
503
504 TRACE("(%p,%p,%p,%p)\n",iface,plStart,plLength,ppResult);
505
506 if (ppResult == NULL)
507 return AVIERR_BADPARAM;
508 *ppResult = NULL;
509 if (plStart == NULL || plLength == NULL || *plStart < 0 || *plLength < 0)
510 return AVIERR_BADPARAM;
511
512 /* check bounds */
513 if (*(LPDWORD)plLength > This->sInfo.dwLength)
514 *(LPDWORD)plLength = This->sInfo.dwLength;
515 if (*(LPDWORD)plStart < This->sInfo.dwStart) {
516 *(LPDWORD)plLength -= This->sInfo.dwStart - *(LPDWORD)plStart;
517 *(LPDWORD)plStart = This->sInfo.dwStart;
518 if (*plLength < 0)
519 return AVIERR_BADPARAM;
520 }
521 if (*(LPDWORD)plStart + *(LPDWORD)plLength > This->sInfo.dwStart + This->sInfo.dwLength)
522 *(LPDWORD)plLength = This->sInfo.dwStart + This->sInfo.dwLength -
523 *(LPDWORD)plStart;
524
525 pEdit = (IAVIEditStreamImpl*)AVIFILE_CreateEditStream(NULL);
526 if (pEdit == NULL)
527 return AVIERR_MEMORY;
528
529 hr = IAVIEditStream_Paste((PAVIEDITSTREAM)pEdit,&start,plLength,
530 (PAVISTREAM)&This->iAVIStream,*plStart,
531 *plStart + *plLength);
532 *plStart = start;
533 if (FAILED(hr))
534 IAVIEditStream_Release((PAVIEDITSTREAM)pEdit);
535 else
536 *ppResult = (PAVISTREAM)&pEdit->iAVIStream;
537
538 return hr;
539 }
540
541 static HRESULT WINAPI IAVIEditStream_fnPaste(IAVIEditStream*iface,LONG*plStart,
542 LONG*plLength,PAVISTREAM pSource,
543 LONG lStart,LONG lLength)
544 {
545 IAVIEditStreamImpl *This = (IAVIEditStreamImpl *)iface;
546 AVISTREAMINFOW srcInfo;
547 IEditStreamInternal*pInternal = NULL;
548 IAVIEditStreamImpl *pEdit = NULL;
549 PAVISTREAM pStream;
550 DWORD startPos, endPos, streamNr, nStreams;
551 ULONG n;
552
553 TRACE("(%p,%p,%p,%p,%d,%d)\n",iface,plStart,plLength,
554 pSource,lStart,lLength);
555
556 if (pSource == NULL)
557 return AVIERR_BADHANDLE;
558 if (plStart == NULL || *plStart < 0)
559 return AVIERR_BADPARAM;
560 if (This->sInfo.dwStart + This->sInfo.dwLength < *plStart)
561 return AVIERR_BADPARAM; /* Can't paste with holes */
562 if (FAILED(IAVIStream_Info(pSource, &srcInfo, sizeof(srcInfo))))
563 return AVIERR_ERROR;
564 if (lStart < srcInfo.dwStart || lStart >= srcInfo.dwStart + srcInfo.dwLength)
565 return AVIERR_BADPARAM;
566 if (This->sInfo.fccType == 0) {
567 /* This stream is empty */
568 IAVIStream_Info(pSource, &This->sInfo, sizeof(This->sInfo));
569 This->sInfo.dwStart = *plStart;
570 This->sInfo.dwLength = 0;
571 }
572 if (This->sInfo.fccType != srcInfo.fccType)
573 return AVIERR_UNSUPPORTED; /* different stream types */
574 if (lLength == -1) /* Copy the hole stream */
575 lLength = srcInfo.dwLength;
576 if (lStart + lLength > srcInfo.dwStart + srcInfo.dwLength)
577 lLength = srcInfo.dwStart + srcInfo.dwLength - lStart;
578 if (lLength + *plStart >= 0x80000000)
579 return AVIERR_MEMORY;
580
581 /* streamtype specific tests */
582 if (srcInfo.fccType == streamtypeVIDEO) {
583 LONG size;
584
585 size = srcInfo.rcFrame.right - srcInfo.rcFrame.left;
586 if (size != This->sInfo.rcFrame.right - This->sInfo.rcFrame.left)
587 return AVIERR_UNSUPPORTED; /* FIXME: Can't GetFrame convert it? */
588 size = srcInfo.rcFrame.bottom - srcInfo.rcFrame.top;
589 if (size != This->sInfo.rcFrame.bottom - This->sInfo.rcFrame.top)
590 return AVIERR_UNSUPPORTED; /* FIXME: Can't GetFrame convert it? */
591 } else if (srcInfo.fccType == streamtypeAUDIO) {
592 if (! AVIFILE_FormatsEqual((PAVISTREAM)&This->iAVIStream, pSource))
593 return AVIERR_UNSUPPORTED;
594 } else {
595 /* FIXME: streamtypeMIDI and streamtypeTEXT */
596 return AVIERR_UNSUPPORTED;
597 }
598
599 /* try to get an IEditStreamInternal interface */
600 if (SUCCEEDED(IAVIStream_QueryInterface(pSource, &IID_IEditStreamInternal,
601 (LPVOID*)&pInternal))) {
602 pInternal->lpVtbl->GetEditStreamImpl(pInternal, (LPVOID*)&pEdit);
603 pInternal->lpVtbl->Release(pInternal);
604 }
605
606 /* for video must check for change of format */
607 if (This->sInfo.fccType == streamtypeVIDEO) {
608 if (! This->bDecompress) {
609 /* Need to decompress if any of the following conditions matches:
610 * - pSource is an editable stream which decompresses
611 * - the nearest keyframe of pSource isn't lStart
612 * - the nearest keyframe of this stream isn't *plStart
613 * - the format of pSource doesn't match this one
614 */
615 if ((pEdit != NULL && pEdit->bDecompress) ||
616 AVIStreamNearestKeyFrame(pSource, lStart) != lStart ||
617 AVIStreamNearestKeyFrame((PAVISTREAM)&This->iAVIStream, *plStart) != *plStart ||
618 (This->nStreams > 0 && !AVIFILE_FormatsEqual((PAVISTREAM)&This->iAVIStream, pSource))) {
619 /* Use first stream part to get format to convert everything to */
620 AVIFILE_ReadFrame(This, This->pStreams[0].pStream,
621 This->pStreams[0].dwStart);
622
623 /* Check if we could convert the source streams to the desired format... */
624 if (pEdit != NULL) {
625 if (FAILED(AVIFILE_FindStreamInTable(pEdit, lStart, &pStream,
626 &startPos, &streamNr, TRUE)))
627 return AVIERR_INTERNAL;
628 for (n = lStart; n < lStart + lLength; streamNr++) {
629 if (AVIFILE_ReadFrame(This, pEdit->pStreams[streamNr].pStream, startPos) == NULL)
630 return AVIERR_BADFORMAT;
631 startPos = pEdit->pStreams[streamNr].dwStart;
632 n += pEdit->pStreams[streamNr].dwLength;
633 }
634 } else if (AVIFILE_ReadFrame(This, pSource, lStart) == NULL)
635 return AVIERR_BADFORMAT;
636
637 This->bDecompress = TRUE;
638 This->sInfo.fccHandler = 0;
639 }
640 } else if (AVIFILE_ReadFrame(This, pSource, lStart) == NULL)
641 return AVIERR_BADFORMAT; /* Can't convert source to own format */
642 } /* FIXME: something special for the other formats? */
643
644 /* Make sure we have enough memory for parts */
645 if (pEdit != NULL) {
646 DWORD nLastStream;
647
648 AVIFILE_FindStreamInTable(pEdit, lStart + lLength, &pStream,
649 &endPos, &nLastStream, TRUE);
650 AVIFILE_FindStreamInTable(pEdit, lStart, &pStream,
651 &startPos, &streamNr, FALSE);
652 if (nLastStream == streamNr)
653 nLastStream++;
654
655 nStreams = nLastStream - streamNr;
656 } else
657 nStreams = 1;
658 if (This->nStreams + nStreams + 1 > This->nTableSize) {
659 n = This->nStreams + nStreams + 33;
660
661 This->pStreams = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->pStreams, n * sizeof(EditStreamTable));
662 if (This->pStreams == NULL)
663 return AVIERR_MEMORY;
664 This->nTableSize = n;
665 }
666
667 if (plLength != NULL)
668 *plLength = lLength;
669
670 /* now do the real work */
671 if (This->sInfo.dwStart + This->sInfo.dwLength > *plStart) {
672 AVIFILE_FindStreamInTable(This, *plStart, &pStream,
673 &startPos, &streamNr, FALSE);
674 if (startPos != This->pStreams[streamNr].dwStart) {
675 /* split stream streamNr at startPos */
676 memmove(This->pStreams + streamNr + nStreams + 1,
677 This->pStreams + streamNr,
678 (This->nStreams + nStreams - streamNr + 1) * sizeof(EditStreamTable));
679
680 This->pStreams[streamNr + 2].dwLength =
681 EditStreamEnd(This, streamNr + 2) - startPos;
682 This->pStreams[streamNr + 2].dwStart = startPos;
683 This->pStreams[streamNr].dwLength =
684 startPos - This->pStreams[streamNr].dwStart;
685 IAVIStream_AddRef(This->pStreams[streamNr].pStream);
686 streamNr++;
687 } else {
688 /* insert before stream at streamNr */
689 memmove(This->pStreams + streamNr + nStreams, This->pStreams + streamNr,
690 (This->nStreams + nStreams - streamNr) * sizeof(EditStreamTable));
691 }
692 } else /* append the streams */
693 streamNr = This->nStreams;
694
695 if (pEdit != NULL) {
696 /* insert the parts of the editable stream instead of itself */
697 AVIFILE_FindStreamInTable(pEdit, lStart + lLength, &pStream,
698 &endPos, NULL, FALSE);
699 AVIFILE_FindStreamInTable(pEdit, lStart, &pStream, &startPos, &n, FALSE);
700
701 memcpy(This->pStreams + streamNr, pEdit->pStreams + n,
702 nStreams * sizeof(EditStreamTable));
703 if (This->pStreams[streamNr].dwStart < startPos) {
704 This->pStreams[streamNr].dwLength =
705 EditStreamEnd(This, streamNr) - startPos;
706 This->pStreams[streamNr].dwStart = startPos;
707 }
708 if (endPos < EditStreamEnd(This, streamNr + nStreams))
709 This->pStreams[streamNr + nStreams].dwLength =
710 endPos - This->pStreams[streamNr + nStreams].dwStart;
711 } else {
712 /* a simple stream */
713 This->pStreams[streamNr].pStream = pSource;
714 This->pStreams[streamNr].dwStart = lStart;
715 This->pStreams[streamNr].dwLength = lLength;
716 }
717
718 for (n = 0; n < nStreams; n++) {
719 IAVIStream_AddRef(This->pStreams[streamNr + n].pStream);
720 if (0 < streamNr + n &&
721 This->pStreams[streamNr + n - 1].pStream != This->pStreams[streamNr + n].pStream) {
722 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
723 This->sInfo.dwFormatChangeCount++;
724 }
725 }
726 This->sInfo.dwEditCount++;
727 This->sInfo.dwLength += lLength;
728 This->nStreams += nStreams;
729
730 return AVIERR_OK;
731 }
732
733 static HRESULT WINAPI IAVIEditStream_fnClone(IAVIEditStream*iface,
734 PAVISTREAM*ppResult)
735 {
736 IAVIEditStreamImpl *This = (IAVIEditStreamImpl *)iface;
737 IAVIEditStreamImpl* pEdit;
738 DWORD i;
739
740 TRACE("(%p,%p)\n",iface,ppResult);
741
742 if (ppResult == NULL)
743 return AVIERR_BADPARAM;
744 *ppResult = NULL;
745
746 pEdit = (IAVIEditStreamImpl*)AVIFILE_CreateEditStream(NULL);
747 if (pEdit == NULL)
748 return AVIERR_MEMORY;
749 if (This->nStreams > pEdit->nTableSize) {
750 pEdit->pStreams = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pEdit->pStreams,
751 This->nStreams * sizeof(EditStreamTable));
752 if (pEdit->pStreams == NULL)
753 return AVIERR_MEMORY;
754 pEdit->nTableSize = This->nStreams;
755 }
756 pEdit->nStreams = This->nStreams;
757 memcpy(pEdit->pStreams, This->pStreams,
758 This->nStreams * sizeof(EditStreamTable));
759 memcpy(&pEdit->sInfo,&This->sInfo,sizeof(This->sInfo));
760 for (i = 0; i < This->nStreams; i++) {
761 if (pEdit->pStreams[i].pStream != NULL)
762 IAVIStream_AddRef(pEdit->pStreams[i].pStream);
763 }
764
765 *ppResult = (PAVISTREAM)&pEdit->iAVIStream;
766
767 return AVIERR_OK;
768 }
769
770 static HRESULT WINAPI IAVIEditStream_fnSetInfo(IAVIEditStream*iface,
771 LPAVISTREAMINFOW asi,LONG size)
772 {
773 IAVIEditStreamImpl *This = (IAVIEditStreamImpl *)iface;
774
775 TRACE("(%p,%p,%d)\n",iface,asi,size);
776
777 /* check parameters */
778 if (asi == NULL)
779 return AVIERR_BADPARAM;
780 if (size != sizeof(AVISTREAMINFOW))
781 return AVIERR_BADSIZE;
782 if (asi->dwScale == 0 || asi->dwRate == 0 || (LONG)asi->dwQuality < -1 ||
783 asi->dwQuality > ICQUALITY_HIGH)
784 return AVIERR_ERROR;
785
786 This->sInfo.wLanguage = asi->wLanguage;
787 This->sInfo.wPriority = asi->wPriority;
788 This->sInfo.dwStart = asi->dwStart;
789 if (asi->dwRate != 0)
790 This->sInfo.dwRate = asi->dwRate;
791 if (asi->dwScale != 0)
792 This->sInfo.dwScale = asi->dwScale;
793 if (asi->dwQuality <= ICQUALITY_HIGH)
794 This->sInfo.dwQuality = ICQUALITY_HIGH;
795 CopyRect(&This->sInfo.rcFrame, &asi->rcFrame);
796 memcpy(&This->sInfo.szName, &asi->szName, sizeof(asi->szName));
797 This->sInfo.dwEditCount++;
798
799 return AVIERR_OK;
800 }
801
802 static HRESULT WINAPI IEditAVIStream_fnQueryInterface(IAVIStream*iface,
803 REFIID refiid,LPVOID*obj)
804 {
805 IEditAVIStreamImpl *This = (IEditAVIStreamImpl *)iface;
806
807 assert(This->pae != NULL);
808
809 return IAVIEditStream_QueryInterface((IAVIEditStream*)This->pae,refiid,obj);
810 }
811
812 static ULONG WINAPI IEditAVIStr