AVIBALL.C

/************************************************************************** 
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
* KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
* PURPOSE.
*
* Copyright 1993 - 1998 Microsoft Corporation. All Rights Reserved.
*
**************************************************************************/
/****************************************************************************
*
* 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.
*
***************************************************************************/

#define INC_OLE2
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <vfw.h>

#include "aviball.h"

///////////////////////////////////////////////////////////////////////////
//
// silly 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 XSPEED7
#define YSPEED5

///////////////////////////////////////////////////////////////////////////
//
// 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 * lpvtbl;

//
// private ball instance data
//
ULONGulRefCount;

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, * PAVIBALL;

///////////////////////////////////////////////////////////////////////////
//
// custom stream methods
//
///////////////////////////////////////////////////////////////////////////

HRESULT STDMETHODCALLTYPE AVIBallQueryInterface(PAVISTREAM ps, REFIID riid, LPVOID * 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, AVISTREAMINFOW * psi, LONG lSize);
LONG STDMETHODCALLTYPE AVIBallFindSample (PAVISTREAM ps, LONG lPos, LONG lFlags);
HRESULT STDMETHODCALLTYPE AVIBallReadFormat (PAVISTREAM ps, LONG lPos, LPVOID lpFormat, LONG *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 * plBytes,LONG * plSamples);
HRESULT STDMETHODCALLTYPE AVIBallWrite (PAVISTREAM ps, LONG lStart, LONG lSamples, LPVOID lpBuffer, LONG cbBuffer, DWORD dwFlags, LONG *plSampWritten, LONG *plBytesWritten);
HRESULT STDMETHODCALLTYPE AVIBallDelete (PAVISTREAM ps, LONG lStart, LONG lSamples);
HRESULT STDMETHODCALLTYPE AVIBallReadData (PAVISTREAM ps, DWORD fcc, LPVOID lp,LONG *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 WINAPI 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, AVISTREAMINFOW * psi, LONG lSize)
{
PAVIBALL pball = (PAVIBALL) ps;

if (lSize < sizeof(AVISTREAMINFO))
return ResultFromScode(AVIERR_BUFFERTOOSMALL);

_fmemset(psi, 0, (int)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;
CopyMemory((PVOID)psi->szName,
(PVOID)L"Bouncing ball video",
sizeof(L"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 *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 silly 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 * plBytes,LONG * plSamples)
{
PAVIBALL pball = (PAVIBALL) ps;
LONG lSize = pball->height * ALIGNULONG(pball->width); // size of frame
// in bytes
int x, y;
HPSTR 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 * 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 *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 *plSampWritten, LONG *plBytesWritten)
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}

HRESULT STDMETHODCALLTYPE AVIBallDelete (PAVISTREAM ps, LONG lStart, LONG lSamples)
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}