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