431 lines
13 KiB
C
431 lines
13 KiB
C
/****************************************************************************
|
|
*
|
|
* AVIBALL.C
|
|
*
|
|
* Sample AVIStream handler for a bouncing ball. This code demonstrates
|
|
* how to write a custom stream handler so an application can deal with
|
|
* your custom file/data/whatever by using the standard AVIStream functions.
|
|
*
|
|
* Copyright (c) 1992 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* You have a royalty-free right to use, modify, reproduce and
|
|
* distribute the Sample Files (and/or any modified version) in
|
|
* any way you find useful, provided that you agree that
|
|
* Microsoft has no warranty obligations or liability for any
|
|
* Sample Application Files which are modified.
|
|
*
|
|
***************************************************************************/
|
|
|
|
#include <windows.h>
|
|
#include <windowsx.h>
|
|
#include <win32.h>
|
|
#include <vfw.h>
|
|
#include <coguid.h>
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// default parameters
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
#define DEFAULT_WIDTH 240
|
|
#define DEFAULT_HEIGHT 120
|
|
#define DEFAULT_LENGTH 100
|
|
#define DEFAULT_SIZE 6
|
|
#define DEFAULT_COLOR RGB(255,0,0)
|
|
#define XSPEED 7
|
|
#define YSPEED 5
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// useful macros
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
#define ALIGNULONG(i) ((i+3)&(~3)) /* ULONG aligned ! */
|
|
#define WIDTHBYTES(i) ((unsigned)((i+31)&(~31))/8) /* ULONG aligned ! */
|
|
#define DIBWIDTHBYTES(bi) (int)WIDTHBYTES((int)(bi).biWidth * (int)(bi).biBitCount)
|
|
#define DIBPTR(lpbi) ((LPBYTE)(lpbi) + \
|
|
(int)(lpbi)->biSize + \
|
|
(int)(lpbi)->biClrUsed * sizeof(RGBQUAD) )
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// custom video stream instance structure
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
typedef struct {
|
|
|
|
//
|
|
// The Vtbl must come first
|
|
//
|
|
IAVIStreamVtbl FAR * lpvtbl;
|
|
|
|
//
|
|
// private ball instance data
|
|
//
|
|
ULONG ulRefCount;
|
|
|
|
DWORD fccType; // is this audio/video
|
|
|
|
int width; // size in pixels of each frame
|
|
int height;
|
|
int length; // length in frames of the pretend AVI movie
|
|
int size;
|
|
COLORREF color; // ball color
|
|
|
|
} AVIBALL, FAR * PAVIBALL;
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// custom stream methods
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT STDMETHODCALLTYPE AVIBallQueryInterface(PAVISTREAM ps, REFIID riid, LPVOID FAR* ppvObj);
|
|
HRESULT STDMETHODCALLTYPE AVIBallCreate (PAVISTREAM ps, LONG lParam1, LONG lParam2);
|
|
ULONG STDMETHODCALLTYPE AVIBallAddRef (PAVISTREAM ps);
|
|
ULONG STDMETHODCALLTYPE AVIBallRelease (PAVISTREAM ps);
|
|
HRESULT STDMETHODCALLTYPE AVIBallInfo (PAVISTREAM ps, AVISTREAMINFO FAR * psi, LONG lSize);
|
|
LONG STDMETHODCALLTYPE AVIBallFindSample (PAVISTREAM ps, LONG lPos, LONG lFlags);
|
|
HRESULT STDMETHODCALLTYPE AVIBallReadFormat (PAVISTREAM ps, LONG lPos, LPVOID lpFormat, LONG FAR *lpcbFormat);
|
|
HRESULT STDMETHODCALLTYPE AVIBallSetFormat (PAVISTREAM ps, LONG lPos, LPVOID lpFormat, LONG cbFormat);
|
|
HRESULT STDMETHODCALLTYPE AVIBallRead (PAVISTREAM ps, LONG lStart, LONG lSamples, LPVOID lpBuffer, LONG cbBuffer, LONG FAR * plBytes,LONG FAR * plSamples);
|
|
HRESULT STDMETHODCALLTYPE AVIBallWrite (PAVISTREAM ps, LONG lStart, LONG lSamples, LPVOID lpBuffer, LONG cbBuffer, DWORD dwFlags, LONG FAR *plSampWritten, LONG FAR *plBytesWritten);
|
|
HRESULT STDMETHODCALLTYPE AVIBallDelete (PAVISTREAM ps, LONG lStart, LONG lSamples);
|
|
HRESULT STDMETHODCALLTYPE AVIBallReadData (PAVISTREAM ps, DWORD fcc, LPVOID lp,LONG FAR *lpcb);
|
|
HRESULT STDMETHODCALLTYPE AVIBallWriteData (PAVISTREAM ps, DWORD fcc, LPVOID lp,LONG cb);
|
|
|
|
IAVIStreamVtbl AVIBallHandler = {
|
|
AVIBallQueryInterface,
|
|
AVIBallAddRef,
|
|
AVIBallRelease,
|
|
AVIBallCreate,
|
|
AVIBallInfo,
|
|
AVIBallFindSample,
|
|
AVIBallReadFormat,
|
|
AVIBallSetFormat,
|
|
AVIBallRead,
|
|
AVIBallWrite,
|
|
AVIBallDelete,
|
|
AVIBallReadData,
|
|
AVIBallWriteData
|
|
};
|
|
|
|
|
|
//
|
|
// This is the function an application would call to create a PAVISTREAM to
|
|
// reference the ball. Then the standard AVIStream function calls can be
|
|
// used to work with this stream.
|
|
//
|
|
PAVISTREAM FAR PASCAL NewBall(void)
|
|
{
|
|
PAVIBALL pball;
|
|
|
|
//
|
|
// Create a pointer to our private structure which will act as our
|
|
// PAVISTREAM
|
|
//
|
|
pball = (PAVIBALL) GlobalAllocPtr(GHND, sizeof(AVIBALL));
|
|
|
|
if (!pball)
|
|
return 0;
|
|
|
|
//
|
|
// Fill the function table
|
|
//
|
|
pball->lpvtbl = &AVIBallHandler;
|
|
|
|
//
|
|
// Call our own create code to create a new instance (calls AVIBallCreate)
|
|
// For now, don't use any lParams.
|
|
//
|
|
pball->lpvtbl->Create((PAVISTREAM) pball, 0, 0);
|
|
|
|
return (PAVISTREAM) pball;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// This function is called to initialize an instance of the bouncing ball.
|
|
//
|
|
// When called, we look at the information possibly passed in <lParam1>,
|
|
// if any, and use it to determine the length of movie they want. (Not
|
|
// supported by NewBall right now, but it could be).
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
HRESULT STDMETHODCALLTYPE AVIBallCreate(PAVISTREAM ps, LONG lParam1, LONG lParam2)
|
|
{
|
|
PAVIBALL pball = (PAVIBALL) ps;
|
|
|
|
//
|
|
// what type of data are we? (audio/video/other stream)
|
|
//
|
|
pball->fccType = streamtypeVIDEO;
|
|
|
|
//
|
|
// We define lParam1 as being the length of movie they want us to pretend
|
|
// to be.
|
|
//
|
|
if (lParam1)
|
|
pball->length = (int) lParam1;
|
|
else
|
|
pball->length = DEFAULT_LENGTH;
|
|
|
|
switch (pball->fccType) {
|
|
|
|
case streamtypeVIDEO:
|
|
pball->color = DEFAULT_COLOR;
|
|
pball->width = DEFAULT_WIDTH;
|
|
pball->height = DEFAULT_HEIGHT;
|
|
pball->size = DEFAULT_SIZE;
|
|
pball->ulRefCount = 1; // note that we are opened once
|
|
return AVIERR_OK; // success
|
|
|
|
case streamtypeAUDIO:
|
|
return ResultFromScode(AVIERR_UNSUPPORTED); // we don't do audio
|
|
|
|
default:
|
|
return ResultFromScode(AVIERR_UNSUPPORTED); // or anything else
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Increment our reference count
|
|
//
|
|
ULONG STDMETHODCALLTYPE AVIBallAddRef(PAVISTREAM ps)
|
|
{
|
|
PAVIBALL pball = (PAVIBALL) ps;
|
|
return (++pball->ulRefCount);
|
|
}
|
|
|
|
|
|
//
|
|
// Decrement our reference count
|
|
//
|
|
ULONG STDMETHODCALLTYPE AVIBallRelease(PAVISTREAM ps)
|
|
{
|
|
PAVIBALL pball = (PAVIBALL) ps;
|
|
if (--pball->ulRefCount)
|
|
return pball->ulRefCount;
|
|
|
|
// Free any data we're keeping around - like our private structure
|
|
GlobalFreePtr(pball);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//
|
|
// Fills an AVISTREAMINFO structure
|
|
//
|
|
HRESULT STDMETHODCALLTYPE AVIBallInfo(PAVISTREAM ps, AVISTREAMINFO FAR * psi, LONG lSize)
|
|
{
|
|
PAVIBALL pball = (PAVIBALL) ps;
|
|
|
|
if (lSize < sizeof(AVISTREAMINFO))
|
|
return ResultFromScode(AVIERR_BUFFERTOOSMALL);
|
|
|
|
_fmemset(psi, 0, lSize);
|
|
|
|
// Fill out a stream header with information about us.
|
|
psi->fccType = pball->fccType;
|
|
psi->fccHandler = mmioFOURCC('B','a','l','l');
|
|
psi->dwScale = 1;
|
|
psi->dwRate = 15;
|
|
psi->dwLength = pball->length;
|
|
psi->dwSuggestedBufferSize = pball->height * ALIGNULONG(pball->width);
|
|
psi->rcFrame.right = pball->width;
|
|
psi->rcFrame.bottom = pball->height;
|
|
lstrcpy(psi->szName, TEXT("Bouncing ball video"));
|
|
|
|
return AVIERR_OK;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// AVIBallReadFormat: needs to return the format of our data.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
HRESULT STDMETHODCALLTYPE AVIBallReadFormat (PAVISTREAM ps, LONG lPos,LPVOID lpFormat,LONG FAR *lpcbFormat)
|
|
{
|
|
PAVIBALL pball = (PAVIBALL) ps;
|
|
LPBITMAPINFO lpbi = (LPBITMAPINFO) lpFormat;
|
|
|
|
if (lpFormat == NULL || *lpcbFormat == 0) {
|
|
*lpcbFormat = sizeof(BITMAPINFOHEADER) + 2 * sizeof(RGBQUAD);
|
|
return AVIERR_OK;
|
|
}
|
|
|
|
if (*lpcbFormat < sizeof(BITMAPINFOHEADER) + 2 * sizeof(RGBQUAD))
|
|
return ResultFromScode(AVIERR_BUFFERTOOSMALL);
|
|
|
|
// This is a relatively odd example: we build up our
|
|
// format from scratch every time.
|
|
|
|
lpbi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
lpbi->bmiHeader.biCompression = BI_RGB;
|
|
lpbi->bmiHeader.biWidth = pball->width;
|
|
lpbi->bmiHeader.biHeight = pball->height;
|
|
lpbi->bmiHeader.biBitCount = 8;
|
|
lpbi->bmiHeader.biPlanes = 1;
|
|
lpbi->bmiHeader.biClrUsed = 2;
|
|
lpbi->bmiHeader.biSizeImage = pball->height * DIBWIDTHBYTES(lpbi->bmiHeader);
|
|
|
|
lpbi->bmiColors[0].rgbRed = 0;
|
|
lpbi->bmiColors[0].rgbGreen = 0;
|
|
lpbi->bmiColors[0].rgbBlue = 0;
|
|
lpbi->bmiColors[1].rgbRed = GetRValue(pball->color);
|
|
lpbi->bmiColors[1].rgbGreen = GetGValue(pball->color);
|
|
lpbi->bmiColors[1].rgbBlue = GetBValue(pball->color);
|
|
|
|
*lpcbFormat = sizeof(BITMAPINFOHEADER) + 2 * sizeof(RGBQUAD);
|
|
|
|
return AVIERR_OK;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// AVIBallRead: needs to return the data for a particular frame.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
HRESULT STDMETHODCALLTYPE AVIBallRead (PAVISTREAM ps, LONG lStart,LONG lSamples,LPVOID lpBuffer,LONG cbBuffer,LONG FAR * plBytes,LONG FAR * plSamples)
|
|
{
|
|
PAVIBALL pball = (PAVIBALL) ps;
|
|
LONG lSize = pball->height * ALIGNULONG(pball->width); // size of frame
|
|
// in bytes
|
|
int x, y;
|
|
BYTE _huge *hp = lpBuffer;
|
|
int xPos, yPos;
|
|
|
|
// Reject out of range values
|
|
if (lStart < 0 || lStart >= pball->length)
|
|
return ResultFromScode(AVIERR_BADPARAM);
|
|
|
|
// Did they just want to know the size of our data?
|
|
if (lpBuffer == NULL || cbBuffer == 0)
|
|
goto exit;
|
|
|
|
// Will our frame fit in the buffer passed?
|
|
if (lSize > cbBuffer)
|
|
return ResultFromScode(AVIERR_BUFFERTOOSMALL);
|
|
|
|
// Figure out the position of the ball.
|
|
// It just bounces back and forth.
|
|
|
|
xPos = 5 + XSPEED * (int) lStart; // x = x0 + vt
|
|
xPos = xPos % ((pball->width - pball->size) * 2); // limit to 2xwidth
|
|
if (xPos > (pball->width - pball->size)) // reflect if
|
|
xPos = 2 * (pball->width - pball->size) - xPos; // needed
|
|
|
|
yPos = 5 + YSPEED * (int) lStart;
|
|
yPos = yPos % ((pball->height - pball->size) * 2);
|
|
if (yPos > (pball->height - pball->size))
|
|
yPos = 2 * (pball->height - pball->size) - yPos;
|
|
|
|
//
|
|
// Build a DIB from scratch by writing in 1's where the ball is, 0's
|
|
// where it isn't.
|
|
//
|
|
// Notice that we just build it in the buffer we've been passed.
|
|
//
|
|
// This is pretty ugly, I have to admit.
|
|
//
|
|
for (y = 0; y < pball->height; y++)
|
|
{
|
|
if (y >= yPos && y < yPos + pball->size)
|
|
{
|
|
for (x = 0; x < pball->width; x++)
|
|
{
|
|
*hp++ = (BYTE) ((x >= xPos && x < xPos + pball->size) ? 1 : 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (x = 0; x < pball->width; x++)
|
|
{
|
|
*hp++ = 0;
|
|
}
|
|
}
|
|
|
|
hp += pball->width - ALIGNULONG(pball->width);
|
|
}
|
|
|
|
exit:
|
|
// We always return exactly one frame
|
|
if (plSamples)
|
|
*plSamples = 1;
|
|
|
|
// Return the size of our frame
|
|
if (plBytes)
|
|
*plBytes = lSize;
|
|
|
|
return AVIERR_OK;
|
|
}
|
|
|
|
|
|
HRESULT STDMETHODCALLTYPE AVIBallQueryInterface(PAVISTREAM ps, REFIID riid, LPVOID FAR* ppvObj)
|
|
{
|
|
PAVIBALL pball = (PAVIBALL) ps;
|
|
|
|
// We support the Unknown interface (everybody does) and our Stream
|
|
// interface.
|
|
|
|
if (_fmemcmp(riid, &IID_IUnknown, sizeof(GUID)) == 0)
|
|
*ppvObj = (LPVOID)pball;
|
|
|
|
else if (_fmemcmp(riid, &IID_IAVIStream, sizeof(GUID)) == 0)
|
|
*ppvObj = (LPVOID)pball;
|
|
|
|
else {
|
|
*ppvObj = NULL;
|
|
return ResultFromScode(E_NOINTERFACE);
|
|
}
|
|
|
|
AVIBallAddRef(ps);
|
|
|
|
return AVIERR_OK;
|
|
}
|
|
|
|
LONG STDMETHODCALLTYPE AVIBallFindSample (PAVISTREAM ps, LONG lPos, LONG lFlags)
|
|
{
|
|
// The only format change is frame 0
|
|
if ((lFlags & FIND_TYPE) == FIND_FORMAT) {
|
|
if ((lFlags & FIND_DIR) == FIND_NEXT && lPos > 0)
|
|
return -1; // no more format changes
|
|
else
|
|
return 0;
|
|
|
|
// FIND_KEY and FIND_ANY always return the same position because
|
|
// every frame is non-empty and a key frame
|
|
} else
|
|
return lPos;
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE AVIBallReadData (PAVISTREAM ps, DWORD fcc, LPVOID lp, LONG FAR *lpcb)
|
|
{
|
|
return ResultFromScode(AVIERR_UNSUPPORTED);
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE AVIBallSetFormat (PAVISTREAM ps, LONG lPos, LPVOID lpFormat, LONG cbFormat)
|
|
{
|
|
return ResultFromScode(AVIERR_UNSUPPORTED);
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE AVIBallWriteData (PAVISTREAM ps, DWORD fcc, LPVOID lp, LONG cb)
|
|
{
|
|
return ResultFromScode(AVIERR_UNSUPPORTED);
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE AVIBallWrite (PAVISTREAM ps, LONG lStart, LONG lSamples, LPVOID lpBuffer, LONG cbBuffer, DWORD dwFlags, LONG FAR *plSampWritten, LONG FAR *plBytesWritten)
|
|
{
|
|
return ResultFromScode(AVIERR_UNSUPPORTED);
|
|
}
|
|
|
|
HRESULT STDMETHODCALLTYPE AVIBallDelete (PAVISTREAM ps, LONG lStart, LONG lSamples)
|
|
{
|
|
return ResultFromScode(AVIERR_UNSUPPORTED);
|
|
}
|