wineandroid: Update the contents when we receive a new surface from Java.
[wine.git] / dlls / avifil32 / editstream.c
blob2e0fa2fd60f7c558c555d0c734965c91c7c3ef39
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 IAVIEditStreamImpl *AVIFILE_CreateEditStream(IAVIStream *stream);
75 static inline IAVIEditStreamImpl *impl_from_IAVIEditStream(IAVIEditStream *iface)
77 return CONTAINING_RECORD(iface, IAVIEditStreamImpl, IAVIEditStream_iface);
80 static inline IAVIEditStreamImpl *impl_from_IAVIStream(IAVIStream *iface)
82 return CONTAINING_RECORD(iface, IAVIEditStreamImpl, IAVIStream_iface);
85 /***********************************************************************/
87 static HRESULT AVIFILE_FindStreamInTable(IAVIEditStreamImpl* const This,
88 DWORD pos,PAVISTREAM *ppStream,
89 DWORD* streamPos,
90 DWORD* streamNr,BOOL bFindSample)
92 DWORD n;
94 TRACE("(%p,%u,%p,%p,%p,%d)\n",This,pos,ppStream,streamPos,
95 streamNr,bFindSample);
97 if (pos < This->sInfo.dwStart)
98 return AVIERR_BADPARAM;
100 pos -= This->sInfo.dwStart;
101 for (n = 0; n < This->nStreams; n++) {
102 if (pos < This->pStreams[n].dwLength) {
103 *ppStream = This->pStreams[n].pStream;
104 *streamPos = This->pStreams[n].dwStart + pos;
105 if (streamNr != NULL)
106 *streamNr = n;
108 return AVIERR_OK;
110 pos -= This->pStreams[n].dwLength;
112 if (pos == 0 && bFindSample) {
113 *ppStream = This->pStreams[--n].pStream;
114 *streamPos = EditStreamEnd(This, n);
115 if (streamNr != NULL)
116 *streamNr = n;
118 TRACE(" -- pos=0 && b=1 -> (%p,%u,%u)\n",*ppStream, *streamPos, n);
119 return AVIERR_OK;
120 } else {
121 *ppStream = NULL;
122 *streamPos = 0;
123 if (streamNr != NULL)
124 *streamNr = 0;
126 TRACE(" -> ERROR (NULL,0,0)\n");
127 return AVIERR_BADPARAM;
131 static LPVOID AVIFILE_ReadFrame(IAVIEditStreamImpl* const This,
132 PAVISTREAM pstream, LONG pos)
134 PGETFRAME pg;
136 TRACE("(%p,%p,%d)\n",This,pstream,pos);
138 if (pstream == NULL)
139 return NULL;
141 /* if stream changes make sure that only palette changes */
142 if (This->pCurStream != pstream) {
143 pg = AVIStreamGetFrameOpen(pstream, NULL);
144 if (pg == NULL)
145 return NULL;
146 if (This->pg != NULL) {
147 if (IGetFrame_SetFormat(pg, This->lpFrame, NULL, 0, 0, -1, -1) != S_OK) {
148 AVIStreamGetFrameClose(pg);
149 ERR(": IGetFrame_SetFormat failed\n");
150 return NULL;
152 AVIStreamGetFrameClose(This->pg);
154 This->pg = pg;
155 This->pCurStream = pstream;
158 /* now get the decompressed frame */
159 This->lpFrame = AVIStreamGetFrame(This->pg, pos);
160 if (This->lpFrame != NULL)
161 This->sInfo.dwSuggestedBufferSize = This->lpFrame->biSizeImage;
163 return This->lpFrame;
166 static HRESULT AVIFILE_RemoveStream(IAVIEditStreamImpl* const This, DWORD nr)
168 assert(This != NULL);
169 assert(nr < This->nStreams);
171 /* remove part nr */
172 IAVIStream_Release(This->pStreams[nr].pStream);
173 This->nStreams--;
174 if (nr < This->nStreams)
175 memmove(&This->pStreams[nr], &This->pStreams[nr + 1],
176 (This->nStreams - nr) * sizeof(This->pStreams[0]));
177 This->pStreams[This->nStreams].pStream = NULL;
178 This->pStreams[This->nStreams].dwStart = 0;
179 This->pStreams[This->nStreams].dwLength = 0;
181 /* try to merge the part before the deleted one and the one after it */
182 if (0 < nr && 0 < This->nStreams &&
183 This->pStreams[nr - 1].pStream == This->pStreams[nr].pStream) {
184 if (EditStreamEnd(This, nr - 1) == This->pStreams[nr].dwStart) {
185 This->pStreams[nr - 1].dwLength += This->pStreams[nr].dwLength;
186 return AVIFILE_RemoveStream(This, nr);
190 return AVIERR_OK;
193 static BOOL AVIFILE_FormatsEqual(PAVISTREAM avi1, PAVISTREAM avi2)
195 LPVOID fmt1 = NULL, fmt2 = NULL;
196 LONG size1, size2, start1, start2;
197 BOOL status = FALSE;
199 assert(avi1 != NULL && avi2 != NULL);
201 /* get stream starts and check format sizes */
202 start1 = AVIStreamStart(avi1);
203 start2 = AVIStreamStart(avi2);
204 if (FAILED(AVIStreamFormatSize(avi1, start1, &size1)))
205 return FALSE;
206 if (FAILED(AVIStreamFormatSize(avi2, start2, &size2)))
207 return FALSE;
208 if (size1 != size2)
209 return FALSE;
211 /* sizes match, now get formats and compare them */
212 fmt1 = HeapAlloc(GetProcessHeap(), 0, size1);
213 if (fmt1 == NULL)
214 return FALSE;
215 if (SUCCEEDED(AVIStreamReadFormat(avi1, start1, fmt1, &size1))) {
216 fmt2 = HeapAlloc(GetProcessHeap(), 0, size1);
217 if (fmt2 != NULL) {
218 if (SUCCEEDED(AVIStreamReadFormat(avi2, start2, fmt2, &size1)))
219 status = (memcmp(fmt1, fmt2, size1) == 0);
223 HeapFree(GetProcessHeap(), 0, fmt2);
224 HeapFree(GetProcessHeap(), 0, fmt1);
226 return status;
229 /***********************************************************************/
231 static HRESULT WINAPI IAVIEditStream_fnQueryInterface(IAVIEditStream*iface,REFIID refiid,LPVOID *obj)
233 IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
235 TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj);
237 if (IsEqualGUID(&IID_IUnknown, refiid) ||
238 IsEqualGUID(&IID_IAVIEditStream, refiid) ||
239 IsEqualGUID(&IID_IEditStreamInternal, refiid)) {
240 *obj = iface;
241 IAVIEditStream_AddRef(iface);
243 return S_OK;
244 } else if (IsEqualGUID(&IID_IAVIStream, refiid)) {
245 *obj = &This->IAVIStream_iface;
246 IAVIEditStream_AddRef(iface);
248 return S_OK;
251 return E_NOINTERFACE;
254 static ULONG WINAPI IAVIEditStream_fnAddRef(IAVIEditStream*iface)
256 IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
257 ULONG ref = InterlockedIncrement(&This->ref);
259 TRACE("(%p) -> %d\n", iface, ref);
261 return ref;
264 static ULONG WINAPI IAVIEditStream_fnRelease(IAVIEditStream*iface)
266 IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
267 DWORD i;
268 ULONG ref = InterlockedDecrement(&This->ref);
270 TRACE("(%p) -> %d\n", iface, ref);
272 if (!ref) {
273 /* release memory */
274 if (This->pg != NULL)
275 AVIStreamGetFrameClose(This->pg);
276 if (This->pStreams != NULL) {
277 for (i = 0; i < This->nStreams; i++) {
278 if (This->pStreams[i].pStream != NULL)
279 IAVIStream_Release(This->pStreams[i].pStream);
281 HeapFree(GetProcessHeap(), 0, This->pStreams);
284 HeapFree(GetProcessHeap(), 0, This);
285 return 0;
287 return ref;
290 static HRESULT WINAPI IAVIEditStream_fnCut(IAVIEditStream*iface,LONG*plStart,
291 LONG*plLength,PAVISTREAM*ppResult)
293 IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
294 PAVISTREAM stream;
295 DWORD start, len, streamPos, streamNr;
296 HRESULT hr;
298 TRACE("(%p,%p,%p,%p)\n",iface,plStart,plLength,ppResult);
300 if (ppResult != NULL)
301 *ppResult = NULL;
302 if (plStart == NULL || plLength == NULL || *plStart < 0)
303 return AVIERR_BADPARAM;
305 /* if asked for cut part copy it before deleting */
306 if (ppResult != NULL) {
307 hr = IAVIEditStream_Copy(iface, plStart, plLength, ppResult);
308 if (FAILED(hr))
309 return hr;
312 start = *plStart;
313 len = *plLength;
315 /* now delete the requested part */
316 while (len > 0) {
317 hr = AVIFILE_FindStreamInTable(This, start, &stream,
318 &streamPos, &streamNr, FALSE);
319 if (FAILED(hr))
320 return hr;
321 if (This->pStreams[streamNr].dwStart == streamPos) {
322 /* deleting from start of part */
323 if (len < This->pStreams[streamNr].dwLength) {
324 start += len;
325 This->pStreams[streamNr].dwStart += len;
326 This->pStreams[streamNr].dwLength -= len;
327 This->sInfo.dwLength -= len;
328 len = 0;
330 /* we must return decompressed data now */
331 This->bDecompress = TRUE;
332 } else {
333 /* deleting hole part */
334 len -= This->pStreams[streamNr].dwLength;
335 AVIFILE_RemoveStream(This,streamNr);
337 } else if (EditStreamEnd(This, streamNr) <= streamPos + len) {
338 /* deleting at end of a part */
339 DWORD count = EditStreamEnd(This, streamNr) - streamPos;
340 This->sInfo.dwLength -= count;
341 len -= count;
342 This->pStreams[streamNr].dwLength =
343 streamPos - This->pStreams[streamNr].dwStart;
344 } else {
345 /* splitting */
346 if (This->nStreams + 1 >= This->nTableSize) {
347 This->pStreams = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->pStreams,
348 (This->nTableSize + 32) * sizeof(EditStreamTable));
349 if (This->pStreams == NULL)
350 return AVIERR_MEMORY;
351 This->nTableSize += 32;
353 memmove(This->pStreams + streamNr + 1, This->pStreams + streamNr,
354 (This->nStreams - streamNr) * sizeof(EditStreamTable));
355 This->nStreams++;
357 IAVIStream_AddRef(This->pStreams[streamNr + 1].pStream);
358 This->pStreams[streamNr + 1].dwStart = streamPos + len;
359 This->pStreams[streamNr + 1].dwLength =
360 EditStreamEnd(This, streamNr) - This->pStreams[streamNr + 1].dwStart;
362 This->pStreams[streamNr].dwLength =
363 streamPos - This->pStreams[streamNr].dwStart;
364 This->sInfo.dwLength -= len;
365 len = 0;
369 This->sInfo.dwEditCount++;
371 return AVIERR_OK;
374 static HRESULT WINAPI IAVIEditStream_fnCopy(IAVIEditStream*iface,LONG*plStart,
375 LONG*plLength,PAVISTREAM*ppResult)
377 IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
378 IAVIEditStreamImpl* pEdit;
379 HRESULT hr;
380 LONG start = 0;
382 TRACE("(%p,%p,%p,%p)\n",iface,plStart,plLength,ppResult);
384 if (ppResult == NULL)
385 return AVIERR_BADPARAM;
386 *ppResult = NULL;
387 if (plStart == NULL || plLength == NULL || *plStart < 0 || *plLength < 0)
388 return AVIERR_BADPARAM;
390 /* check bounds */
391 if (*(LPDWORD)plLength > This->sInfo.dwLength)
392 *(LPDWORD)plLength = This->sInfo.dwLength;
393 if (*(LPDWORD)plStart < This->sInfo.dwStart) {
394 *(LPDWORD)plLength -= This->sInfo.dwStart - *(LPDWORD)plStart;
395 *(LPDWORD)plStart = This->sInfo.dwStart;
396 if (*plLength < 0)
397 return AVIERR_BADPARAM;
399 if (*(LPDWORD)plStart + *(LPDWORD)plLength > This->sInfo.dwStart + This->sInfo.dwLength)
400 *(LPDWORD)plLength = This->sInfo.dwStart + This->sInfo.dwLength -
401 *(LPDWORD)plStart;
403 pEdit = AVIFILE_CreateEditStream(NULL);
404 if (pEdit == NULL)
405 return AVIERR_MEMORY;
407 hr = IAVIEditStream_Paste(&pEdit->IAVIEditStream_iface, &start, plLength, &This->IAVIStream_iface,
408 *plStart, *plStart + *plLength);
409 *plStart = start;
410 if (FAILED(hr))
411 IAVIEditStream_Release(&pEdit->IAVIEditStream_iface);
412 else
413 *ppResult = &This->IAVIStream_iface;
415 return hr;
418 static HRESULT WINAPI IAVIEditStream_fnPaste(IAVIEditStream*iface,LONG*plStart,
419 LONG*plLength,PAVISTREAM pSource,
420 LONG lStart,LONG lLength)
422 IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
423 AVISTREAMINFOW srcInfo;
424 IAVIEditStreamImpl *pEdit = NULL;
425 PAVISTREAM pStream;
426 DWORD startPos, endPos, streamNr, nStreams;
427 ULONG n;
429 TRACE("(%p,%p,%p,%p,%d,%d)\n",iface,plStart,plLength,
430 pSource,lStart,lLength);
432 if (pSource == NULL)
433 return AVIERR_BADHANDLE;
434 if (plStart == NULL || *plStart < 0)
435 return AVIERR_BADPARAM;
436 if (This->sInfo.dwStart + This->sInfo.dwLength < *plStart)
437 return AVIERR_BADPARAM; /* Can't paste with holes */
438 if (FAILED(IAVIStream_Info(pSource, &srcInfo, sizeof(srcInfo))))
439 return AVIERR_ERROR;
440 if (lStart < srcInfo.dwStart || lStart >= srcInfo.dwStart + srcInfo.dwLength)
441 return AVIERR_BADPARAM;
442 if (This->sInfo.fccType == 0) {
443 /* This stream is empty */
444 IAVIStream_Info(pSource, &This->sInfo, sizeof(This->sInfo));
445 This->sInfo.dwStart = *plStart;
446 This->sInfo.dwLength = 0;
448 if (This->sInfo.fccType != srcInfo.fccType)
449 return AVIERR_UNSUPPORTED; /* different stream types */
450 if (lLength == -1) /* Copy the hole stream */
451 lLength = srcInfo.dwLength;
452 if (lStart + lLength > srcInfo.dwStart + srcInfo.dwLength)
453 lLength = srcInfo.dwStart + srcInfo.dwLength - lStart;
454 if (lLength + *plStart >= 0x80000000)
455 return AVIERR_MEMORY;
457 /* streamtype specific tests */
458 if (srcInfo.fccType == streamtypeVIDEO) {
459 LONG size;
461 size = srcInfo.rcFrame.right - srcInfo.rcFrame.left;
462 if (size != This->sInfo.rcFrame.right - This->sInfo.rcFrame.left)
463 return AVIERR_UNSUPPORTED; /* FIXME: Can't GetFrame convert it? */
464 size = srcInfo.rcFrame.bottom - srcInfo.rcFrame.top;
465 if (size != This->sInfo.rcFrame.bottom - This->sInfo.rcFrame.top)
466 return AVIERR_UNSUPPORTED; /* FIXME: Can't GetFrame convert it? */
467 } else if (srcInfo.fccType == streamtypeAUDIO) {
468 if (!AVIFILE_FormatsEqual(&This->IAVIStream_iface, pSource))
469 return AVIERR_UNSUPPORTED;
470 } else {
471 /* FIXME: streamtypeMIDI and streamtypeTEXT */
472 return AVIERR_UNSUPPORTED;
475 /* try to get an IEditStreamInternal interface */
476 if (SUCCEEDED(IAVIStream_QueryInterface(pSource, &IID_IEditStreamInternal, (LPVOID*)&pEdit)))
477 IAVIEditStream_Release(&pEdit->IAVIEditStream_iface); /* pSource holds a reference */
479 /* for video must check for change of format */
480 if (This->sInfo.fccType == streamtypeVIDEO) {
481 if (! This->bDecompress) {
482 /* Need to decompress if any of the following conditions matches:
483 * - pSource is an editable stream which decompresses
484 * - the nearest keyframe of pSource isn't lStart
485 * - the nearest keyframe of this stream isn't *plStart
486 * - the format of pSource doesn't match this one
488 if ((pEdit != NULL && pEdit->bDecompress) ||
489 AVIStreamNearestKeyFrame(pSource, lStart) != lStart ||
490 AVIStreamNearestKeyFrame(&This->IAVIStream_iface, *plStart) != *plStart ||
491 (This->nStreams > 0 && !AVIFILE_FormatsEqual(&This->IAVIStream_iface, pSource))) {
492 /* Use first stream part to get format to convert everything to */
493 AVIFILE_ReadFrame(This, This->pStreams[0].pStream,
494 This->pStreams[0].dwStart);
496 /* Check if we could convert the source streams to the desired format... */
497 if (pEdit != NULL) {
498 if (FAILED(AVIFILE_FindStreamInTable(pEdit, lStart, &pStream,
499 &startPos, &streamNr, TRUE)))
500 return AVIERR_INTERNAL;
501 for (n = lStart; n < lStart + lLength; streamNr++) {
502 if (AVIFILE_ReadFrame(This, pEdit->pStreams[streamNr].pStream, startPos) == NULL)
503 return AVIERR_BADFORMAT;
504 startPos = pEdit->pStreams[streamNr].dwStart;
505 n += pEdit->pStreams[streamNr].dwLength;
507 } else if (AVIFILE_ReadFrame(This, pSource, lStart) == NULL)
508 return AVIERR_BADFORMAT;
510 This->bDecompress = TRUE;
511 This->sInfo.fccHandler = 0;
513 } else if (AVIFILE_ReadFrame(This, pSource, lStart) == NULL)
514 return AVIERR_BADFORMAT; /* Can't convert source to own format */
515 } /* FIXME: something special for the other formats? */
517 /* Make sure we have enough memory for parts */
518 if (pEdit != NULL) {
519 DWORD nLastStream;
521 AVIFILE_FindStreamInTable(pEdit, lStart + lLength, &pStream,
522 &endPos, &nLastStream, TRUE);
523 AVIFILE_FindStreamInTable(pEdit, lStart, &pStream,
524 &startPos, &streamNr, FALSE);
525 if (nLastStream == streamNr)
526 nLastStream++;
528 nStreams = nLastStream - streamNr;
529 } else
530 nStreams = 1;
531 if (This->nStreams + nStreams + 1 > This->nTableSize) {
532 n = This->nStreams + nStreams + 33;
534 This->pStreams = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->pStreams, n * sizeof(EditStreamTable));
535 if (This->pStreams == NULL)
536 return AVIERR_MEMORY;
537 This->nTableSize = n;
540 if (plLength != NULL)
541 *plLength = lLength;
543 /* now do the real work */
544 if (This->sInfo.dwStart + This->sInfo.dwLength > *plStart) {
545 AVIFILE_FindStreamInTable(This, *plStart, &pStream,
546 &startPos, &streamNr, FALSE);
547 if (startPos != This->pStreams[streamNr].dwStart) {
548 /* split stream streamNr at startPos */
549 memmove(This->pStreams + streamNr + nStreams + 1,
550 This->pStreams + streamNr,
551 (This->nStreams + nStreams - streamNr + 1) * sizeof(EditStreamTable));
553 This->pStreams[streamNr + 2].dwLength =
554 EditStreamEnd(This, streamNr + 2) - startPos;
555 This->pStreams[streamNr + 2].dwStart = startPos;
556 This->pStreams[streamNr].dwLength =
557 startPos - This->pStreams[streamNr].dwStart;
558 IAVIStream_AddRef(This->pStreams[streamNr].pStream);
559 streamNr++;
560 } else {
561 /* insert before stream at streamNr */
562 memmove(This->pStreams + streamNr + nStreams, This->pStreams + streamNr,
563 (This->nStreams + nStreams - streamNr) * sizeof(EditStreamTable));
565 } else /* append the streams */
566 streamNr = This->nStreams;
568 if (pEdit != NULL) {
569 /* insert the parts of the editable stream instead of itself */
570 AVIFILE_FindStreamInTable(pEdit, lStart + lLength, &pStream,
571 &endPos, NULL, FALSE);
572 AVIFILE_FindStreamInTable(pEdit, lStart, &pStream, &startPos, &n, FALSE);
574 memcpy(This->pStreams + streamNr, pEdit->pStreams + n,
575 nStreams * sizeof(EditStreamTable));
576 if (This->pStreams[streamNr].dwStart < startPos) {
577 This->pStreams[streamNr].dwLength =
578 EditStreamEnd(This, streamNr) - startPos;
579 This->pStreams[streamNr].dwStart = startPos;
581 if (endPos < EditStreamEnd(This, streamNr + nStreams))
582 This->pStreams[streamNr + nStreams].dwLength =
583 endPos - This->pStreams[streamNr + nStreams].dwStart;
584 } else {
585 /* a simple stream */
586 This->pStreams[streamNr].pStream = pSource;
587 This->pStreams[streamNr].dwStart = lStart;
588 This->pStreams[streamNr].dwLength = lLength;
591 for (n = 0; n < nStreams; n++) {
592 IAVIStream_AddRef(This->pStreams[streamNr + n].pStream);
593 if (0 < streamNr + n &&
594 This->pStreams[streamNr + n - 1].pStream != This->pStreams[streamNr + n].pStream) {
595 This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
596 This->sInfo.dwFormatChangeCount++;
599 This->sInfo.dwEditCount++;
600 This->sInfo.dwLength += lLength;
601 This->nStreams += nStreams;
603 return AVIERR_OK;
606 static HRESULT WINAPI IAVIEditStream_fnClone(IAVIEditStream*iface,
607 PAVISTREAM*ppResult)
609 IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
610 IAVIEditStreamImpl* pEdit;
611 DWORD i;
613 TRACE("(%p,%p)\n",iface,ppResult);
615 if (ppResult == NULL)
616 return AVIERR_BADPARAM;
617 *ppResult = NULL;
619 pEdit = AVIFILE_CreateEditStream(NULL);
620 if (pEdit == NULL)
621 return AVIERR_MEMORY;
622 if (This->nStreams > pEdit->nTableSize) {
623 pEdit->pStreams = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pEdit->pStreams,
624 This->nStreams * sizeof(EditStreamTable));
625 if (pEdit->pStreams == NULL)
626 return AVIERR_MEMORY;
627 pEdit->nTableSize = This->nStreams;
629 pEdit->nStreams = This->nStreams;
630 memcpy(pEdit->pStreams, This->pStreams,
631 This->nStreams * sizeof(EditStreamTable));
632 memcpy(&pEdit->sInfo,&This->sInfo,sizeof(This->sInfo));
633 for (i = 0; i < This->nStreams; i++) {
634 if (pEdit->pStreams[i].pStream != NULL)
635 IAVIStream_AddRef(pEdit->pStreams[i].pStream);
638 *ppResult = &This->IAVIStream_iface;
640 return AVIERR_OK;
643 static HRESULT WINAPI IAVIEditStream_fnSetInfo(IAVIEditStream*iface,
644 LPAVISTREAMINFOW asi,LONG size)
646 IAVIEditStreamImpl *This = impl_from_IAVIEditStream(iface);
648 TRACE("(%p,%p,%d)\n",iface,asi,size);
650 /* check parameters */
651 if (size >= 0 && size < sizeof(AVISTREAMINFOW))
652 return AVIERR_BADSIZE;
654 This->sInfo.wLanguage = asi->wLanguage;
655 This->sInfo.wPriority = asi->wPriority;
656 This->sInfo.dwStart = asi->dwStart;
657 This->sInfo.dwRate = asi->dwRate;
658 This->sInfo.dwScale = asi->dwScale;
659 This->sInfo.dwQuality = asi->dwQuality;
660 This->sInfo.rcFrame = asi->rcFrame;
661 memcpy(This->sInfo.szName, asi->szName, sizeof(asi->szName));
662 This->sInfo.dwEditCount++;
664 return AVIERR_OK;
667 static const struct IAVIEditStreamVtbl ieditstream = {
668 IAVIEditStream_fnQueryInterface,
669 IAVIEditStream_fnAddRef,
670 IAVIEditStream_fnRelease,
671 IAVIEditStream_fnCut,
672 IAVIEditStream_fnCopy,
673 IAVIEditStream_fnPaste,
674 IAVIEditStream_fnClone,
675 IAVIEditStream_fnSetInfo
678 static HRESULT WINAPI IEditAVIStream_fnQueryInterface(IAVIStream*iface,
679 REFIID refiid,LPVOID*obj)
681 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
682 return IAVIEditStream_QueryInterface(&This->IAVIEditStream_iface,refiid,obj);
685 static ULONG WINAPI IEditAVIStream_fnAddRef(IAVIStream*iface)
687 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
688 return IAVIEditStream_AddRef(&This->IAVIEditStream_iface);
691 static ULONG WINAPI IEditAVIStream_fnRelease(IAVIStream*iface)
693 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
694 return IAVIEditStream_Release(&This->IAVIEditStream_iface);
697 static HRESULT WINAPI IEditAVIStream_fnCreate(IAVIStream*iface,
698 LPARAM lParam1,LPARAM lParam2)
700 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
702 if (lParam2 != 0)
703 return AVIERR_ERROR;
705 if (This->pStreams == NULL) {
706 This->pStreams = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 256 * sizeof(EditStreamTable));
707 if (This->pStreams == NULL)
708 return AVIERR_MEMORY;
709 This->nTableSize = 256;
712 if (lParam1 != 0) {
713 IAVIStream_Info((PAVISTREAM)lParam1, &This->sInfo, sizeof(This->sInfo));
714 IAVIStream_AddRef((PAVISTREAM)lParam1);
715 This->pStreams[0].pStream = (PAVISTREAM)lParam1;
716 This->pStreams[0].dwStart = This->sInfo.dwStart;
717 This->pStreams[0].dwLength = This->sInfo.dwLength;
718 This->nStreams = 1;
720 return AVIERR_OK;
723 static HRESULT WINAPI IEditAVIStream_fnInfo(IAVIStream*iface,
724 AVISTREAMINFOW *psi,LONG size)
726 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
728 TRACE("(%p,%p,%d)\n",iface,psi,size);
730 if (psi == NULL)
731 return AVIERR_BADPARAM;
732 if (size < 0)
733 return AVIERR_BADSIZE;
735 if (This->bDecompress)
736 This->sInfo.fccHandler = 0;
738 memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo)));
740 if ((DWORD)size < sizeof(This->sInfo))
741 return AVIERR_BUFFERTOOSMALL;
742 return AVIERR_OK;
745 static LONG WINAPI IEditAVIStream_fnFindSample(IAVIStream*iface,LONG pos,
746 LONG flags)
748 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
749 PAVISTREAM stream;
750 DWORD streamPos, streamNr;
752 TRACE("(%p,%d,0x%08X)\n",iface,pos,flags);
754 if (flags & FIND_FROM_START)
755 pos = (LONG)This->sInfo.dwStart;
757 /* outside of stream? */
758 if (pos < (LONG)This->sInfo.dwStart ||
759 (LONG)This->sInfo.dwStart + (LONG)This->sInfo.dwLength <= pos)
760 return -1;
762 /* map our position to a stream and position in it */
763 if (AVIFILE_FindStreamInTable(This, pos, &stream, &streamPos,
764 &streamNr, TRUE) != S_OK)
765 return -1; /* doesn't exist */
767 if (This->bDecompress) {
768 /* only one stream -- format changes only at start */
769 if (flags & FIND_FORMAT)
770 return (flags & FIND_NEXT ? -1 : 0);
772 /* FIXME: map positions back to us */
773 return IAVIStream_FindSample(stream, streamPos, flags);
774 } else {
775 /* assume change of format every frame */
776 return pos;
780 static HRESULT WINAPI IEditAVIStream_fnReadFormat(IAVIStream*iface,LONG pos,
781 LPVOID format,LONG*fmtsize)
783 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
784 LPBITMAPINFOHEADER lp;
785 PAVISTREAM stream;
786 DWORD n;
787 HRESULT hr;
789 TRACE("(%p,%d,%p,%p)\n",iface,pos,format,fmtsize);
791 if (fmtsize == NULL || pos < This->sInfo.dwStart ||
792 This->sInfo.dwStart + This->sInfo.dwLength <= pos)
793 return AVIERR_BADPARAM;
795 /* find stream corresponding to position */
796 hr = AVIFILE_FindStreamInTable(This, pos, &stream, &n, NULL, FALSE);
797 if (FAILED(hr))
798 return hr;
800 if (! This->bDecompress)
801 return IAVIStream_ReadFormat(stream, n, format, fmtsize);
803 lp = AVIFILE_ReadFrame(This, stream, n);
804 if (lp == NULL)
805 return AVIERR_ERROR;
806 if (lp->biBitCount <= 8) {
807 n = (lp->biClrUsed > 0 ? lp->biClrUsed : 1 << lp->biBitCount);
808 n *= sizeof(RGBQUAD);
809 } else
810 n = 0;
811 n += lp->biSize;
813 memcpy(format, lp, min((LONG)n, *fmtsize));
814 hr = ((LONG)n > *fmtsize ? AVIERR_BUFFERTOOSMALL : AVIERR_OK);
815 *fmtsize = n;
817 return hr;
820 static HRESULT WINAPI IEditAVIStream_fnSetFormat(IAVIStream*iface,LONG pos,
821 LPVOID format,LONG formatsize)
823 TRACE("(%p,%d,%p,%d)\n",iface,pos,format,formatsize);
825 return AVIERR_UNSUPPORTED;
828 static HRESULT WINAPI IEditAVIStream_fnRead(IAVIStream*iface,LONG start,
829 LONG samples,LPVOID buffer,
830 LONG buffersize,LONG*bytesread,
831 LONG*samplesread)
833 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
834 PAVISTREAM stream;
835 DWORD streamPos, streamNr;
836 LONG readBytes, readSamples, count;
837 HRESULT hr;
839 TRACE("(%p,%d,%d,%p,%d,%p,%p) -- 0x%08X\n",iface,start,samples,
840 buffer,buffersize,bytesread,samplesread,This->sInfo.fccType);
842 /* check parameters */
843 if (bytesread != NULL)
844 *bytesread = 0;
845 if (samplesread != NULL)
846 *samplesread = 0;
847 if (buffersize < 0)
848 return AVIERR_BADSIZE;
849 if ((DWORD)start < This->sInfo.dwStart ||
850 This->sInfo.dwStart + This->sInfo.dwLength < (DWORD)start)
851 return AVIERR_BADPARAM;
853 if (! This->bDecompress) {
854 /* audio like data -- sample-based */
855 do {
856 if (samples == 0)
857 return AVIERR_OK; /* nothing at all or already done */
859 if (FAILED(AVIFILE_FindStreamInTable(This, start, &stream,
860 &streamPos, &streamNr, FALSE)))
861 return AVIERR_ERROR;
863 /* limit to end of the stream */
864 count = samples;
865 if (streamPos + count > EditStreamEnd(This, streamNr))
866 count = EditStreamEnd(This, streamNr) - streamPos;
868 hr = IAVIStream_Read(stream, streamPos, count, buffer, buffersize,
869 &readBytes, &readSamples);
870 if (FAILED(hr))
871 return hr;
872 if (readBytes == 0 && readSamples == 0 && count != 0)
873 return AVIERR_FILEREAD; /* for bad stream implementations */
875 if (samplesread != NULL)
876 *samplesread += readSamples;
877 if (bytesread != NULL)
878 *bytesread += readBytes;
879 if (buffer != NULL) {
880 buffer = ((LPBYTE)buffer)+readBytes;
881 buffersize -= readBytes;
883 start += count;
884 samples -= count;
885 } while (This->sInfo.dwStart + This->sInfo.dwLength > start);
886 } else {
887 /* video like data -- frame-based */
888 LPBITMAPINFOHEADER lp;
890 if (samples == 0)
891 return AVIERR_OK;
893 if (FAILED(AVIFILE_FindStreamInTable(This, start, &stream,
894 &streamPos, &streamNr, FALSE)))
895 return AVIERR_ERROR;
897 lp = AVIFILE_ReadFrame(This, stream, streamPos);
898 if (lp == NULL)
899 return AVIERR_ERROR;
901 if (buffer != NULL) {
902 /* need size of format to skip */
903 if (lp->biBitCount <= 8) {
904 count = lp->biClrUsed > 0 ? lp->biClrUsed : 1 << lp->biBitCount;
905 count *= sizeof(RGBQUAD);
906 } else
907 count = 0;
908 count += lp->biSize;
910 if (buffersize < lp->biSizeImage)
911 return AVIERR_BUFFERTOOSMALL;
912 memcpy(buffer, (LPBYTE)lp + count, lp->biSizeImage);
915 if (bytesread != NULL)
916 *bytesread = lp->biSizeImage;
917 if (samplesread != NULL)
918 *samplesread = 1;
921 return AVIERR_OK;
924 static HRESULT WINAPI IEditAVIStream_fnWrite(IAVIStream*iface,LONG start,
925 LONG samples,LPVOID buffer,
926 LONG buffersize,DWORD flags,
927 LONG*sampwritten,LONG*byteswritten)
929 TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n",iface,start,samples,buffer,
930 buffersize,flags,sampwritten,byteswritten);
932 /* be sure return parameters have correct values */
933 if (sampwritten != NULL)
934 *sampwritten = 0;
935 if (byteswritten != NULL)
936 *byteswritten = 0;
938 return AVIERR_UNSUPPORTED;
941 static HRESULT WINAPI IEditAVIStream_fnDelete(IAVIStream*iface,LONG start,
942 LONG samples)
944 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
946 TRACE("(%p,%d,%d)\n",iface,start,samples);
948 return IAVIEditStream_Cut(&This->IAVIEditStream_iface,&start,&samples,NULL);
951 static HRESULT WINAPI IEditAVIStream_fnReadData(IAVIStream*iface,DWORD fcc,
952 LPVOID lp,LONG *lpread)
954 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
955 DWORD n;
957 TRACE("(%p,0x%08X,%p,%p)\n",iface,fcc,lp,lpread);
959 /* check parameters */
960 if (lp == NULL || lpread == NULL)
961 return AVIERR_BADPARAM;
963 /* simply ask every stream and return the first block found */
964 for (n = 0; n < This->nStreams; n++) {
965 HRESULT hr = IAVIStream_ReadData(This->pStreams[n].pStream,fcc,lp,lpread);
967 if (SUCCEEDED(hr))
968 return hr;
971 *lpread = 0;
972 return AVIERR_NODATA;
975 static HRESULT WINAPI IEditAVIStream_fnWriteData(IAVIStream*iface,DWORD fcc,
976 LPVOID lp,LONG size)
978 TRACE("(%p,0x%08X,%p,%d)\n",iface,fcc,lp,size);
980 return AVIERR_UNSUPPORTED;
983 static HRESULT WINAPI IEditAVIStream_fnSetInfo(IAVIStream*iface,
984 AVISTREAMINFOW*info,LONG len)
986 IAVIEditStreamImpl *This = impl_from_IAVIStream( iface );
988 TRACE("(%p,%p,%d)\n",iface,info,len);
990 return IAVIEditStream_SetInfo(&This->IAVIEditStream_iface,info,len);
993 static const struct IAVIStreamVtbl ieditstast = {
994 IEditAVIStream_fnQueryInterface,
995 IEditAVIStream_fnAddRef,
996 IEditAVIStream_fnRelease,
997 IEditAVIStream_fnCreate,
998 IEditAVIStream_fnInfo,
999 IEditAVIStream_fnFindSample,
1000 IEditAVIStream_fnReadFormat,
1001 IEditAVIStream_fnSetFormat,
1002 IEditAVIStream_fnRead,
1003 IEditAVIStream_fnWrite,
1004 IEditAVIStream_fnDelete,
1005 IEditAVIStream_fnReadData,
1006 IEditAVIStream_fnWriteData,
1007 IEditAVIStream_fnSetInfo
1010 static IAVIEditStreamImpl *AVIFILE_CreateEditStream(IAVIStream *pstream)
1012 IAVIEditStreamImpl *pedit = NULL;
1014 pedit = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIEditStreamImpl));
1015 if (pedit == NULL)
1016 return NULL;
1018 pedit->IAVIEditStream_iface.lpVtbl = &ieditstream;
1019 pedit->IAVIStream_iface.lpVtbl = &ieditstast;
1020 pedit->ref = 1;
1022 IAVIStream_Create(&pedit->IAVIStream_iface, (LPARAM)pstream, 0);
1024 return pedit;
1027 /***********************************************************************
1028 * CreateEditableStream (AVIFIL32.@)
1030 HRESULT WINAPI CreateEditableStream(IAVIStream **editable, IAVIStream *src)
1032 IAVIEditStream *edit = NULL;
1033 IAVIEditStreamImpl *editobj;
1034 HRESULT hr;
1036 TRACE("(%p,%p)\n", editable, src);
1038 if (!editable)
1039 return AVIERR_BADPARAM;
1040 *editable = NULL;
1042 if (src) {
1043 hr = IAVIStream_QueryInterface(src, &IID_IAVIEditStream, (void**)&edit);
1044 if (SUCCEEDED(hr) && edit) {
1045 hr = IAVIEditStream_Clone(edit, editable);
1046 IAVIEditStream_Release(edit);
1048 return hr;
1052 /* Need own implementation of IAVIEditStream */
1053 editobj = AVIFILE_CreateEditStream(src);
1054 if (!editobj)
1055 return AVIERR_MEMORY;
1056 *editable = &editobj->IAVIStream_iface;
1058 return S_OK;