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); 
}