getframe.c 14.1 KB
Newer Older
1
/*
2
 * Copyright 2002-2003 Michael Günnewig
3 4 5 6 7 8 9 10 11 12 13 14 15
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
#include "vfw.h"

#include "avifile_private.h"

#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(avifile);

#ifndef DIBPTR
#define DIBPTR(lp)      ((LPBYTE)(lp) + (lp)->biSize + \
                         (lp)->biClrUsed * sizeof(RGBQUAD))
#endif

/***********************************************************************/

typedef struct _IGetFrameImpl {
  /* IUnknown stuff */
42
  IGetFrame          IGetFrame_iface;
43
  LONG               ref;
44 45 46 47 48 49

  /* IGetFrame stuff */
  BOOL               bFixedStream;
  PAVISTREAM         pStream;

  LPVOID             lpInBuffer;
50
  LONG               cbInBuffer;
51
  LPBITMAPINFOHEADER lpInFormat;
52
  LONG               cbInFormat;
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71

  LONG               lCurrentFrame;
  LPBITMAPINFOHEADER lpOutFormat;
  LPVOID             lpOutBuffer;

  HIC                hic;
  BOOL               bResize;
  DWORD              x;
  DWORD              y;
  DWORD              dx;
  DWORD              dy;

  BOOL               bFormatChanges;
  DWORD              dwFormatChangeCount;
  DWORD              dwEditCount;
} IGetFrameImpl;

/***********************************************************************/

72 73 74 75 76
static inline IGetFrameImpl *impl_from_IGetFrame(IGetFrame *iface)
{
  return CONTAINING_RECORD(iface, IGetFrameImpl, IGetFrame_iface);
}

77 78
static void AVIFILE_CloseCompressor(IGetFrameImpl *This)
{
79
  if (This->lpInFormat != This->lpOutFormat) {
80
    HeapFree(GetProcessHeap(), 0, This->lpOutFormat);
81 82
    This->lpOutFormat = NULL;
  }
83 84
  HeapFree(GetProcessHeap(), 0, This->lpInFormat);
  This->lpInFormat = NULL;
85
  if (This->hic != NULL) {
86 87 88 89 90
    if (This->bResize)
      ICDecompressExEnd(This->hic);
    else
      ICDecompressEnd(This->hic);
    ICClose(This->hic);
91
    This->hic = NULL;
92 93 94 95 96 97
  }
}

static HRESULT WINAPI IGetFrame_fnQueryInterface(IGetFrame *iface,
						 REFIID refiid, LPVOID *obj)
{
98
  IGetFrameImpl *This = impl_from_IGetFrame(iface);
99 100 101 102 103 104

  TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj);

  if (IsEqualGUID(&IID_IUnknown, refiid) ||
      IsEqualGUID(&IID_IGetFrame, refiid)) {
    *obj = iface;
105
    IGetFrame_AddRef(iface);
106 107 108 109 110 111 112 113
    return S_OK;
  }

  return OLE_E_ENUM_NOMORE;
}

static ULONG   WINAPI IGetFrame_fnAddRef(IGetFrame *iface)
{
114
  IGetFrameImpl *This = impl_from_IGetFrame(iface);
115
  ULONG ref = InterlockedIncrement(&This->ref);
116 117 118

  TRACE("(%p)\n", iface);

119
  return ref;
120 121 122 123
}

static ULONG   WINAPI IGetFrame_fnRelease(IGetFrame *iface)
{
124
  IGetFrameImpl *This = impl_from_IGetFrame(iface);
125
  ULONG ref = InterlockedDecrement(&This->ref);
126 127 128

  TRACE("(%p)\n", iface);

129
  if (!ref) {
130 131
    AVIFILE_CloseCompressor(This);
    if (This->pStream != NULL) {
132
      IAVIStream_Release(This->pStream);
133 134 135
      This->pStream = NULL;
    }

136
    HeapFree(GetProcessHeap(), 0, iface);
137 138
  }

139
  return ref;
140 141 142 143
}

