1 /* 2 * Copyright 2002-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 "avifile_private.h" 20 21 #ifndef DIBPTR 22 #define DIBPTR(lp) ((LPBYTE)(lp) + (lp)->biSize + \ 23 (lp)->biClrUsed * sizeof(RGBQUAD)) 24 #endif 25 26 /***********************************************************************/ 27 28 typedef struct _IGetFrameImpl { 29 /* IUnknown stuff */ 30 IGetFrame IGetFrame_iface; 31 LONG ref; 32 33 /* IGetFrame stuff */ 34 BOOL bFixedStream; 35 PAVISTREAM pStream; 36 37 LPVOID lpInBuffer; 38 LONG cbInBuffer; 39 LPBITMAPINFOHEADER lpInFormat; 40 LONG cbInFormat; 41 42 LONG lCurrentFrame; 43 LPBITMAPINFOHEADER lpOutFormat; 44 LPVOID lpOutBuffer; 45 46 HIC hic; 47 BOOL bResize; 48 DWORD x; 49 DWORD y; 50 DWORD dx; 51 DWORD dy; 52 53 BOOL bFormatChanges; 54 DWORD dwFormatChangeCount; 55 DWORD dwEditCount; 56 } IGetFrameImpl; 57 58 /***********************************************************************/ 59 60 static inline IGetFrameImpl *impl_from_IGetFrame(IGetFrame *iface) 61 { 62 return CONTAINING_RECORD(iface, IGetFrameImpl, IGetFrame_iface); 63 } 64 65 static void AVIFILE_CloseCompressor(IGetFrameImpl *This) 66 { 67 if (This->lpInFormat != This->lpOutFormat) { 68 HeapFree(GetProcessHeap(), 0, This->lpOutFormat); 69 This->lpOutFormat = NULL; 70 } 71 HeapFree(GetProcessHeap(), 0, This->lpInFormat); 72 This->lpInFormat = NULL; 73 if (This->hic != NULL) { 74 if (This->bResize) 75 ICDecompressExEnd(This->hic); 76 else 77 ICDecompressEnd(This->hic); 78 ICClose(This->hic); 79 This->hic = NULL; 80 } 81 } 82 83 static HRESULT WINAPI IGetFrame_fnQueryInterface(IGetFrame *iface, 84 REFIID refiid, LPVOID *obj) 85 { 86 IGetFrameImpl *This = impl_from_IGetFrame(iface); 87 88 TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj); 89 90 if (IsEqualGUID(&IID_IUnknown, refiid) || 91 IsEqualGUID(&IID_IGetFrame, refiid)) { 92 *obj = iface; 93 IGetFrame_AddRef(iface); 94 return S_OK; 95 } 96 97 return OLE_E_ENUM_NOMORE; 98 } 99 100 static ULONG WINAPI IGetFrame_fnAddRef(IGetFrame *iface) 101 { 102 IGetFrameImpl *This = impl_from_IGetFrame(iface); 103 ULONG ref = InterlockedIncrement(&This->ref); 104 105 TRACE("(%p)\n", iface); 106 107 return ref; 108 } 109 110 static ULONG WINAPI IGetFrame_fnRelease(IGetFrame *iface) 111 { 112 IGetFrameImpl *This = impl_from_IGetFrame(iface); 113 ULONG ref = InterlockedDecrement(&This->ref); 114 115 TRACE("(%p)\n", iface); 116 117 if (!ref) { 118 AVIFILE_CloseCompressor(This); 119 if (This->pStream != NULL) { 120 IAVIStream_Release(This->pStream); 121 This->pStream = NULL; 122 } 123 124 HeapFree(GetProcessHeap(), 0, iface); 125 return 0; 126 } 127 128 return ref; 129 } 130 131 static LPVOID WINAPI IGetFrame_fnGetFrame(IGetFrame *iface, LONG lPos) 132 { 133 IGetFrameImpl *This = impl_from_IGetFrame(iface); 134 135 LONG readBytes; 136 LONG readSamples; 137 138 TRACE("(%p,%d)\n", iface, lPos); 139 140 /* We don't want negative start values! -- marks invalid buffer content */ 141 if (lPos < 0) 142 return NULL; 143 144 /* check state */ 145 if (This->pStream == NULL) 146 return NULL; 147 if (This->lpInFormat == NULL) 148 return NULL; 149 150 /* Could stream have changed? */ 151 if (! This->bFixedStream) { 152 AVISTREAMINFOW sInfo; 153 154 IAVIStream_Info(This->pStream, &sInfo, sizeof(sInfo)); 155 156 if (sInfo.dwEditCount != This->dwEditCount) { 157 This->dwEditCount = sInfo.dwEditCount; 158 This->lCurrentFrame = -1; 159 } 160 161 if (sInfo.dwFormatChangeCount != This->dwFormatChangeCount) { 162 /* stream has changed */ 163 if (This->lpOutFormat != NULL) { 164 BITMAPINFOHEADER bi; 165 166 bi = *This->lpOutFormat; 167 AVIFILE_CloseCompressor(This); 168 169 if (FAILED(IGetFrame_SetFormat(iface, &bi, NULL, 0, 0, -1, -1))) { 170 if (FAILED(IGetFrame_SetFormat(iface, NULL, NULL, 0, 0, -1, -1))) 171 return NULL; 172 } 173 } else if (FAILED(IGetFrame_SetFormat(iface, NULL, NULL, 0, 0, -1, -1))) 174 return NULL; 175 } 176 } 177 178 if (lPos != This->lCurrentFrame) { 179 LONG lNext = IAVIStream_FindSample(This->pStream,lPos,FIND_KEY|FIND_PREV); 180 181 if (lNext == -1) 182 return NULL; /* frame doesn't exist */ 183 if (lNext <= This->lCurrentFrame && This->lCurrentFrame < lPos) 184 lNext = This->lCurrentFrame + 1; 185 186 for (; lNext <= lPos; lNext++) { 187 /* new format for this frame? */ 188 if (This->bFormatChanges) { 189 IAVIStream_ReadFormat(This->pStream, lNext, 190 This->lpInFormat, &This->cbInFormat); 191 if (This->lpOutFormat != NULL) { 192 if (This->lpOutFormat->biBitCount <= 8) 193 ICDecompressGetPalette(This->hic, This->lpInFormat, 194 This->lpOutFormat); 195 } 196 } 197 198 /* read input frame */ 199 while (FAILED(AVIStreamRead(This->pStream, lNext, 1, This->lpInBuffer, 200 This->cbInBuffer, &readBytes, &readSamples))) { 201 /* not enough memory for input buffer? */ 202 readBytes = 0; 203 if (FAILED(AVIStreamSampleSize(This->pStream, lNext, &readBytes))) 204 return NULL; /* bad thing, but bad things will happen */ 205 if (readBytes <= 0) { 206 ERR(": IAVIStream::Read doesn't return needed bytes!\n"); 207 return NULL; 208 } 209 210 /* IAVIStream::Read failed because of other reasons not buffersize? */ 211 if (This->cbInBuffer >= readBytes) 212 break; 213 This->cbInBuffer = This->cbInFormat + readBytes; 214 This->lpInFormat = HeapReAlloc(GetProcessHeap(), 0, This->lpInFormat, This->cbInBuffer); 215 if (This->lpInFormat == NULL) 216 return NULL; /* out of memory */ 217 This->lpInBuffer = (BYTE*)This->lpInFormat + This->cbInFormat; 218 } 219 220 if (readSamples != 1) { 221 ERR(": no frames read\n"); 222 return NULL; 223 } 224 if (readBytes != 0) { 225 This->lpInFormat->biSizeImage = readBytes; 226 227 /* nothing to decompress? */ 228 if (This->hic == NULL) { 229 This->lCurrentFrame = lPos; 230 return This->lpInFormat; 231 } 232 233 if (This->bResize) { 234 ICDecompressEx(This->hic,0,This->lpInFormat,This->lpInBuffer,0,0, 235 This->lpInFormat->biWidth,This->lpInFormat->biHeight, 236 This->lpOutFormat,This->lpOutBuffer,This->x,This->y, 237 This->dx,This->dy); 238 } else { 239 ICDecompress(This->hic, 0, This->lpInFormat, This->lpInBuffer, 240 This->lpOutFormat, This->lpOutBuffer); 241 } 242 } 243 } /* for (lNext < lPos) */ 244 } /* if (This->lCurrentFrame != lPos) */ 245 246 return (This->hic == NULL ? This->lpInFormat : This->lpOutFormat); 247 } 248 249 static HRESULT WINAPI IGetFrame_fnBegin(IGetFrame *iface, LONG lStart, 250 LONG lEnd, LONG lRate) 251 { 252 IGetFrameImpl *This = impl_from_IGetFrame(iface); 253 254 TRACE("(%p,%d,%d,%d)\n", iface, lStart, lEnd, lRate); 255 256 This->bFixedStream = TRUE; 257 258 return (IGetFrame_GetFrame(iface, lStart) ? AVIERR_OK : AVIERR_ERROR); 259 } 260 261 static HRESULT WINAPI IGetFrame_fnEnd(IGetFrame *iface) 262 { 263 IGetFrameImpl *This = impl_from_IGetFrame(iface); 264 265 TRACE("(%p)\n", iface); 266 267 This->bFixedStream = FALSE; 268 269 return AVIERR_OK; 270 } 271 272 static HRESULT WINAPI IGetFrame_fnSetFormat(IGetFrame *iface, 273 LPBITMAPINFOHEADER lpbiWanted, 274 LPVOID lpBits, INT x, INT y, 275 INT dx, INT dy) 276 { 277 IGetFrameImpl *This = impl_from_IGetFrame(iface); 278 279 AVISTREAMINFOW sInfo; 280 LPBITMAPINFOHEADER lpbi = lpbiWanted; 281 BOOL bBestDisplay = FALSE; 282 283 TRACE("(%p,%p,%p,%d,%d,%d,%d)\n", iface, lpbiWanted, lpBits, 284 x, y, dx, dy); 285 286 if (This->pStream == NULL) 287 return AVIERR_ERROR; 288 289 if (lpbiWanted == (LPBITMAPINFOHEADER)AVIGETFRAMEF_BESTDISPLAYFMT) { 290 lpbi = NULL; 291 bBestDisplay = TRUE; 292 } 293 294 IAVIStream_Info(This->pStream, &sInfo, sizeof(sInfo)); 295 if (sInfo.fccType != streamtypeVIDEO) 296 return AVIERR_UNSUPPORTED; 297 298 This->bFormatChanges = (sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) != 0; 299 This->dwFormatChangeCount = sInfo.dwFormatChangeCount; 300 This->dwEditCount = sInfo.dwEditCount; 301 This->lCurrentFrame = -1; 302 303 /* get input format from stream */ 304 if (This->lpInFormat == NULL) { 305 HRESULT hr; 306 307 This->cbInBuffer = (LONG)sInfo.dwSuggestedBufferSize; 308 if (This->cbInBuffer == 0) 309 This->cbInBuffer = 1024; 310 311 IAVIStream_ReadFormat(This->pStream, sInfo.dwStart, 312 NULL, &This->cbInFormat); 313 314 This->lpInFormat = HeapAlloc(GetProcessHeap(), 0, This->cbInFormat + This->cbInBuffer); 315 if (This->lpInFormat == NULL) { 316 AVIFILE_CloseCompressor(This); 317 return AVIERR_MEMORY; 318 } 319 320 hr = IAVIStream_ReadFormat(This->pStream, sInfo.dwStart, This->lpInFormat, &This->cbInFormat); 321 if (FAILED(hr)) { 322 AVIFILE_CloseCompressor(This); 323 return hr; 324 } 325 326 This->lpInBuffer = ((LPBYTE)This->lpInFormat) + This->cbInFormat; 327 } 328 329 /* check input format */ 330 if (This->lpInFormat->biClrUsed == 0 && This->lpInFormat->biBitCount <= 8) 331 This->lpInFormat->biClrUsed = 1u << This->lpInFormat->biBitCount; 332 if (This->lpInFormat->biSizeImage == 0 && 333 This->lpInFormat->biCompression == BI_RGB) { 334 This->lpInFormat->biSizeImage = 335 DIBWIDTHBYTES(*This->lpInFormat) * This->lpInFormat->biHeight; 336 } 337 338 /* only to pass through? */ 339 if (This->lpInFormat->biCompression == BI_RGB && lpBits == NULL) { 340 if (lpbi == NULL || 341 (lpbi->biCompression == BI_RGB && 342 lpbi->biWidth == This->lpInFormat->biWidth && 343 lpbi->biHeight == This->lpInFormat->biHeight && 344 lpbi->biBitCount == This->lpInFormat->biBitCount)) { 345 This->lpOutFormat = This->lpInFormat; 346 This->lpOutBuffer = DIBPTR(This->lpInFormat); 347 return AVIERR_OK; 348 } 349 } 350 351 /* need memory for output format? */ 352 if (This->lpOutFormat == NULL) { 353 This->lpOutFormat = 354 HeapAlloc(GetProcessHeap(), 0, sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)); 355 if (This->lpOutFormat == NULL) { 356 AVIFILE_CloseCompressor(This); 357 return AVIERR_MEMORY; 358 } 359 } 360 361 /* need handle to video compressor */ 362 if (This->hic == NULL) { 363 FOURCC fccHandler; 364 365 if (This->lpInFormat->biCompression == BI_RGB) 366 fccHandler = comptypeDIB; 367 else if (This->lpInFormat->biCompression == BI_RLE8) 368 fccHandler = mmioFOURCC('R','L','E',' '); 369 else 370 fccHandler = sInfo.fccHandler; 371 372 if (lpbi != NULL) { 373 if (lpbi->biWidth == 0) 374 lpbi->biWidth = This->lpInFormat->biWidth; 375 if (lpbi->biHeight == 0) 376 lpbi->biHeight = This->lpInFormat->biHeight; 377 } 378 379 This->hic = ICLocate(ICTYPE_VIDEO, fccHandler, This->lpInFormat, lpbi, ICMODE_DECOMPRESS); 380 if (This->hic == NULL) { 381 AVIFILE_CloseCompressor(This); 382 return AVIERR_NOCOMPRESSOR; 383 } 384 } 385 386 /* output format given? */ 387 if (lpbi != NULL) { 388 /* check the given output format ... */ 389 if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8) 390 lpbi->biClrUsed = 1u << lpbi->biBitCount; 391 392 /* ... and remember it */ 393 memcpy(This->lpOutFormat, lpbi, 394 lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD)); 395 if (lpbi->biBitCount <= 8) 396 ICDecompressGetPalette(This->hic, This->lpInFormat, This->lpOutFormat); 397 398 return AVIERR_OK; 399 } else { 400 if (bBestDisplay) { 401 ICGetDisplayFormat(This->hic, This->lpInFormat, 402 This->lpOutFormat, 0, dx, dy); 403 } else if (ICDecompressGetFormat(This->hic, This->lpInFormat, 404 This->lpOutFormat) < 0) { 405 AVIFILE_CloseCompressor(This); 406 return AVIERR_NOCOMPRESSOR; 407 } 408 409 /* check output format */ 410 if (This->lpOutFormat->biClrUsed == 0 && 411 This->lpOutFormat->biBitCount <= 8) 412 This->lpOutFormat->biClrUsed = 1u << This->lpOutFormat->biBitCount; 413 if (This->lpOutFormat->biSizeImage == 0 && 414 This->lpOutFormat->biCompression == BI_RGB) { 415 This->lpOutFormat->biSizeImage = 416 DIBWIDTHBYTES(*This->lpOutFormat) * This->lpOutFormat->biHeight; 417 } 418 419 if (lpBits == NULL) { 420 DWORD size = This->lpOutFormat->biClrUsed * sizeof(RGBQUAD); 421 422 size += This->lpOutFormat->biSize + This->lpOutFormat->biSizeImage; 423 This->lpOutFormat = HeapReAlloc(GetProcessHeap(), 0, This->lpOutFormat, size); 424 if (This->lpOutFormat == NULL) { 425 AVIFILE_CloseCompressor(This); 426 return AVIERR_MEMORY; 427 } 428 This->lpOutBuffer = DIBPTR(This->lpOutFormat); 429 } else 430 This->lpOutBuffer = lpBits; 431 432 /* for user size was irrelevant */ 433 if (dx == -1) 434 dx = This->lpOutFormat->biWidth; 435 if (dy == -1) 436 dy = This->lpOutFormat->biHeight; 437 438 /* need to resize? */ 439 if (x != 0 || y != 0) { 440 if (dy == This->lpOutFormat->biHeight && 441 dx == This->lpOutFormat->biWidth) 442 This->bResize = FALSE; 443 else 444 This->bResize = TRUE; 445 } 446 447 if (This->bResize) { 448 This->x = x; 449 This->y = y; 450 This->dx = dx; 451 This->dy = dy; 452 453 if (ICDecompressExBegin(This->hic,0,This->lpInFormat,This->lpInBuffer,0, 454 0,This->lpInFormat->biWidth, 455 This->lpInFormat->biHeight,This->lpOutFormat, 456 This->lpOutBuffer, x, y, dx, dy) == ICERR_OK) 457 return AVIERR_OK; 458 } else if (ICDecompressBegin(This->hic, This->lpInFormat, 459 This->lpOutFormat) == ICERR_OK) 460 return AVIERR_OK; 461 462 AVIFILE_CloseCompressor(This); 463 464 return AVIERR_COMPRESSOR; 465 } 466 } 467 468 static const struct IGetFrameVtbl igetframeVtbl = { 469 IGetFrame_fnQueryInterface, 470 IGetFrame_fnAddRef, 471 IGetFrame_fnRelease, 472 IGetFrame_fnGetFrame, 473 IGetFrame_fnBegin, 474 IGetFrame_fnEnd, 475 IGetFrame_fnSetFormat 476 }; 477 478 PGETFRAME AVIFILE_CreateGetFrame(PAVISTREAM pStream) 479 { 480 IGetFrameImpl *pg; 481 482 /* check parameter */ 483 if (pStream == NULL) 484 return NULL; 485 486 pg = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IGetFrameImpl)); 487 if (pg != NULL) { 488 pg->IGetFrame_iface.lpVtbl = &igetframeVtbl; 489 pg->ref = 1; 490 pg->lCurrentFrame = -1; 491 pg->pStream = pStream; 492 IAVIStream_AddRef(pStream); 493 } 494 495 return &pg->IGetFrame_iface; 496 } 497 498 /***********************************************************************/ 499