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