static LPVOID  WINAPI IGetFrame_fnGetFrame(IGetFrame *iface, LONG lPos)
{
144
  IGetFrameImpl *This = impl_from_IGetFrame(iface);
145 146 147 148

  LONG readBytes;
  LONG readSamples;

149
  TRACE("(%p,%d)\n", iface, lPos);
150

151 152 153 154
  /* We don't want negative start values! -- marks invalid buffer content */
  if (lPos < 0)
    return NULL;

155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
  /* check state */
  if (This->pStream == NULL)
    return NULL;
  if (This->lpInFormat == NULL)
    return NULL;

  /* Could stream have changed? */
  if (! This->bFixedStream) {
    AVISTREAMINFOW sInfo;

    IAVIStream_Info(This->pStream, &sInfo, sizeof(sInfo));

    if (sInfo.dwEditCount != This->dwEditCount) {
      This->dwEditCount   = sInfo.dwEditCount;
      This->lCurrentFrame = -1;
    }

    if (sInfo.dwFormatChangeCount != This->dwFormatChangeCount) {
      /* stream has changed */
      if (This->lpOutFormat != NULL) {
	BITMAPINFOHEADER bi;

177
	bi = *This->lpOutFormat;
178 179 180 181 182 183 184 185 186 187 188 189
	AVIFILE_CloseCompressor(This);

	if (FAILED(IGetFrame_SetFormat(iface, &bi, NULL, 0, 0, -1, -1))) {
	  if (FAILED(IGetFrame_SetFormat(iface, NULL, NULL, 0, 0, -1, -1)))
	    return NULL;
	}
      } else if (FAILED(IGetFrame_SetFormat(iface, NULL, NULL, 0, 0, -1, -1)))
	return NULL;
    }
  }

  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 199
      /* new format for this frame? */
      if (This->bFormatChanges) {
200 201
	IAVIStream_ReadFormat(This->pStream, lNext,
			      This->lpInFormat, &This->cbInFormat);
202 203 204 205 206 207 208 209 210 211 212 213 214
	if (This->lpOutFormat != NULL) {
	  if (This->lpOutFormat->biBitCount <= 8)
	    ICDecompressGetPalette(This->hic, This->lpInFormat,
				   This->lpOutFormat);
	}
      }

      /* read input frame */
      while (FAILED(AVIStreamRead(This->pStream, lNext, 1, This->lpInBuffer,
				  This->cbInBuffer, &readBytes, &readSamples))) {
	/* not enough memory for input buffer? */
	readBytes = 0;
	if (FAILED(AVIStreamSampleSize(This->pStream, lNext, &readBytes)))
215 216
	  return NULL; /* bad thing, but bad things will happen */
	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 223
	if (This->cbInBuffer >= readBytes)
	  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 229 230
	This->lpInBuffer = (BYTE*)This->lpInFormat + This->cbInFormat;
      }

231 232
      if (readSamples != 1) {
	ERR(": no frames read\n");
233
	return NULL;
234
      }
235 236 237 238
      if (readBytes != 0) {
	This->lpInFormat->biSizeImage = readBytes;

	/* nothing to decompress? */
239
	if (This->hic == NULL) {
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
	  This->lCurrentFrame = lPos;
	  return This->lpInFormat;
	}

	if (This->bResize) {
	  ICDecompressEx(This->hic,0,This->lpInFormat,This->lpInBuffer,0,0,
			 This->lpInFormat->biWidth,This->lpInFormat->biHeight,
			 This->lpOutFormat,This->lpOutBuffer,This->x,This->y,
			 This->dx,This->dy);
	} else {
	  ICDecompress(This->hic, 0, This->lpInFormat, This->lpInBuffer,
		       This->lpOutFormat, This->lpOutBuffer);
	}
      }
    } /* for (lNext < lPos) */
  } /* if (This->lCurrentFrame != lPos) */

257
  return (This->hic == NULL ? This->lpInFormat : This->lpOutFormat);
258 259 260 261 262
}

static HRESULT WINAPI IGetFrame_fnBegin(IGetFrame *iface, LONG lStart,
					LONG lEnd, LONG lRate)
{
263
  IGetFrameImpl *This = impl_from_IGetFrame(iface);
264

265
  TRACE("(%p,%d,%d,%d)\n", iface, lStart, lEnd, lRate);
266 267 268 269 270 271 272 273

  This->bFixedStream = TRUE;

  return (IGetFrame_GetFrame(iface, lStart) ? AVIERR_OK : AVIERR_ERROR);
}

static HRESULT WINAPI IGetFrame_fnEnd(IGetFrame *iface)
{
274
  IGetFrameImpl *This = impl_from_IGetFrame(iface);
275 276 277 278 279 280 281 282 283 284 285 286 287

  TRACE("(%p)\n", iface);

  This->bFixedStream = FALSE;

  return AVIERR_OK;
}

