1 /* 2 * Copyright 2002 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 "wingdi.h" 25 #include "winuser.h" 26 #include "winerror.h" 27 #include "mmsystem.h" 28 #include "vfw.h" 29 30 #include "avifile_private.h" 31 32 #include "wine/debug.h" 33 34 WINE_DEFAULT_DEBUG_CHANNEL(avifile); 35 36 #define MAX_FRAMESIZE (16 * 1024 * 1024) 37 #define MAX_FRAMESIZE_DIFF 512 38 39 /***********************************************************************/ 40 41 typedef struct _IAVIStreamImpl { 42 /* IUnknown stuff */ 43 IAVIStream IAVIStream_iface; 44 LONG ref; 45 46 /* IAVIStream stuff */ 47 PAVISTREAM pStream; 48 AVISTREAMINFOW sInfo; 49 50 PGETFRAME pg; 51 HIC hic; 52 DWORD dwICMFlags; 53 54 LONG lCurrent; 55 LONG lLastKey; 56 LONG lKeyFrameEvery; 57 DWORD dwLastQuality; 58 DWORD dwBytesPerFrame; 59 DWORD dwUnusedBytes; 60 61 LPBITMAPINFOHEADER lpbiCur; /* current frame */ 62 LPVOID lpCur; 63 LPBITMAPINFOHEADER lpbiPrev; /* previous frame */ 64 LPVOID lpPrev; 65 66 LPBITMAPINFOHEADER lpbiOutput; /* output format of codec */ 67 LONG cbOutput; 68 LPBITMAPINFOHEADER lpbiInput; /* input format for codec */ 69 LONG cbInput; 70 } IAVIStreamImpl; 71 72 /***********************************************************************/ 73 74 static HRESULT AVIFILE_EncodeFrame(IAVIStreamImpl *This, 75 LPBITMAPINFOHEADER lpbi, LPVOID lpBits); 76 static HRESULT AVIFILE_OpenGetFrame(IAVIStreamImpl *This); 77 78 static inline IAVIStreamImpl *impl_from_IAVIStream(IAVIStream *iface) 79 { 80 return CONTAINING_RECORD(iface, IAVIStreamImpl, IAVIStream_iface); 81 } 82 83 static inline void AVIFILE_Reset(IAVIStreamImpl *This) 84 { 85 This->lCurrent = -1; 86 This->lLastKey = 0; 87 This->dwLastQuality = ICQUALITY_HIGH; 88 This->dwUnusedBytes = 0; 89 } 90 91 static HRESULT WINAPI ICMStream_fnQueryInterface(IAVIStream *iface, 92 REFIID refiid, LPVOID *obj) 93 { 94 IAVIStreamImpl *This = impl_from_IAVIStream(iface); 95 96 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(refiid), obj); 97 98 if (IsEqualGUID(&IID_IUnknown, refiid) || 99 IsEqualGUID(&IID_IAVIStream, refiid)) { 100 *obj = &This->IAVIStream_iface; 101 IAVIStream_AddRef(iface); 102 103 return S_OK; 104 } 105 106 return OLE_E_ENUM_NOMORE; 107 } 108 109 static ULONG WINAPI ICMStream_fnAddRef(IAVIStream *iface) 110 { 111 IAVIStreamImpl *This = impl_from_IAVIStream(iface); 112 ULONG ref = InterlockedIncrement(&This->ref); 113 114 TRACE("(%p) -> %d\n", iface, ref); 115 116 /* also add reference to the nested stream */ 117 if (This->pStream != NULL) 118 IAVIStream_AddRef(This->pStream); 119 120 return ref; 121 } 122 123 static ULONG WINAPI ICMStream_fnRelease(IAVIStream* iface) 124 { 125 IAVIStreamImpl *This = impl_from_IAVIStream(iface); 126 ULONG ref = InterlockedDecrement(&This->ref); 127 128 TRACE("(%p) -> %d\n", iface, ref); 129 130 if (ref == 0) { 131 /* destruct */ 132 if (This->pg != NULL) { 133 AVIStreamGetFrameClose(This->pg); 134 This->pg = NULL; 135 } 136 if (This->pStream != NULL) { 137 IAVIStream_Release(This->pStream); 138 This->pStream = NULL; 139 } 140 if (This->hic != NULL) { 141 if (This->lpbiPrev != NULL) { 142 ICDecompressEnd(This->hic); 143 HeapFree(GetProcessHeap(), 0, This->lpbiPrev); 144 This->lpbiPrev = NULL; 145 This->lpPrev = NULL; 146 } 147 ICCompressEnd(This->hic); 148 This->hic = NULL; 149 } 150 if (This->lpbiCur != NULL) { 151 HeapFree(GetProcessHeap(), 0, This->lpbiCur); 152 This->lpbiCur = NULL; 153 This->lpCur = NULL; 154 } 155 if (This->lpbiOutput != NULL) { 156 HeapFree(GetProcessHeap(), 0, This->lpbiOutput); 157 This->lpbiOutput = NULL; 158 This->cbOutput = 0; 159 } 160 if (This->lpbiInput != NULL) { 161 HeapFree(GetProcessHeap(), 0, This->lpbiInput); 162 This->lpbiInput = NULL; 163 This->cbInput = 0; 164 } 165 166 HeapFree(GetProcessHeap(), 0, This); 167 168 return 0; 169 } 170 171 /* also release reference to the nested stream */ 172 if (This->pStream != NULL) 173 IAVIStream_Release(This->pStream); 174 175 return ref; 176 } 177 178 /* lParam1: PAVISTREAM 179 * lParam2: LPAVICOMPRESSOPTIONS 180 */ 181 static HRESULT WINAPI ICMStream_fnCreate(IAVIStream *iface, LPARAM lParam1, 182 LPARAM lParam2) 183 { 184 IAVIStreamImpl *This = impl_from_IAVIStream(iface); 185 186 ICINFO icinfo; 187 ICCOMPRESSFRAMES icFrames; 188 LPAVICOMPRESSOPTIONS pco = (LPAVICOMPRESSOPTIONS)lParam2; 189 190 TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2); 191 192 /* check parameter */ 193 if ((LPVOID)lParam1 == NULL) 194 return AVIERR_BADPARAM; 195 196 /* get infos from stream */ 197 IAVIStream_Info((PAVISTREAM)lParam1, &This->sInfo, sizeof(This->sInfo)); 198 if (This->sInfo.fccType != streamtypeVIDEO) 199 return AVIERR_ERROR; /* error in registry or AVIMakeCompressedStream */ 200 201 /* add reference to the stream */ 202 This->pStream = (PAVISTREAM)lParam1; 203 IAVIStream_AddRef(This->pStream); 204 205 AVIFILE_Reset(This); 206 207 if (pco != NULL && pco->fccHandler != comptypeDIB) { 208 /* we should compress */ 209 This->sInfo.fccHandler = pco->fccHandler; 210 211 This->hic = ICOpen(ICTYPE_VIDEO, pco->fccHandler, ICMODE_COMPRESS); 212 if (This->hic == NULL) 213 return AVIERR_NOCOMPRESSOR; 214 215 /* restore saved state of codec */ 216 if (pco->cbParms > 0 && pco->lpParms != NULL) { 217 ICSetState(This->hic, pco->lpParms, pco->cbParms); 218 } 219 220 /* set quality -- resolve default quality */ 221 This->sInfo.dwQuality = pco->dwQuality; 222 if (pco->dwQuality == ICQUALITY_DEFAULT) 223 This->sInfo.dwQuality = ICGetDefaultQuality(This->hic); 224 225 /* get capabilities of codec */ 226 ICGetInfo(This->hic, &icinfo, sizeof(icinfo)); 227 This->dwICMFlags = icinfo.dwFlags; 228 229 /* use keyframes? */ 230 if ((pco->dwFlags & AVICOMPRESSF_KEYFRAMES) && 231 (icinfo.dwFlags & (VIDCF_TEMPORAL|VIDCF_FASTTEMPORALC))) { 232 This->lKeyFrameEvery = pco->dwKeyFrameEvery; 233 } else 234 This->lKeyFrameEvery = 1; 235 236 /* use datarate? */ 237 if ((pco->dwFlags & AVICOMPRESSF_DATARATE)) { 238 /* Do we have a chance to reduce size to desired one? */ 239 if ((icinfo.dwFlags & (VIDCF_CRUNCH|VIDCF_QUALITY)) == 0) 240 return AVIERR_NOCOMPRESSOR; 241 242 assert(This->sInfo.dwRate != 0); 243 244 This->dwBytesPerFrame = MulDiv(pco->dwBytesPerSecond, 245 This->sInfo.dwScale, This->sInfo.dwRate); 246 } else { 247 pco->dwBytesPerSecond = 0; 248 This->dwBytesPerFrame = 0; 249 } 250 251 if (icinfo.dwFlags & VIDCF_COMPRESSFRAMES) { 252 memset(&icFrames, 0, sizeof(icFrames)); 253 icFrames.lpbiOutput = This->lpbiOutput; 254 icFrames.lpbiInput = This->lpbiInput; 255 icFrames.lFrameCount = This->sInfo.dwLength; 256 icFrames.lQuality = This->sInfo.dwQuality; 257 icFrames.lDataRate = pco->dwBytesPerSecond; 258 icFrames.lKeyRate = This->lKeyFrameEvery; 259 icFrames.dwRate = This->sInfo.dwRate; 260 icFrames.dwScale = This->sInfo.dwScale; 261 ICSendMessage(This->hic, ICM_COMPRESS_FRAMES_INFO, 262 (LPARAM)&icFrames, (LPARAM)sizeof(icFrames)); 263 } 264 } else 265 This->sInfo.fccHandler = comptypeDIB; 266 267 return AVIERR_OK; 268 } 269 270 static HRESULT WINAPI ICMStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi, 271 LONG size) 272 { 273 IAVIStreamImpl *This = impl_from_IAVIStream(iface); 274 275 TRACE("(%p,%p,%d)\n", iface, psi, size); 276 277 if (psi == NULL) 278 return AVIERR_BADPARAM; 279 if (size < 0) 280 return AVIERR_BADSIZE; 281 282 memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo))); 283 284 if ((DWORD)size < sizeof(This->sInfo)) 285 return AVIERR_BUFFERTOOSMALL; 286 return AVIERR_OK; 287 } 288 289 static LONG WINAPI ICMStream_fnFindSample(IAVIStream *iface, LONG pos, 290 LONG flags) 291 { 292 IAVIStreamImpl *This = impl_from_IAVIStream(iface); 293 294 TRACE("(%p,%d,0x%08X)\n",iface,pos,flags); 295 296 if (flags & FIND_FROM_START) { 297 pos = This->sInfo.dwStart; 298 flags &= ~(FIND_FROM_START|FIND_PREV); 299 flags |= FIND_NEXT; 300 } 301 302 if (flags & FIND_RET) 303 WARN(": FIND_RET flags will be ignored!\n"); 304 305 if (flags & FIND_KEY) { 306 if (This->hic == NULL) 307 return pos; /* we decompress so every frame is a keyframe */ 308 309 if (flags & FIND_PREV) { 310 /* need to read old or new frames? */ 311 if (This->lLastKey <= pos || pos < This->lCurrent) 312 IAVIStream_Read(iface, pos, 1, NULL, 0, NULL, NULL); 313 314 return This->lLastKey; 315 } 316 } else if (flags & FIND_ANY) { 317 return pos; /* We really don't know, reread is too expensive, so guess. */ 318 } else if (flags & FIND_FORMAT) { 319 if (flags & FIND_PREV) 320 return 0; 321 } 322 323 return -1; 324 } 325 326 static HRESULT WINAPI ICMStream_fnReadFormat(IAVIStream *iface, LONG pos, 327 LPVOID format, LONG *formatsize) 328 { 329 IAVIStreamImpl *This = impl_from_IAVIStream(iface); 330 331 LPBITMAPINFOHEADER lpbi; 332 HRESULT hr; 333 334 TRACE("(%p,%d,%p,%p)\n", iface, pos, format, formatsize); 335 336 if (formatsize == NULL) 337 return AVIERR_BADPARAM; 338 339 if (This->pg == NULL) { 340 hr = AVIFILE_OpenGetFrame(This); 341 342 if (FAILED(hr)) 343 return hr; 344 } 345 346 lpbi = AVIStreamGetFrame(This->pg, pos); 347 if (lpbi == NULL) 348 return AVIERR_MEMORY; 349 350 if (This->hic == NULL) { 351 LONG size = lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD); 352 353 if (size > 0) { 354 if (This->sInfo.dwSuggestedBufferSize < lpbi->biSizeImage) 355 This->sInfo.dwSuggestedBufferSize = lpbi->biSizeImage; 356 357 This->cbOutput = size; 358 if (format != NULL) { 359 if (This->lpbiOutput != NULL) 360 memcpy(format, This->lpbiOutput, min(*formatsize, This->cbOutput)); 361 else 362 memcpy(format, lpbi, min(*formatsize, size)); 363 } 364 } 365 } else if (format != NULL) 366 memcpy(format, This->lpbiOutput, min(*formatsize, This->cbOutput)); 367 368 if (*formatsize < This->cbOutput) 369 hr = AVIERR_BUFFERTOOSMALL; 370 else 371 hr = AVIERR_OK; 372 373 *formatsize = This->cbOutput; 374 return hr; 375 } 376 377 static HRESULT WINAPI ICMStream_fnSetFormat(IAVIStream *iface, LONG pos, 378 LPVOID format, LONG formatsize) 379 { 380 IAVIStreamImpl *This = impl_from_IAVIStream(iface); 381 382 TRACE("(%p,%d,%p,%d)\n", iface, pos, format, formatsize); 383 384 /* check parameters */ 385 if (format == NULL || formatsize <= 0) 386 return AVIERR_BADPARAM; 387 388 /* We can only accept RGB data for writing */ 389 if (((LPBITMAPINFOHEADER)format)->biCompression != BI_RGB) { 390 WARN(": need RGB data as input\n"); 391 return AVIERR_UNSUPPORTED; 392 } 393 394 /* Input format already known? 395 * Changing of palette is supported, but be quiet if it's the same */ 396 if (This->lpbiInput != NULL) { 397 if (This->cbInput != formatsize) 398 return AVIERR_UNSUPPORTED; 399 400 if (memcmp(format, This->lpbiInput, formatsize) == 0) 401 return AVIERR_OK; 402 } 403 404 /* Does the nested stream support writing? */ 405 if ((This->sInfo.dwCaps & AVIFILECAPS_CANWRITE) == 0) 406 return AVIERR_READONLY; 407 408 /* check if frame is already written */ 409 if (This->sInfo.dwLength + This->sInfo.dwStart > pos) 410 return AVIERR_UNSUPPORTED; 411 412 /* check if we should compress */ 413 if (This->sInfo.fccHandler == 0 || 414 This->sInfo.fccHandler == mmioFOURCC('N','O','N','E')) 415 This->sInfo.fccHandler = comptypeDIB; 416 417 /* only pass through? */ 418 if (This->sInfo.fccHandler == comptypeDIB) 419 return IAVIStream_SetFormat(This->pStream, pos, format, formatsize); 420 421 /* initial format setting? */ 422 if (This->lpbiInput == NULL) { 423 ULONG size; 424 425 assert(This->hic != NULL); 426 427 /* get memory for input format */ 428 This->lpbiInput = HeapAlloc(GetProcessHeap(), 0, formatsize); 429 if (This->lpbiInput == NULL) 430 return AVIERR_MEMORY; 431 This->cbInput = formatsize; 432 memcpy(This->lpbiInput, format, formatsize); 433 434 /* get output format */ 435 size = ICCompressGetFormatSize(This->hic, This->lpbiInput); 436 if (size < sizeof(BITMAPINFOHEADER)) 437 return AVIERR_COMPRESSOR; 438 This->lpbiOutput = HeapAlloc(GetProcessHeap(), 0, size); 439 if (This->lpbiOutput == NULL) 440 return AVIERR_MEMORY; 441 This->cbOutput = size; 442 if (ICCompressGetFormat(This->hic,This->lpbiInput,This->lpbiOutput) < S_OK) 443 return AVIERR_COMPRESSOR; 444 445 /* update AVISTREAMINFO structure */ 446 This->sInfo.rcFrame.right = 447 This->sInfo.rcFrame.left + This->lpbiOutput->biWidth; 448 This->sInfo.rcFrame.bottom = 449 This->sInfo.rcFrame.top + This->lpbiOutput->biHeight; 450 451 /* prepare codec for compression */ 452 if (ICCompressBegin(This->hic, This->lpbiInput, This->lpbiOutput) != S_OK) 453 return AVIERR_COMPRESSOR; 454 455 /* allocate memory for compressed frame */ 456 size = ICCompressGetSize(This->hic, This->lpbiInput, This->lpbiOutput); 457 This->lpbiCur = HeapAlloc(GetProcessHeap(), 0, This->cbOutput + size); 458 if (This->lpbiCur == NULL) 459 return AVIERR_MEMORY; 460 memcpy(This->lpbiCur, This->lpbiOutput, This->cbOutput); 461 This->lpCur = DIBPTR(This->lpbiCur); 462 463 /* allocate memory for last frame if needed */ 464 if (This->lKeyFrameEvery != 1 && 465 (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) { 466 size = ICDecompressGetFormatSize(This->hic, This->lpbiOutput); 467 This->lpbiPrev = HeapAlloc(GetProcessHeap(), 0, size); 468 if (This->lpbiPrev == NULL) 469 return AVIERR_MEMORY; 470 if (ICDecompressGetFormat(This->hic, This->lpbiOutput, This->lpbiPrev) < S_OK) 471 return AVIERR_COMPRESSOR; 472 473 if (This->lpbiPrev->biSizeImage == 0) { 474 This->lpbiPrev->biSizeImage = 475 DIBWIDTHBYTES(*This->lpbiPrev) * This->lpbiPrev->biHeight; 476 } 477 478 /* get memory for format and picture */ 479 size += This->lpbiPrev->biSizeImage; 480 This->lpbiPrev = HeapReAlloc(GetProcessHeap(), 0, This->lpbiPrev, size); 481 if (This->lpbiPrev == NULL) 482 return AVIERR_MEMORY; 483 This->lpPrev = DIBPTR(This->lpbiPrev); 484 485 /* prepare codec also for decompression */ 486 if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK) 487 return AVIERR_COMPRESSOR; 488 } 489 } else { 490 /* format change -- check that's only the palette */ 491 LPBITMAPINFOHEADER lpbi = format; 492 493 if (lpbi->biSize != This->lpbiInput->biSize || 494 lpbi->biWidth != This->lpbiInput->biWidth || 495 lpbi->biHeight != This->lpbiInput->biHeight || 496 lpbi->biBitCount != This->lpbiInput->biBitCount || 497 lpbi->biPlanes != This->lpbiInput->biPlanes || 498 lpbi->biCompression != This->lpbiInput->biCompression || 499 lpbi->biClrUsed != This->lpbiInput->biClrUsed) 500 return AVIERR_UNSUPPORTED; 501 502 /* get new output format */ 503 if (ICCompressGetFormat(This->hic, lpbi, This->lpbiOutput) < S_OK) 504 return AVIERR_BADFORMAT; 505 506 /* restart compression */ 507 ICCompressEnd(This->hic); 508 if (ICCompressBegin(This->hic, lpbi, This->lpbiOutput) != S_OK) 509 return AVIERR_COMPRESSOR; 510 511 /* check if we need to restart decompression also */ 512 if (This->lKeyFrameEvery != 1 && 513 (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) { 514 ICDecompressEnd(This->hic); 515 if (ICDecompressGetFormat(This->hic,This->lpbiOutput,This->lpbiPrev) < S_OK) 516 return AVIERR_COMPRESSOR; 517 if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK) 518 return AVIERR_COMPRESSOR; 519 } 520 } 521 522 /* tell nested stream the new format */ 523 return IAVIStream_SetFormat(This->pStream, pos, 524 This->lpbiOutput, This->cbOutput); 525 } 526 527 static HRESULT WINAPI ICMStream_fnRead(IAVIStream *iface, LONG start, 528 LONG samples, LPVOID buffer, 529 LONG buffersize, LPLONG bytesread, 530 LPLONG samplesread) 531 { 532 IAVIStreamImpl *This = impl_from_IAVIStream(iface); 533 534 LPBITMAPINFOHEADER lpbi; 535 536 TRACE("(%p,%d,%d,%p,%d,%p,%p)\n", iface, start, samples, buffer, 537 buffersize, bytesread, samplesread); 538 539 /* clear return parameters if given */ 540 if (bytesread != NULL) 541 *bytesread = 0; 542 if (samplesread != NULL) 543 *samplesread = 0; 544 545 if (samples == 0) 546 return AVIERR_OK; 547 548 /* check parameters */ 549 if (samples != 1 && (bytesread == NULL && samplesread == NULL)) 550 return AVIERR_BADPARAM; 551 if (samples == -1) /* read as much as we could */ 552 samples = 1; 553 554 if (This->pg == NULL) { 555 HRESULT hr = AVIFILE_OpenGetFrame(This); 556 557 if (FAILED(hr)) 558 return hr; 559 } 560 561 /* compress or decompress? */ 562 if (This->hic == NULL) { 563 /* decompress */ 564 lpbi = AVIStreamGetFrame(This->pg, start); 565 if (lpbi == NULL) 566 return AVIERR_MEMORY; 567 568 if (buffer != NULL && buffersize > 0) { 569 /* check buffersize */ 570 if (buffersize < lpbi->biSizeImage) 571 return AVIERR_BUFFERTOOSMALL; 572 573 memcpy(buffer, DIBPTR(lpbi), lpbi->biSizeImage); 574 } 575 576 /* fill out return parameters if given */ 577 if (bytesread != NULL) 578 *bytesread = lpbi->biSizeImage; 579 } else { 580 /* compress */ 581 if (This->lCurrent > start) 582 AVIFILE_Reset(This); 583 584 while (start > This->lCurrent) { 585 HRESULT hr; 586 587 lpbi = AVIStreamGetFrame(This->pg, ++This->lCurrent); 588 if (lpbi == NULL) { 589 AVIFILE_Reset(This); 590 return AVIERR_MEMORY; 591 } 592 593 hr = AVIFILE_EncodeFrame(This, lpbi, DIBPTR(lpbi)); 594 if (FAILED(hr)) { 595 AVIFILE_Reset(This); 596 return hr; 597 } 598 } 599 600 if (buffer != NULL && buffersize > 0) { 601 /* check buffersize */ 602 if (This->lpbiCur->biSizeImage > buffersize) 603 return AVIERR_BUFFERTOOSMALL; 604 605 memcpy(buffer, This->lpCur, This->lpbiCur->biSizeImage); 606 } 607 608 /* fill out return parameters if given */ 609 if (bytesread != NULL) 610 *bytesread = This->lpbiCur->biSizeImage; 611 } 612 613 /* fill out return parameters if given */ 614 if (samplesread != NULL) 615 *samplesread = 1; 616 617 return AVIERR_OK; 618 } 619 620 static HRESULT WINAPI ICMStream_fnWrite(IAVIStream *iface, LONG start, 621 LONG samples, LPVOID buffer, 622 LONG buffersize, DWORD flags, 623 LPLONG sampwritten, 624 LPLONG byteswritten) 625 { 626 IAVIStreamImpl *This = impl_from_IAVIStream(iface); 627 628 HRESULT hr; 629 630 TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n", iface, start, samples, 631 buffer, buffersize, flags, sampwritten, byteswritten); 632 633 /* clear return parameters if given */ 634 if (sampwritten != NULL) 635 *sampwritten = 0; 636 if (byteswritten != NULL) 637 *byteswritten = 0; 638 639 /* check parameters */ 640 if (buffer == NULL && (buffersize > 0 || samples > 0)) 641 return AVIERR_BADPARAM; 642 643 if (This->sInfo.fccHandler == comptypeDIB) { 644 /* only pass through */ 645 flags |= AVIIF_KEYFRAME; 646 647 return IAVIStream_Write(This->pStream, start, samples, buffer, buffersize, 648 flags, sampwritten, byteswritten); 649 } else { 650 /* compress data before writing to pStream */ 651 if (samples != 1 && (sampwritten == NULL && byteswritten == NULL)) 652 return AVIERR_UNSUPPORTED; 653 654 This->lCurrent = start; 655 hr = AVIFILE_EncodeFrame(This, This->lpbiInput, buffer); 656 if (FAILED(hr)) 657 return hr; 658 659 if (This->lLastKey == start) 660 flags |= AVIIF_KEYFRAME; 661 662 return IAVIStream_Write(This->pStream, start, samples, This->lpCur, 663 This->lpbiCur->biSizeImage, flags, byteswritten, 664 sampwritten); 665 } 666 } 667 668 static HRESULT WINAPI ICMStream_fnDelete(IAVIStream *iface, LONG start, 669 LONG samples) 670 { 671 IAVIStreamImpl *This = impl_from_IAVIStream(iface); 672 673 TRACE("(%p,%d,%d)\n", iface, start, samples); 674 675 return IAVIStream_Delete(This->pStream, start, samples); 676 } 677 678 static HRESULT WINAPI ICMStream_fnReadData(IAVIStream *iface, DWORD fcc, 679 LPVOID lp, LPLONG lpread) 680 { 681 IAVIStreamImpl *This = impl_from_IAVIStream(iface); 682 683 TRACE("(%p,0x%08X,%p,%p)\n", iface, fcc, lp, lpread); 684 685 assert(This->pStream != NULL); 686 687 return IAVIStream_ReadData(This->pStream, fcc, lp, lpread); 688 } 689 690 static HRESULT WINAPI ICMStream_fnWriteData(IAVIStream *iface, DWORD fcc, 691 LPVOID lp, LONG size) 692 { 693 IAVIStreamImpl *This = impl_from_IAVIStream(iface); 694 695 TRACE("(%p,0x%08x,%p,%d)\n", iface, fcc, lp, size); 696 697 assert(This->pStream != NULL); 698 699 return IAVIStream_WriteData(This->pStream, fcc, lp, size); 700 } 701 702 static HRESULT WINAPI ICMStream_fnSetInfo(IAVIStream *iface, 703 LPAVISTREAMINFOW info, LONG infolen) 704 { 705 FIXME("(%p,%p,%d): stub\n", iface, info, infolen); 706 707 return E_FAIL; 708 } 709 710 static const struct IAVIStreamVtbl iicmst = { 711 ICMStream_fnQueryInterface, 712 ICMStream_fnAddRef, 713 ICMStream_fnRelease, 714 ICMStream_fnCreate, 715 ICMStream_fnInfo, 716 ICMStream_fnFindSample, 717 ICMStream_fnReadFormat, 718 ICMStream_fnSetFormat, 719 ICMStream_fnRead, 720 ICMStream_fnWrite, 721 ICMStream_fnDelete, 722 ICMStream_fnReadData, 723 ICMStream_fnWriteData, 724 ICMStream_fnSetInfo 725 }; 726 727 HRESULT AVIFILE_CreateICMStream(REFIID riid, LPVOID *ppv) 728 { 729 IAVIStreamImpl *pstream; 730 HRESULT hr; 731 732 assert(riid != NULL && ppv != NULL); 733 734 *ppv = NULL; 735 736 pstream = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIStreamImpl)); 737 if (pstream == NULL) 738 return AVIERR_MEMORY; 739 740 pstream->IAVIStream_iface.lpVtbl = &iicmst; 741 AVIFILE_Reset(pstream); 742 743 hr = IAVIStream_QueryInterface(&pstream->IAVIStream_iface, riid, ppv); 744 if (FAILED(hr)) 745 HeapFree(GetProcessHeap(), 0, pstream); 746 747 return hr; 748 } 749 750 /***********************************************************************/ 751 752 static HRESULT AVIFILE_EncodeFrame(IAVIStreamImpl *This, 753 LPBITMAPINFOHEADER lpbi, LPVOID lpBits) 754 { 755 DWORD dwMinQual, dwMaxQual, dwCurQual; 756 DWORD dwRequest; 757 DWORD icmFlags = 0; 758 DWORD idxFlags = 0; 759 BOOL bDecreasedQual = FALSE; 760 BOOL doSizeCheck; 761 BOOL noPrev; 762 763 /* make lKeyFrameEvery and at start a keyframe */ 764 if ((This->lKeyFrameEvery != 0 && 765 (This->lCurrent - This->lLastKey) >= This->lKeyFrameEvery) || 766 This->lCurrent == This->sInfo.dwStart) { 767 idxFlags = AVIIF_KEYFRAME; 768 icmFlags = ICCOMPRESS_KEYFRAME; 769 } 770 771 if (This->lKeyFrameEvery != 0) { 772 if (This->lCurrent == This->sInfo.dwStart) { 773 if (idxFlags & AVIIF_KEYFRAME) { 774 /* allow keyframes to consume all unused bytes */ 775 dwRequest = This->dwBytesPerFrame + This->dwUnusedBytes; 776 This->dwUnusedBytes = 0; 777 } else { 778 /* for non-keyframes only allow some of the unused bytes to be consumed */ 779 DWORD tmp1 = 0; 780 DWORD tmp2; 781 782 if (This->dwBytesPerFrame >= This->dwUnusedBytes) 783 tmp1 = This->dwBytesPerFrame / This->lKeyFrameEvery; 784 tmp2 = (This->dwUnusedBytes + tmp1) / This->lKeyFrameEvery; 785 786 dwRequest = This->dwBytesPerFrame - tmp1 + tmp2; 787 This->dwUnusedBytes -= tmp2; 788 } 789 } else 790 dwRequest = MAX_FRAMESIZE; 791 } else { 792 /* only one keyframe at start desired */ 793 if (This->lCurrent == This->sInfo.dwStart) { 794 dwRequest = This->dwBytesPerFrame + This->dwUnusedBytes; 795 This->dwUnusedBytes = 0; 796 } else 797 dwRequest = MAX_FRAMESIZE; 798 } 799 800 /* must we check for frame size to gain the requested 801 * data rate or can we trust the codec? */ 802 doSizeCheck = (dwRequest != 0 && ((This->dwICMFlags & (VIDCF_CRUNCH|VIDCF_QUALITY)) == 0)); 803 804 dwMaxQual = dwCurQual = This->sInfo.dwQuality; 805 dwMinQual = ICQUALITY_LOW; 806 807 noPrev = TRUE; 808 if ((icmFlags & ICCOMPRESS_KEYFRAME) == 0 && 809 (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) 810 noPrev = FALSE; 811 812 do { 813 DWORD idxCkid = 0; 814 DWORD res; 815 816 res = ICCompress(This->hic,icmFlags,This->lpbiCur,This->lpCur,lpbi,lpBits, 817 &idxCkid, &idxFlags, This->lCurrent, dwRequest, dwCurQual, 818 noPrev ? NULL:This->lpbiPrev, noPrev ? NULL:This->lpPrev); 819 if (res == ICERR_NEWPALETTE) { 820 FIXME(": codec has changed palette -- unhandled!\n"); 821 } else if (res != ICERR_OK) 822 return AVIERR_COMPRESSOR; 823 824 /* need to check for framesize */ 825 if (! doSizeCheck) 826 break; 827 828 if (dwRequest >= This->lpbiCur->biSizeImage) { 829 /* frame is smaller -- try to maximize quality */ 830 if (dwMaxQual - dwCurQual > 10) { 831 DWORD tmp = dwRequest / 8; 832 833 if (tmp < MAX_FRAMESIZE_DIFF) 834 tmp = MAX_FRAMESIZE_DIFF; 835 836 if (tmp < dwRequest - This->lpbiCur->biSizeImage && bDecreasedQual) { 837 tmp = dwCurQual; 838 dwCurQual = (dwMinQual + dwMaxQual) / 2; 839 dwMinQual = tmp; 840 continue; 841 } 842 } else 843 break; 844 } else if (dwMaxQual - dwMinQual <= 1) { 845 break; 846 } else { 847 dwMaxQual = dwCurQual; 848 849 if (bDecreasedQual || dwCurQual == This->dwLastQuality) 850 dwCurQual = (dwMinQual + dwMaxQual) / 2; 851 else 852 FIXME(": no new quality computed min=%u cur=%u max=%u last=%u\n", 853 dwMinQual, dwCurQual, dwMaxQual, This->dwLastQuality); 854 855 bDecreasedQual = TRUE; 856 } 857 } while (TRUE); 858 859 /* remember some values */ 860 This->dwLastQuality = dwCurQual; 861 This->dwUnusedBytes = dwRequest - This->lpbiCur->biSizeImage; 862 if (icmFlags & ICCOMPRESS_KEYFRAME) 863 This->lLastKey = This->lCurrent; 864 865 /* Does we manage previous frame? */ 866 if (This->lpPrev != NULL && This->lKeyFrameEvery != 1) 867 ICDecompress(This->hic, 0, This->lpbiCur, This->lpCur, 868 This->lpbiPrev, This->lpPrev); 869 870 return AVIERR_OK; 871 } 872 873 static HRESULT AVIFILE_OpenGetFrame(IAVIStreamImpl *This) 874 { 875 LPBITMAPINFOHEADER lpbi; 876 DWORD size; 877 878 /* pre-conditions */ 879 assert(This != NULL); 880 assert(This->pStream != NULL); 881 assert(This->pg == NULL); 882 883 This->pg = AVIStreamGetFrameOpen(This->pStream, NULL); 884 if (This->pg == NULL) 885 return AVIERR_ERROR; 886 887 /* When we only decompress this is enough */ 888 if (This->sInfo.fccHandler == comptypeDIB) 889 return AVIERR_OK; 890 891 assert(This->hic != NULL); 892 assert(This->lpbiOutput == NULL); 893 894 /* get input format */ 895 lpbi = AVIStreamGetFrame(This->pg, This->sInfo.dwStart); 896 if (lpbi == NULL) 897 return AVIERR_MEMORY; 898 899 /* get memory for output format */ 900 size = ICCompressGetFormatSize(This->hic, lpbi); 901 if ((LONG)size < (LONG)sizeof(BITMAPINFOHEADER)) 902 return AVIERR_COMPRESSOR; 903 This->lpbiOutput = HeapAlloc(GetProcessHeap(), 0, size); 904 if (This->lpbiOutput == NULL) 905 return AVIERR_MEMORY; 906 This->cbOutput = size; 907 908 if (ICCompressGetFormat(This->hic, lpbi, This->lpbiOutput) < S_OK) 909 return AVIERR_BADFORMAT; 910 911 /* update AVISTREAMINFO structure */ 912 This->sInfo.rcFrame.right = 913 This->sInfo.rcFrame.left + This->lpbiOutput->biWidth; 914 This->sInfo.rcFrame.bottom = 915 This->sInfo.rcFrame.top + This->lpbiOutput->biHeight; 916 This->sInfo.dwSuggestedBufferSize = 917 ICCompressGetSize(This->hic, lpbi, This->lpbiOutput); 918 919 /* prepare codec for compression */ 920 if (ICCompressBegin(This->hic, lpbi, This->lpbiOutput) != S_OK) 921 return AVIERR_COMPRESSOR; 922 923 /* allocate memory for current frame */ 924 size += This->sInfo.dwSuggestedBufferSize; 925 This->lpbiCur = HeapAlloc(GetProcessHeap(), 0, size); 926 if (This->lpbiCur == NULL) 927 return AVIERR_MEMORY; 928 memcpy(This->lpbiCur, This->lpbiOutput, This->cbOutput); 929 This->lpCur = DIBPTR(This->lpbiCur); 930 931 /* allocate memory for last frame if needed */ 932 if (This->lKeyFrameEvery != 1 && 933 (This->dwICMFlags & VIDCF_FASTTEMPORALC) == 0) { 934 size = ICDecompressGetFormatSize(This->hic, This->lpbiOutput); 935 This->lpbiPrev = HeapAlloc(GetProcessHeap(), 0, size); 936 if (This->lpbiPrev == NULL) 937 return AVIERR_MEMORY; 938 if (ICDecompressGetFormat(This->hic, This->lpbiOutput, This->lpbiPrev) < S_OK) 939 return AVIERR_COMPRESSOR; 940 941 if (This->lpbiPrev->biSizeImage == 0) { 942 This->lpbiPrev->biSizeImage = 943 DIBWIDTHBYTES(*This->lpbiPrev) * This->lpbiPrev->biHeight; 944 } 945 946 /* get memory for format and picture */ 947 size += This->lpbiPrev->biSizeImage; 948 This->lpbiPrev = HeapReAlloc(GetProcessHeap(), 0, This->lpbiPrev, size ); 949 if (This->lpbiPrev == NULL) 950 return AVIERR_MEMORY; 951 This->lpPrev = DIBPTR(This->lpbiPrev); 952 953 /* prepare codec also for decompression */ 954 if (ICDecompressBegin(This->hic,This->lpbiOutput,This->lpbiPrev) != S_OK) 955 return AVIERR_COMPRESSOR; 956 } 957 958 return AVIERR_OK; 959 } 960