comctl32: Use SetRect() instead of open coding it.
[wine.git] / dlls / avifil32 / editstream.c
blob3aa8637ae1a4fa22fb774ad6768f454be933700e
1 /*
2 * Copyright 2003 Michael Günnewig
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.
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.
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
19 #include <assert.h>
20 #include <stdarg.h>
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"
30 #include "avifile_private.h"
31 #include "extrachunk.h"
33 #include "wine/debug.h"
34 #include "initguid.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
38 /***********************************************************************/
40 /* internal interface to get access to table of stream in an editable stream */
42 DEFINE_AVIGUID(IID_IEditStreamInternal, 0x0002000A,0,0);
44 typedef struct _EditStreamTable {
45 PAVISTREAM pStream; /* stream which contains the data */
46 DWORD dwStart; /* where starts the part which is also our */
47 DWORD dwLength; /* how many is also in this stream */
48 } EditStreamTable;
50 #define EditStreamEnd(This,streamNr) ((This)->pStreams[streamNr].dwStart + \
51 (This)->pStreams[streamNr].dwLength)
53 typedef struct _IAVIEditStreamImpl IAVIEditStreamImpl;
55 struct _IAVIEditStreamImpl {
56 IAVIEditStream IAVIEditStream_iface;
57 IAVIStream IAVIStream_iface;
59 LONG ref;
61 AVISTREAMINFOW sInfo;
63 EditStreamTable *pStreams;
64 DWORD nStreams; /* current fill level of pStreams table */
65 DWORD nTableSize; /* size of pStreams table */
67 BOOL bDecompress;
68 PAVISTREAM pCurStream;
69 PGETFRAME pg; /* IGetFrame for pCurStream */
70 LPBITMAPINFOHEADER lpFrame; /* frame of pCurStream */
73 static inline IAVIEditStreamImpl *impl_from_IAVIEditStream(IAVIEditStream *iface)
75 return CONTAINING_RECORD(iface, IAVIEditStreamImpl, IAVIEditStream_iface);
78 static inline IAVIEditStreamImpl *impl_from_IAVIStream(IAVIStream *iface)
80 return CONTAINING_RECORD(iface, IAVIEditStreamImpl, IAVIStream_iface);
83 /***********************************************************************/
85 static HRESULT AVIFILE_FindStreamInTable(IAVIEditStreamImpl* const This,
86 DWORD pos,PAVISTREAM *ppStream,
87 DWORD* streamPos,
88 DWORD* streamNr,BOOL bFindSample)
90 DWORD n;
92 TRACE("(%p,%u,%p,%p,%p,%d)\n",This,pos,ppStream,streamPos,
93 streamNr,bFindSample);
95 if (pos < This->sInfo.dwStart)
96 return AVIERR_BADPARAM;
98 pos -= This->sInfo.dwStart;
99 for (n = 0; n < This->nStreams; n++) {
100 if (pos < This->pStreams[n].dwLength) {
101 *ppStream = This->pStreams[n].pStream;
102 *streamPos = This->pStreams[n].dwStart + pos;
103 if (streamNr != NULL)
104 *streamNr = n;
106 return AVIERR_OK;
108 pos -= This->pStreams[n].dwLength;
110 if (pos == 0 && bFindSample) {
111 *ppStream = This->pStreams[--n].pStream;
112 *streamPos = EditStreamEnd(This, n);
113 if (streamNr != NULL)
114 *streamNr = n;
116 TRACE(" -- pos=0 && b=1 -> (%p,%u,%u)\n",*ppStream, *streamPos, n);
117 return AVIERR_OK;
118 } else {
119 *ppStream = NULL;
120 *streamPos = 0;
121 if (streamNr != NULL)
122 *streamNr = 0;
124 TRACE(" -> ERROR (NULL,0,0)\n");
125 return AVIERR_BADPARAM;
129 static LPVOID AVIFILE_ReadFrame(IAVIEditStreamImpl* const This,
130 PAVISTREAM pstream, LONG pos)
132 PGETFRAME pg;
134 TRACE("(%p,%p,%d)\n",This,pstream,pos);
136 if (pstream == NULL)
137 return NULL;
139 /* if stream changes make sure that only palette changes */
140 if (This->pCurStream != pstream) {
141 pg = AVIStreamGetFrameOpen(pstream, NULL);
142 if (pg == NULL)
143 return NULL;
144 if (This->pg != NULL) {
145 if (IGetFrame_SetFormat(pg, This->lpFrame, NULL, 0, 0, -1, -1) != S_OK) {
146 AVIStreamGetFrameClose(pg);
147 ERR(": IGetFrame_SetFormat failed\n");
148 return NULL;
150 AVIStreamGetFrameClose(This->pg);
152 This->pg = pg;
153 This->pCurStream = pstream;
156 /* now get the decompressed frame */
157 This->lpFrame = AVIStreamGetFrame(This->pg, pos);
158 if (This->lpFrame != NULL)
159 This->sInfo.dwSuggestedBufferSize = This->lpFrame->biSizeImage;
161 return This->lpFrame;
164 static HRESULT AVIFILE_RemoveStream(IAVIEditStreamImpl* const This, DWORD nr)
166 assert(This != NULL);
167 assert(nr < This->nStreams);
169 /* remove part nr */
170 IAVIStream_Release(This->pStreams[nr].pStream);
171 This->nStreams--;
172 if (nr < This->nStreams)
173 memmove(&This->pStreams[nr], &This->pStreams[nr + 1],
174 (This->nStreams - nr) * sizeof(This->pStreams[0]));
175 This->pStreams[This->nStreams].pStream = NULL;
176 This->pStreams[This->nStreams].dwStart = 0;
177 This->pStreams[This->nStreams].dwLength = 0;
179 /* try to merge the part before the deleted one and the one after it */
180 if (0 < nr && 0 < This->nStreams &&
181 This->pStreams[nr - 1].pStream == This->pStreams[nr].pStream) {
182 if (EditStreamEnd(This, nr - 1) == This->pStreams[nr].dwStart) {
183 This->pStreams[nr - 1].dwLength += This->pStreams[nr].dwLength;
184 return AVIFILE_RemoveStream(This, nr);
188 return AVIERR_OK;
191 static BOOL AVIFILE_FormatsEqual(PAVISTREAM avi1, PAVISTREAM avi2)
193 LPVOID fmt1 = NULL, fmt2 = NULL;
194 LONG size1, size2, start1, start2;
195 BOOL status = FALSE;
197 assert(avi1 != NULL && avi2 != NULL);
199 /* get stream starts and check format sizes */
200 start1 = AVIStreamStart(avi1);
201 start2 = AVIStreamStart(avi2);
202 if (FAILED(AVIStreamFormatSize(avi1, start1, &size1)))
203 return FALSE;
204 if (FAILED(AVIStreamFormatSize(avi2, start2, &size2)))
205 return FALSE;
206 if (size1 != size2)
207 return FALSE;
209 /* sizes match, now get formats and compare them */
210 fmt1 = HeapAlloc(GetProcessHeap(), 0, size1);
211 if (fmt1 == NULL)
212 return FALSE;
213 if (SUCCEEDED(AVIStreamReadFormat(avi1, start1, fmt1, &size1))) {
214 fmt2 = HeapAlloc(GetProcessHeap(), 0, size1);
215 if (fmt2 != NULL) {
216 if (SUCCEEDED(AVIStreamReadFormat(avi2, start2, fmt2, &size1)))
217 status = (memcmp(fmt1, fmt2, size1) == 0);
221 HeapFree(GetProcessHeap(), 0, fmt2);
222 HeapFree(GetProcessHeap(), 0, fmt1);
224 return status;
227 /***********************************************************************/
229 static HRESULT WINAPI IAVIEditStream_fnQueryInterface(IAVIEditStream*iface,REFIID refiid,LPVOID *obj)
231 IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
233 TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj);
235 if (IsEqualGUID(&IID_IUnknown, refiid) ||
236 IsEqualGUID(&IID_IAVIEditStream, refiid) ||
237 IsEqualGUID(&IID_IEditStreamInternal, refiid)) {
238 *obj = iface;
239 IAVIEditStream_AddRef(iface);
241 return S_OK;
242 } else if (IsEqualGUID(&IID_IAVIStream, refiid)) {
243 *obj = &This->IAVIStream_iface;
244 IAVIEditStream_AddRef(iface);
246 return S_OK;
249 return E_NOINTERFACE;
252 static ULONG WINAPI IAVIEditStream_fnAddRef(IAVIEditStream*iface)
254 IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
255 ULONG ref = InterlockedIncrement(&This->ref);
257 TRACE("(%p) -> %d\n", iface, ref);
259 return ref;
262 static ULONG WINAPI IAVIEditStream_fnRelease(IAVIEditStream*iface)
264 IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
265 DWORD i;
266 ULONG ref = InterlockedDecrement(&This->ref);
268 TRACE("(%p) -> %d\n", iface, ref);
270 if (!ref) {
271 /* release memory */
272 if (This->pg != NULL)
273 AVIStreamGetFrameClose(This->pg);
274 if (This->pStreams != NULL) {
275 for (i = 0; i < This->nStreams; i++) {
276 if (This->pStreams[i].pStream != NULL)
277 IAVIStream_Release(This->pStreams[i].pStream);
279 HeapFree(GetProcessHeap(), 0, This->pStreams);
282 HeapFree(GetProcessHeap(), 0, This);
283 return 0;
285 return ref;
288 static HRESULT WINAPI IAVIEditStream_fnCut(IAVIEditStream*iface,LONG*plStart,
289 LONG*plLength,PAVISTREAM*ppResult)
291 IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
292 PAVISTREAM stream;
293 DWORD start, len, streamPos, streamNr;
294 HRESULT hr;
296 TRACE("(%p,%p,%p,%p)\n",iface,plStart,plLength,ppResult);
298 if (ppResult != NULL)
299 *ppResult = NULL;
300 if (plStart == NULL || plLength == NULL || *plStart < 0)
301 return AVIERR_BADPARAM;
303 /* if asked for cut part copy it before deleting */
304 if (ppResult != NULL) {
305 hr = IAVIEditStream_Copy(iface, plStart, plLength, ppResult);
306 if (FAILED(hr))
307 return hr;
310 start = *plStart;
311 len = *plLength;
313 /* now delete the requested part */
314 while (len > 0) {
315 hr = AVIFILE_FindStreamInTable(This, start, &stream,
316 &streamPos, &streamNr, FALSE);
317 if (FAILED(hr))
318 return hr;
319 if (This->pStreams[streamNr].dwStart == streamPos) {
320 /* deleting from start of part */
321 if (len < This->pStreams[streamNr].dwLength) {
322 start += len;
323 This->pStreams[streamNr].dwStart += len;
324 This->pStreams[streamNr].dwLength -= len;
325 This->sInfo.dwLength -= len;
326 len = 0;
328 /* we must return decompressed data now */
329 This->bDecompress = TRUE;
330 } else {
331 /* deleting hole part */
332 len -= This->pStreams[streamNr].dwLength;
333 AVIFILE_RemoveStream(This,streamNr);
335 } else if (EditStreamEnd(This, streamNr) <= streamPos + len) {
336 /* deleting at end of a part */
337 DWORD count = EditStreamEnd(This, streamNr) - streamPos;
338 This->sInfo.dwLength -= count;
339 len -= count;
340 This->pStreams[streamNr].dwLength =
341 streamPos - This->pStreams[streamNr].dwStart;
342 } else {
343 /* splitting */
344 if (This->nStreams + 1 >= This->nTableSize) {
345 This->pStreams = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->pStreams,
346 (This->nTableSize + 32) * sizeof(EditStreamTable));
347 if (This->pStreams == NULL)
348 return AVIERR_MEMORY;
349 This->nTableSize += 32;
351 memmove(This->pStreams + streamNr + 1, This->pStreams + streamNr,
352 (This->nStreams - streamNr) * sizeof(EditStreamTable));
353 This->nStreams++;
355 IAVIStream_AddRef(This->pStreams[streamNr + 1].pStream);
356 This->pStreams[streamNr + 1].dwStart = streamPos + len;
357 This->pStreams[streamNr + 1].dwLength =
358 EditStreamEnd(This, streamNr) - This->pStreams[streamNr + 1].dwStart;
360 This->pStreams[streamNr].dwLength =
361 streamPos - This->pStreams[streamNr].dwStart;
362 This->sInfo.dwLength -= len;
363 len = 0;
367 This->sInfo.dwEditCount++;
369 return AVIERR_OK;
372 static HRESULT WINAPI IAVIEditStream_fnCopy(IAVIEditStream*iface,LONG*plStart,
373 LONG*plLength,PAVISTREAM*ppResult)
375 IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
376 IAVIEditStreamImpl* pEdit;
377 HRESULT hr;
378 LONG start = 0;
380 TRACE("(%p,%p,%p,%p)\n",iface,plStart,plLength,ppResult);
382 if (ppResult == NULL)
383 return AVIERR_BADPARAM;
384 *ppResult = NULL;
385 if (plStart == NULL || plLength == NULL || *plStart < 0 || *plLength < 0)
386 return AVIERR_BADPARAM;
388 /* check bounds */
389 if (*(LPDWORD)plLength > This->sInfo.dwLength)
390 *(LPDWORD)plLength = This->sInfo.dwLength;
391 if (*(LPDWORD)plStart < This->sInfo.dwStart) {
392 *(LPDWORD)plLength -= This->sInfo.dwStart - *(LPDWORD)plStart;
393 *(LPDWORD)plStart = This->sInfo.dwStart;
394 if (*plLength < 0)
395 return AVIERR_BADPARAM;
397 if (*(LPDWORD)plStart + *(LPDWORD)plLength > This->sInfo.dwStart + This->sInfo.dwLength)
398 *(LPDWORD)plLength = This->sInfo.dwStart + This->sInfo.dwLength -
399 *(LPDWORD)plStart;
401 pEdit = (IAVIEditStreamImpl*)AVIFILE_CreateEditStream(NULL);
402 if (pEdit == NULL)
403 return AVIERR_MEMORY;
405 hr = IAVIEditStream_Paste((PAVIEDITSTREAM)pEdit, &start, plLength, &This->IAVIStream_iface,
406 *plStart, *plStart + *plLength);
407 *plStart = start;
408 if (FAILED(hr))
409 IAVIEditStream_Release((PAVIEDITSTREAM)pEdit);
410 else
411 *ppResult = &This->IAVIStream_iface;
413 return hr;
416 static HRESULT WINAPI IAVIEditStream_fnPaste(IAVIEditStream*iface,LONG*plStart,
417 LONG*plLength,PAVISTREAM pSource,
418 LONG lStart,LONG lLength)
420 IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
421 AVISTREAMINFOW srcInfo;
422 IAVIEditStreamImpl *pEdit = NULL;
423 PAVISTREAM pStream;
424 DWORD startPos, endPos, streamNr, nStreams;
425 ULONG n;
427 TRACE("(%p,%p,%p,%p,%d,%d)\n",iface,plStart,plLength,
428 pSource,lStart,lLength);
430 if (pSource == NULL)
431 return AVIERR_BADHANDLE;
432 if (plStart == NULL || *plStart < 0)
433 return AVIERR_BADPARAM;
434 if (This->sInfo.dwStart + This->sInfo.dwLength < *plStart)
435 return AVIERR_BADPARAM; /* Can't paste with holes */
436 if (FAILED(IAVIStream_Info(pSource, &srcInfo, sizeof(srcInfo))))
437 return AVIERR_ERROR;
438 if (lStart < srcInfo.dwStart || lStart >= srcInfo.dwStart + srcInfo.dwLength)
439 return AVIERR_BADPARAM;
440 if (This->sInfo.fccType == 0) {
441 /* This stream is empty */
442 IAVIStream_Info(pSource, &This->sInfo, sizeof(This->sInfo));
443 This->sInfo.dwStart = *plStart;
444 This->sInfo.dwLength = 0;
446 if (This->sInfo.fccType != srcInfo.fccType)
447 return AVIERR_UNSUPPORTED; /* different stream types */
448 if (lLength == -1) /* Copy the hole stream */
449 lLength = srcInfo.dwLength;
450 if (lStart + lLength > srcInfo.dwStart + srcInfo.dwLength)
451 lLength = srcInfo.dwStart + srcInfo.dwLength - lStart;
452 if (lLength + *plStart >= 0x80000000)
453 return AVIERR_MEMORY;
455 /* streamtype specific tests */
456 if (srcInfo.fccType == streamtypeVIDEO) {
457 LONG size;
459 size = srcInfo.rcFrame.right - srcInfo.rcFrame.left;
460 if (size != This->sInfo.rcFrame.right - This->sInfo.rcFrame.left)
461 return AVIERR_UNSUPPORTED; /* FIXME: Can't GetFrame convert it? */
462 size = srcInfo.rcFrame.bottom - srcInfo.rcFrame.top;
463 if (size != This->sInfo.rcFrame.bottom - This->sInfo.rcFrame.top)
464 return AVIERR_UNSUPPORTED; /* FIXME: Can't GetFrame convert it? */
465 } else if (srcInfo.fccType == streamtypeAUDIO) {
466 if (!AVIFILE_FormatsEqual(&This->IAVIStream_iface, pSource))
467 return AVIERR_UNSUPPORTED;
468 } else {
469 /* FIXME: streamtypeMIDI and streamtypeTEXT */
470 return AVIERR_UNSUPPORTED;
473 /* try to get an IEditStreamInternal interface */
474 if (SUCCEEDED(IAVIStream_QueryInterface(pSource, &IID_IEditStreamInternal, (LPVOID*)&pEdit)))
475 IAVIEditStream_Release(&pEdit->IAVIEditStream_iface); /* pSource holds a reference */
477 /* for video must check for change of format */
478 if (This->sInfo.fccType == streamtypeVIDEO) {
479 if (! This->bDecompress) {
480 /* Need to decompress if any of the following conditions matches:
481 * - pSource is an editable stream which decompresses
482 * - the nearest keyframe of pSource isn't lStart
483 * - the nearest keyframe of this stream isn't *plStart
484 * - the format of pSource doesn't match this one
486 if ((pEdit != NULL && pEdit->bDecompress) ||
487 AVIStreamNearestKeyFrame(pSource, lStart) != lStart ||
488 AVIStreamNearestKeyFrame(&This->IAVIStream_iface, *plStart) != *plStart ||
489 (This->nStreams > 0 && !AVIFILE_FormatsEqual(&This->IAVIStream_iface, pSource))) {
490 /* Use first stream part to get format to convert everything to */
491 AVIFILE_ReadFrame(This, This->pStreams[0].pStream,
492 This->pStreams[0].dwStart);
494 /* Check if we could convert the source streams to the desired format... */
495 if (pEdit != NULL) {
496 if (FAILED(AVIFILE_FindStreamInTable(pEdit, lStart, &pStream,
497 &startPos, &streamNr, TRUE)))
498 return AVIERR_INTERNAL;
499 for (n = lStart; n < lStart + lLength; streamNr++) {
500 if (AVIFILE_ReadFrame(This, pEdit->pStreams[streamNr].pStream, startPos) == NULL)
501 return AVIERR_BADFORMAT;
502 startPos = pEdit->pStreams[streamNr].dwStart;
503 n += pEdit->pStreams[streamNr].dwLength;
505 } else if (AVIFILE_ReadFrame(This, pSource, lStart) == NULL)
506 return AVIERR_BADFORMAT;
508 This->bDecompress = TRUE;
509 This->sInfo.fccHandler = 0;
511 } else if (AVIFILE_ReadFrame(This, pSource, lStart) == NULL)
512 return AVIERR_BADFORMAT; /* Can't convert source to own format */
513 } /* FIXME: something special for the other formats? */
515 /* Make sure we have enough memory for parts */
516 if (pEdit != NULL) {
517 DWORD nLastStream;
519 AVIFILE_FindStreamInTable(pEdit, lStart + lLength, &pStream,
520 &endPos, &nLastStream, TRUE);
521 AVIFILE_FindStreamInTable(pEdit, lStart, &pStream,
522 &startPos, &streamNr, FALSE);
523 if (nLastStream == streamNr)
524 nLastStream++;
526 nStreams = nLastStream - streamNr;
527 } else
528 nStreams = 1;
529 if (This->nStreams + nStreams + 1 > This->nTableSize) {
530 n = This->nStreams + nStreams + 33;
532 This->pStreams = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->pStreams, n * sizeof(EditStreamTable));
533 if (This->pStreams == NULL)
534 return AVIERR_MEMORY;
535 This->nTableSize = n;
538 if (plLength != NULL)
539 *plLength = lLength;
541 /* now do the real work */
542 if (This->sInfo.dwStart + This->sInfo.dwLength > *plStart) {
543 AVIFILE_FindStreamInTable(This, *plStart, &pStream,
544 &startPos, &streamNr, FALSE);
545 if (startPos != This->pStreams[streamNr].dwStart) {
546 /* split stream streamNr at startPos */
547 memmove(This->pStreams + streamNr + nStreams + 1,
548 This->pStreams + streamNr,
549 (This->nStreams + nStreams - streamNr + 1) * sizeof(EditStreamTable));
551 This->pStreams[streamNr + 2].dwLength =
552 EditStreamEnd(This, streamNr + 2) - startPos;
553 This->pStreams[streamNr + 2].dwStart = startPos;
554 This->pStreams[streamNr].dwLength =
555 startPos - This->pStreams[streamNr].dwStart;
556 IAVIStream_AddRef(This->pStreams[streamNr].pStream);
557 streamNr++;
558 } else {
559 /* insert before stream at streamNr */
560 memmove(This->pStreams + streamNr + nStreams, This->pStreams + streamNr,
561 (This->nStreams + nStreams - streamNr) * sizeof(EditStreamTable));
563 } else /* append the streams */
564 streamNr = This->nStreams;
566 if (pEdit != NULL) {
567 /* insert the parts of the editable stream instead of itself */
568 AVIFILE_FindStreamInTable(pEdit, lStart + lLength, &pStream,
569 &endPos, NULL, FALSE);
570 AVIFILE_FindStreamInTable(pEdit, lStart, &pStream, &startPos, &n, FALSE);
572 memcpy(This->pStreams + streamNr, pEdit->pStreams + n,
573 nStreams * sizeof(EditStreamTable));
574 if (This->pStreams[streamNr].dwStart < startPos) {
575 This->pStreams[streamNr].dwLength =
576 EditStreamEnd(This, streamNr) - startPos;
577 This->pStreams[streamNr].dwStart = startPos;
579 if (endPos < EditStreamEnd(This, streamNr + nStreams))
580 This->pStreams[streamNr + nStreams].dwLength =
581 endPos - This->pStreams[streamNr + nStreams].dwStart;
582 } else {
583 /* a simple stream */
584 This->pStreams[streamNr].pStream = pSource;
585 This->pStreams[streamNr].dwStart = lStart;
586 This->pStreams[streamNr].dwLength = lLength;
589 for (n = 0; n < nStreams; n++) {
590 IAVIStream_AddRef(This->pStreams[streamNr + n].pStream);
591 if (0 < streamNr + n &&
592 This->pStreams[streamNr + n - 1].pStream != This->pStreams[streamNr + n].pStream) {
593 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
594 This->sInfo.dwFormatChangeCount++;
597 This->sInfo.dwEditCount++;
598 This->sInfo.dwLength += lLength;
599 This->nStreams += nStreams;
601 return AVIERR_OK;
604 static HRESULT WINAPI IAVIEditStream_fnClone(IAVIEditStream*iface,
605 PAVISTREAM*ppResult)
607 IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
608 IAVIEditStreamImpl* pEdit;
609 DWORD i;
611 TRACE("(%p,%p)\n",iface,ppResult);
613 if (ppResult == NULL)
614 return AVIERR_BADPARAM;
615 *ppResult = NULL;
617 pEdit = (IAVIEditStreamImpl*)AVIFILE_CreateEditStream(NULL);
618 if (pEdit == NULL)
619 return AVIERR_MEMORY;
620 if (This->nStreams > pEdit->nTableSize) {
621 pEdit->pStreams = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pEdit->pStreams,
622 This->nStreams * sizeof(EditStreamTable));
623 if (pEdit->pStreams == NULL)
624 return AVIERR_MEMORY;
625 pEdit->nTableSize = This->nStreams;
627 pEdit->nStreams = This->nStreams;
628 memcpy(pEdit->pStreams, This->pStreams,
629 This->nStreams * sizeof(EditStreamTable));
630 memcpy(&pEdit->sInfo,&This->sInfo,sizeof(This->sInfo));
631 for (i = 0; i < This->nStreams; i++) {
632 if (pEdit->pStreams[i].pStream != NULL)
633 IAVIStream_AddRef(pEdit->pStreams[i].pStream);
636 *ppResult = &This->IAVIStream_iface;
638 return AVIERR_OK;
641 static HRESULT WINAPI IAVIEditStream_fnSetInfo(IAVIEditStream*iface,
642 LPAVISTREAMINFOW asi,LONG size)
644 IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
646 TRACE("(%p,%p,%d)\n",iface,asi,size);
648 /* check parameters */
649 if (size >= 0 && size < sizeof(AVISTREAMINFOW))
650 return AVIERR_BADSIZE;
652 This->sInfo.wLanguage = asi->wLanguage;
653 This->sInfo.wPriority = asi->wPriority;
654 This->sInfo.dwStart = asi->dwStart;
655 This->sInfo.dwRate = asi->dwRate;
656 This->sInfo.dwScale = asi->dwScale;
657 This->sInfo.dwQuality = asi->dwQuality;
658 CopyRect(&This->sInfo.rcFrame, &asi->rcFrame);
659 memcpy(This->sInfo.szName, asi->szName, sizeof(asi->szName));
660 This->sInfo.dwEditCount++;
662 return AVIERR_OK;
665 static const struct IAVIEditStreamVtbl ieditstream = {
666 IAVIEditStream_fnQueryInterface,
667 IAVIEditStream_fnAddRef,
668 IAVIEditStream_fnRelease,
669 IAVIEditStream_fnCut,
670 IAVIEditStream_fnCopy,
671 IAVIEditStream_fnPaste,
672 IAVIEditStream_fnClone,
673 IAVIEditStream_fnSetInfo
676 static HRESULT WINAPI IEditAVIStream_fnQueryInterface(IAVIStream*iface,
677 REFIID refiid,LPVOID*obj)
679 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
680 return IAVIEditStream_QueryInterface(&This->IAVIEditStream_iface,refiid,obj);
683 static ULONG WINAPI IEditAVIStream_fnAddRef(IAVIStream*iface)
685 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
686 return IAVIEditStream_AddRef(&This->IAVIEditStream_iface);
689 static ULONG WINAPI IEditAVIStream_fnRelease(IAVIStream*iface)
691 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
692 return IAVIEditStream_Release(&This->IAVIEditStream_iface);
695 static HRESULT WINAPI IEditAVIStream_fnCreate(IAVIStream*iface,
696 LPARAM lParam1,LPARAM lParam2)
698 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
700 if (lParam2 != 0)
701 return AVIERR_ERROR;
703 if (This->pStreams == NULL) {
704 This->pStreams = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 256 * sizeof(EditStreamTable));
705 if (This->pStreams == NULL)
706 return AVIERR_MEMORY;
707 This->nTableSize = 256;
710 if (lParam1 != 0) {
711 IAVIStream_Info((PAVISTREAM)lParam1, &This->sInfo, sizeof(This->sInfo));
712 IAVIStream_AddRef((PAVISTREAM)lParam1);
713 This->pStreams[0].pStream = (PAVISTREAM)lParam1;
714 This->pStreams[0].dwStart = This->sInfo.dwStart;
715 This->pStreams[0].dwLength = This->sInfo.dwLength;
716 This->nStreams = 1;
718 return AVIERR_OK;
721 static HRESULT WINAPI IEditAVIStream_fnInfo(IAVIStream*iface,
722 AVISTREAMINFOW *psi,LONG size)
724 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
726 TRACE("(%p,%p,%d)\n",iface,psi,size);
728 if (psi == NULL)
729 return AVIERR_BADPARAM;
730 if (size < 0)
731 return AVIERR_BADSIZE;
733 if (This->bDecompress)
734 This->sInfo.fccHandler = 0;
736 memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo)));
738 if ((DWORD)size < sizeof(This->sInfo))
739 return AVIERR_BUFFERTOOSMALL;
740 return AVIERR_OK;
743 static LONG WINAPI IEditAVIStream_fnFindSample(IAVIStream*iface,LONG pos,
744 LONG flags)
746 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
747 PAVISTREAM stream;
748 DWORD streamPos, streamNr;
750 TRACE("(%p,%d,0x%08X)\n",iface,pos,flags);
752 if (flags & FIND_FROM_START)
753 pos = (LONG)This->sInfo.dwStart;
755 /* outside of stream? */
756 if (pos < (LONG)This->sInfo.dwStart ||
757 (LONG)This->sInfo.dwStart + (LONG)This->sInfo.dwLength <= pos)
758 return -1;
760 /* map our position to a stream and position in it */
761 if (AVIFILE_FindStreamInTable(This, pos, &stream, &streamPos,
762 &streamNr, TRUE) != S_OK)
763 return -1; /* doesn't exist */
765 if (This->bDecompress) {
766 /* only one stream -- format changes only at start */
767 if (flags & FIND_FORMAT)
768 return (flags & FIND_NEXT ? -1 : 0);
770 /* FIXME: map positions back to us */
771 return IAVIStream_FindSample(stream, streamPos, flags);
772 } else {
773 /* assume change of format every frame */
774 return pos;
778 static HRESULT WINAPI IEditAVIStream_fnReadFormat(IAVIStream*iface,LONG pos,
779 LPVOID format,LONG*fmtsize)
781 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
782 LPBITMAPINFOHEADER lp;
783 PAVISTREAM stream;
784 DWORD n;
785 HRESULT hr;
787 TRACE("(%p,%d,%p,%p)\n",iface,pos,format,fmtsize);
789 if (fmtsize == NULL || pos < This->sInfo.dwStart ||
790 This->sInfo.dwStart + This->sInfo.dwLength <= pos)
791 return AVIERR_BADPARAM;
793 /* find stream corresponding to position */
794 hr = AVIFILE_FindStreamInTable(This, pos, &stream, &n, NULL, FALSE);
795 if (FAILED(hr))
796 return hr;
798 if (! This->bDecompress)
799 return IAVIStream_ReadFormat(stream, n, format, fmtsize);
801 lp = AVIFILE_ReadFrame(This, stream, n);
802 if (lp == NULL)
803 return AVIERR_ERROR;
804 if (lp->biBitCount <= 8) {
805 n = (lp->biClrUsed > 0 ? lp->biClrUsed : 1 << lp->biBitCount);
806 n *= sizeof(RGBQUAD);
807 } else
808 n = 0;
809 n += lp->biSize;
811 memcpy(format, lp, min((LONG)n, *fmtsize));
812 hr = ((LONG)n > *fmtsize ? AVIERR_BUFFERTOOSMALL : AVIERR_OK);
813 *fmtsize = n;
815 return hr;
818 static HRESULT WINAPI IEditAVIStream_fnSetFormat(IAVIStream*iface,LONG pos,
819 LPVOID format,LONG formatsize)
821 TRACE("(%p,%d,%p,%d)\n",iface,pos,format,formatsize);
823 return AVIERR_UNSUPPORTED;
826 static HRESULT WINAPI IEditAVIStream_fnRead(IAVIStream*iface,LONG start,
827 LONG samples,LPVOID buffer,
828 LONG buffersize,LONG*bytesread,
829 LONG*samplesread)
831 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
832 PAVISTREAM stream;
833 DWORD streamPos, streamNr;
834 LONG readBytes, readSamples, count;
835 HRESULT hr;
837 TRACE("(%p,%d,%d,%p,%d,%p,%p) -- 0x%08X\n",iface,start,samples,
838 buffer,buffersize,bytesread,samplesread,This->sInfo.fccType);
840 /* check parameters */
841 if (bytesread != NULL)
842 *bytesread = 0;
843 if (samplesread != NULL)
844 *samplesread = 0;
845 if (buffersize < 0)
846 return AVIERR_BADSIZE;
847 if ((DWORD)start < This->sInfo.dwStart ||
848 This->sInfo.dwStart + This->sInfo.dwLength < (DWORD)start)
849 return AVIERR_BADPARAM;
851 if (! This->bDecompress) {
852 /* audio like data -- sample-based */
853 do {
854 if (samples == 0)
855 return AVIERR_OK; /* nothing at all or already done */
857 if (FAILED(AVIFILE_FindStreamInTable(This, start, &stream,
858 &streamPos, &streamNr, FALSE)))
859 return AVIERR_ERROR;
861 /* limit to end of the stream */
862 count = samples;
863 if (streamPos + count > EditStreamEnd(This, streamNr))
864 count = EditStreamEnd(This, streamNr) - streamPos;
866 hr = IAVIStream_Read(stream, streamPos, count, buffer, buffersize,
867 &readBytes, &readSamples);
868 if (FAILED(hr))
869 return hr;
870 if (readBytes == 0 && readSamples == 0 && count != 0)
871 return AVIERR_FILEREAD; /* for bad stream implementations */
873 if (samplesread != NULL)
874 *samplesread += readSamples;
875 if (bytesread != NULL)
876 *bytesread += readBytes;
877 if (buffer != NULL) {
878 buffer = ((LPBYTE)buffer)+readBytes;
879 buffersize -= readBytes;
881 start += count;
882 samples -= count;
883 } while (This->sInfo.dwStart + This->sInfo.dwLength > start);
884 } else {
885 /* video like data -- frame-based */
886 LPBITMAPINFOHEADER lp;
888 if (samples == 0)
889 return AVIERR_OK;
891 if (FAILED(AVIFILE_FindStreamInTable(This, start, &stream,
892 &streamPos, &streamNr, FALSE)))
893 return AVIERR_ERROR;
895 lp = AVIFILE_ReadFrame(This, stream, streamPos);
896 if (lp == NULL)
897 return AVIERR_ERROR;
899 if (buffer != NULL) {
900 /* need size of format to skip */
901 if (lp->biBitCount <= 8) {
902 count = lp->biClrUsed > 0 ? lp->biClrUsed : 1 << lp->biBitCount;
903 count *= sizeof(RGBQUAD);
904 } else
905 count = 0;
906 count += lp->biSize;
908 if (buffersize < lp->biSizeImage)
909 return AVIERR_BUFFERTOOSMALL;
910 memcpy(buffer, (LPBYTE)lp + count, lp->biSizeImage);
913 if (bytesread != NULL)
914 *bytesread = lp->biSizeImage;
915 if (samplesread != NULL)
916 *samplesread = 1;
919 return AVIERR_OK;
922 static HRESULT WINAPI IEditAVIStream_fnWrite(IAVIStream*iface,LONG start,
923 LONG samples,LPVOID buffer,
924 LONG buffersize,DWORD flags,
925 LONG*sampwritten,LONG*byteswritten)
927 TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n",iface,start,samples,buffer,
928 buffersize,flags,sampwritten,byteswritten);
930 /* be sure return parameters have correct values */
931 if (sampwritten != NULL)
932 *sampwritten = 0;
933 if (byteswritten != NULL)
934 *byteswritten = 0;
936 return AVIERR_UNSUPPORTED;
939 static HRESULT WINAPI IEditAVIStream_fnDelete(IAVIStream*iface,LONG start,
940 LONG samples)
942 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
944 TRACE("(%p,%d,%d)\n",iface,start,samples);
946 return IAVIEditStream_Cut(&This->IAVIEditStream_iface,&start,&samples,NULL);
949 static HRESULT WINAPI IEditAVIStream_fnReadData(IAVIStream*iface,DWORD fcc,
950 LPVOID lp,LONG *lpread)
952 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
953 DWORD n;
955 TRACE("(%p,0x%08X,%p,%p)\n",iface,fcc,lp,lpread);
957 /* check parameters */
958 if (lp == NULL || lpread == NULL)
959 return AVIERR_BADPARAM;
961 /* simply ask every stream and return the first block found */
962 for (n = 0; n < This->nStreams; n++) {
963 HRESULT hr = IAVIStream_ReadData(This->pStreams[n].pStream,fcc,lp,lpread);
965 if (SUCCEEDED(hr))
966 return hr;
969 *lpread = 0;
970 return AVIERR_NODATA;
973 static HRESULT WINAPI IEditAVIStream_fnWriteData(IAVIStream*iface,DWORD fcc,
974 LPVOID lp,LONG size)
976 TRACE("(%p,0x%08X,%p,%d)\n",iface,fcc,lp,size);
978 return AVIERR_UNSUPPORTED;
981 static HRESULT WINAPI IEditAVIStream_fnSetInfo(IAVIStream*iface,
982 AVISTREAMINFOW*info,LONG len)
984 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
986 TRACE("(%p,%p,%d)\n",iface,info,len);
988 return IAVIEditStream_SetInfo(&This->IAVIEditStream_iface,info,len);
991 static const struct IAVIStreamVtbl ieditstast = {
992 IEditAVIStream_fnQueryInterface,
993 IEditAVIStream_fnAddRef,
994 IEditAVIStream_fnRelease,
995 IEditAVIStream_fnCreate,
996 IEditAVIStream_fnInfo,
997 IEditAVIStream_fnFindSample,
998 IEditAVIStream_fnReadFormat,
999 IEditAVIStream_fnSetFormat,
1000 IEditAVIStream_fnRead,
1001 IEditAVIStream_fnWrite,
1002 IEditAVIStream_fnDelete,
1003 IEditAVIStream_fnReadData,
1004 IEditAVIStream_fnWriteData,
1005 IEditAVIStream_fnSetInfo
1008 PAVIEDITSTREAM AVIFILE_CreateEditStream(PAVISTREAM pstream)
1010 IAVIEditStreamImpl *pedit = NULL;
1012 pedit = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIEditStreamImpl));
1013 if (pedit == NULL)
1014 return NULL;
1016 pedit->IAVIEditStream_iface.lpVtbl = &ieditstream;
1017 pedit->IAVIStream_iface.lpVtbl = &ieditstast;
1018 pedit->ref = 1;
1020 IAVIStream_Create(&pedit->IAVIStream_iface, (LPARAM)pstream, 0);
1022 return (PAVIEDITSTREAM)pedit;