static HRESULT WINAPI IGetFrame_fnSetFormat(IGetFrame *iface,
					    LPBITMAPINFOHEADER lpbiWanted,
					    LPVOID lpBits, INT x, INT y,
					    INT dx, INT dy)
{
288
  IGetFrameImpl *This = impl_from_IGetFrame(iface);
289 290 291 292 293 294 295 296 297 298 299

  AVISTREAMINFOW     sInfo;
  LPBITMAPINFOHEADER lpbi         = lpbiWanted;
  BOOL               bBestDisplay = FALSE;

  TRACE("(%p,%p,%p,%d,%d,%d,%d)\n", iface, lpbiWanted, lpBits,
	x, y, dx, dy);

  if (This->pStream == NULL)
    return AVIERR_ERROR;

300
  if (lpbiWanted == (LPBITMAPINFOHEADER)AVIGETFRAMEF_BESTDISPLAYFMT) {
301 302 303 304 305 306 307 308
    lpbi = NULL;
    bBestDisplay = TRUE;
  }

  IAVIStream_Info(This->pStream, &sInfo, sizeof(sInfo));
  if (sInfo.fccType != streamtypeVIDEO)
    return AVIERR_UNSUPPORTED;

309
  This->bFormatChanges = (sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) != 0;
310 311 312 313 314 315
  This->dwFormatChangeCount = sInfo.dwFormatChangeCount;
  This->dwEditCount         = sInfo.dwEditCount;
  This->lCurrentFrame       = -1;

  /* get input format from stream */
  if (This->lpInFormat == NULL) {
316 317
    HRESULT hr;

318
    This->cbInBuffer = (LONG)sInfo.dwSuggestedBufferSize;
319 320 321
    if (This->cbInBuffer == 0)
      This->cbInBuffer = 1024;

322 323
    IAVIStream_ReadFormat(This->pStream, sInfo.dwStart,
			  NULL, &This->cbInFormat);
324

325
    This->lpInFormat = HeapAlloc(GetProcessHeap(), 0, This->cbInFormat + This->cbInBuffer);
326 327 328 329 330
    if (This->lpInFormat == NULL) {
      AVIFILE_CloseCompressor(This);
      return AVIERR_MEMORY;
    }

331
    hr = IAVIStream_ReadFormat(This->pStream, sInfo.dwStart, This->lpInFormat, &This->cbInFormat);
332 333 334 335
    if (FAILED(hr)) {
      AVIFILE_CloseCompressor(This);
      return hr;
    }
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364

    This->lpInBuffer = ((LPBYTE)This->lpInFormat) + This->cbInFormat;
  }

  /* check input format */
  if (This->lpInFormat->biClrUsed == 0 && This->lpInFormat->biBitCount <= 8)
    This->lpInFormat->biClrUsed = 1u << This->lpInFormat->biBitCount;
  if (This->lpInFormat->biSizeImage == 0 &&
      This->lpInFormat->biCompression == BI_RGB) {
    This->lpInFormat->biSizeImage =
      DIBWIDTHBYTES(*This->lpInFormat) * This->lpInFormat->biHeight;
  }

  /* only to pass through? */
  if (This->lpInFormat->biCompression == BI_RGB && lpBits == NULL) {
    if (lpbi == NULL || 
	(lpbi->biCompression == BI_RGB &&
	 lpbi->biWidth == This->lpInFormat->biWidth &&
	 lpbi->biHeight == This->lpInFormat->biHeight &&
	 lpbi->biBitCount == This->lpInFormat->biBitCount)) {
      This->lpOutFormat = This->lpInFormat;
      This->lpOutBuffer = DIBPTR(This->lpInFormat);
      return AVIERR_OK;
    }
  }

  /* need memory for output format? */
  if (This->lpOutFormat == NULL) {
    This->lpOutFormat =
365
      HeapAlloc(GetProcessHeap(), 0, sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
366 367 368 369 370 371 372
    if (This->lpOutFormat == NULL) {
      AVIFILE_CloseCompressor(This);
      return AVIERR_MEMORY;
    }
  }

  /* need handle to video compressor */
373
  if (This->hic == NULL) {
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
    FOURCC fccHandler;

    if (This->lpInFormat->biCompression == BI_RGB)
      fccHandler = comptypeDIB;
    else if (This->lpInFormat->biCompression == BI_RLE8)
      fccHandler = mmioFOURCC('R','L','E',' ');
    else
      fccHandler = sInfo.fccHandler;

    if (lpbi != NULL) {
      if (lpbi->biWidth == 0)
	lpbi->biWidth = This->lpInFormat->biWidth;
      if (lpbi->biHeight == 0)
	lpbi->biHeight = This->lpInFormat->biHeight;
    }

    This->hic = ICLocate(ICTYPE_VIDEO, fccHandler, This->lpInFormat, lpbi, ICMODE_DECOMPRESS);
391
    if (This->hic == NULL) {
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
      AVIFILE_CloseCompressor(This);
      return AVIERR_NOCOMPRESSOR;
    }
  }

  /* output format given? */
  if (lpbi != NULL) {
    /* check the given output format ... */
    if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
      lpbi->biClrUsed = 1u << lpbi->biBitCount;

    /* ... and remember it */
    memcpy(This->lpOutFormat, lpbi,
	   lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD));
    if (lpbi->biBitCount <= 8)
      ICDecompressGetPalette(This->hic, This->lpInFormat, This->lpOutFormat);
  } else {
    if (bBestDisplay) {
      ICGetDisplayFormat(This->hic, This->lpInFormat,
			 This->lpOutFormat, 0, dx, dy);
    } else if (ICDecompressGetFormat(This->hic, This->lpInFormat,
				     This->lpOutFormat) < 0) {
      AVIFILE_CloseCompressor(This);
      return AVIERR_NOCOMPRESSOR;
    }
417
  }
418

419 420 421 422 423 424 425 426 427
  /* check output format */
  if (This->lpOutFormat->biClrUsed == 0 &&
      This->lpOutFormat->biBitCount <= 8)
    This->lpOutFormat->biClrUsed = 1u << This->lpOutFormat->biBitCount;
  if (This->lpOutFormat->biSizeImage == 0 &&
      This->lpOutFormat->biCompression == BI_RGB) {
    This->lpOutFormat->biSizeImage =
      DIBWIDTHBYTES(*This->lpOutFormat) * This->lpOutFormat->biHeight;
  }
428

429 430
  if (lpBits == NULL) {
    DWORD size = This->lpOutFormat->biClrUsed * sizeof(RGBQUAD);
431

432 433 434 435 436
    size += This->lpOutFormat->biSize + This->lpOutFormat->biSizeImage;
    This->lpOutFormat = HeapReAlloc(GetProcessHeap(), 0, This->lpOutFormat, size);
    if (This->lpOutFormat == NULL) {
      AVIFILE_CloseCompressor(This);
      return AVIERR_MEMORY;
437
    }
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455
    This->lpOutBuffer = DIBPTR(This->lpOutFormat);
  } else
    This->lpOutBuffer = lpBits;

  /* for user size was irrelevant */
  if (dx == -1)
    dx = This->lpOutFormat->biWidth;
  if (dy == -1)
    dy = This->lpOutFormat->biHeight;

  /* need to resize? */
  if (x != 0 || y != 0) {
    if (dy == This->lpOutFormat->biHeight &&
        dx == This->lpOutFormat->biWidth)
      This->bResize = FALSE;
    else
      This->bResize = TRUE;
  }
456

457 458 459 460 461 462 463 464 465 466
  if (This->bResize) {
    This->x  = x;
    This->y  = y;
    This->dx = dx;
    This->dy = dy;

    if (ICDecompressExBegin(This->hic,0,This->lpInFormat,This->lpInBuffer,0,
                            0,This->lpInFormat->biWidth,
                            This->lpInFormat->biHeight,This->lpOutFormat,
                            This->lpOutBuffer, x, y, dx, dy) == ICERR_OK)
467
      return AVIERR_OK;
468 469 470
  } else if (ICDecompressBegin(This->hic, This->lpInFormat,
                               This->lpOutFormat) == ICERR_OK)
    return AVIERR_OK;
471

472
  AVIFILE_CloseCompressor(This);
473

474
  return AVIERR_COMPRESSOR;
475 476
}

477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
static const struct IGetFrameVtbl igetframeVtbl = {
  IGetFrame_fnQueryInterface,
  IGetFrame_fnAddRef,
  IGetFrame_fnRelease,
  IGetFrame_fnGetFrame,
  IGetFrame_fnBegin,
  IGetFrame_fnEnd,
  IGetFrame_fnSetFormat
};

PGETFRAME AVIFILE_CreateGetFrame(PAVISTREAM pStream)
{
  IGetFrameImpl *pg;

  /* check parameter */
  if (pStream == NULL)
    return NULL;

  pg = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IGetFrameImpl));
  if (pg != NULL) {
    pg->IGetFrame_iface.lpVtbl = &igetframeVtbl;
    pg->ref           = 1;
    pg->lCurrentFrame = -1;
    pg->pStream       = pStream;
    IAVIStream_AddRef(pStream);
  }

504
  return &pg->IGetFrame_iface;
505 506
}

507
/***********************************************************************/