//==========================================================================;
//
// 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 (c) 1992 - 1997 Microsoft Corporation. All Rights Reserved.
//
//--------------------------------------------------------------------------;
#include <streams.h>
#include <olectl.h>
#include <initguid.h>
#include "balluids.h"
#include "ball.h"
#include "fball.h"
// Setup data
const AMOVIESETUP_MEDIATYPE sudOpPinTypes =
{
&MEDIATYPE_Video, // Major type
&MEDIASUBTYPE_NULL // Minor type
};
const AMOVIESETUP_PIN sudOpPin =
{
L"Output", // Pin string name
FALSE, // Is it rendered
TRUE, // Is it an output
FALSE, // Can we have none
FALSE, // Can we have many
&CLSID_NULL, // Connects to filter
NULL, // Connects to pin
1, // Number of types
&sudOpPinTypes }; // Pin details
const AMOVIESETUP_FILTER sudBallax =
{
&CLSID_BouncingBall, // Filter CLSID
L"Bouncing Ball", // String name
MERIT_DO_NOT_USE, // Filter merit
1, // Number pins
&sudOpPin // Pin details
};
// COM global table of objects in this dll
CFactoryTemplate g_Templates[] = {
{ L"Bouncing Ball"
, &CLSID_BouncingBall
, CBouncingBall::CreateInstance
, NULL
, &sudBallax }
};
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
//
// DllRegisterServer
//
// Exported entry points for registration and unregistration
//
STDAPI DllRegisterServer()
{
return AMovieDllRegisterServer2( TRUE );
} // DllRegisterServer
//
// DllUnregisterServer
//
STDAPI DllUnregisterServer()
{
return AMovieDllRegisterServer2( FALSE );
} // DllUnregisterServer
//
// CreateInstance
//
// The only allowed way to create Bouncing balls!
//
CUnknown * WINAPI CBouncingBall::CreateInstance(LPUNKNOWN lpunk, HRESULT *phr)
{
CUnknown *punk = new CBouncingBall(lpunk, phr);
if (punk == NULL) {
*phr = E_OUTOFMEMORY;
}
return punk;
} // CreateInstance
//
// Constructor
//
// Initialise a CBallStream object so that we have a pin.
//
CBouncingBall::CBouncingBall(LPUNKNOWN lpunk, HRESULT *phr) :
CSource(NAME("Bouncing ball"),
lpunk,
CLSID_BouncingBall)
{
CAutoLock cAutoLock(&m_cStateLock);
m_paStreams = (CSourceStream **) new CBallStream*[1];
if (m_paStreams == NULL) {
*phr = E_OUTOFMEMORY;
return;
}
m_paStreams[0] = new CBallStream(phr, this, L"A Bouncing Ball!");
if (m_paStreams[0] == NULL) {
*phr = E_OUTOFMEMORY;
return;
}
} // (Constructor)
//
// Constructor
//
CBallStream::CBallStream(HRESULT *phr,
CBouncingBall *pParent,
LPCWSTR pPinName) :
CSourceStream(NAME("Bouncing Ball"),phr, pParent, pPinName),
m_iImageWidth(320),
m_iImageHeight(240),
m_iDefaultRepeatTime(20)
{
CAutoLock cAutoLock(&m_cSharedState);
m_Ball = new CBall(m_iImageWidth, m_iImageHeight);
if (m_Ball == NULL) {
*phr = E_OUTOFMEMORY;
}
} // (Constructor)
//
// Destructor
//
CBallStream::~CBallStream()
{
CAutoLock cAutoLock(&m_cSharedState);
if( m_Ball )
delete m_Ball;
} // (Destructor)
//
// FillBuffer
//
// Plots a ball into the supplied video buffer
//
HRESULT CBallStream::FillBuffer(IMediaSample *pms)
{
BYTE *pData;
long lDataLen;
pms->GetPointer(&pData);
lDataLen = pms->GetSize();
// If true then we clear the output buffer and don't attempt to
// erase a previous drawing of the ball - this will be the case
// when we start running as the buffer will be full of rubbish
if( m_bZeroMemory ) {
ZeroMemory( pData, lDataLen );
}
{
CAutoLock cAutoLockShared(&m_cSharedState);
// If we haven't just cleared the buffer delete the old
// ball and move the ball on
if( !m_bZeroMemory ){
BYTE aZeroes[ 4 ] = { 0, 0, 0, 0 };
m_Ball->PlotBall(pData, aZeroes, m_iPixelSize);
m_Ball->MoveBall(m_rtSampleTime - (LONG) m_iRepeatTime);
}
m_Ball->PlotBall(pData, m_BallPixel, m_iPixelSize);
// The current time is the sample's start
CRefTime rtStart = m_rtSampleTime;
// Increment to find the finish time
m_rtSampleTime += (LONG)m_iRepeatTime;
pms->SetTime((REFERENCE_TIME *) &rtStart,(REFERENCE_TIME *) &m_rtSampleTime);
}
m_bZeroMemory = FALSE;
pms->SetSyncPoint(TRUE);
return NOERROR;
} // FillBuffer
//
// Notify
//
// Alter the repeat rate according to quality management messages sent from
// the downstream filter (often the renderer). Wind it up or down according
// to the flooding level - also skip forward if we are notified of Late-ness
//
STDMETHODIMP CBallStream::Notify(IBaseFilter * pSender, Quality q)
{
// Adjust the repeat rate.
if (q.Proportion<=0) {
m_iRepeatTime = 1000; // We don't go slower than 1 per second
} else {
m_iRepeatTime = m_iRepeatTime*1000/q.Proportion;
if (m_iRepeatTime>1000) {
m_iRepeatTime = 1000; // We don't go slower than 1 per second
} else if (m_iRepeatTime<10) {
m_iRepeatTime = 10; // We don't go faster than 100/sec
}
}
// skip forwards
if (q.Late > 0) {
m_rtSampleTime += q.Late;
}
return NOERROR;
} // Notify
//
// GetMediaType
//
// I _prefer_ 5 formats - 8, 16 (*2), 24 or 32 bits per pixel and
// I will suggest these with an image size of 320x240. However
// I can accept any image size which gives me some space to bounce.
//
// A bit of fun:
// 8 bit displays get red balls
// 16 bit displays get blue
// 24 bit see green
// And 32 bit see yellow
//
// Prefered types should be ordered by quality, zero as highest quality
// Therefore iPosition =
// 0return a 32bit mediatype
// 1return a 24bit mediatype
// 2return 16bit RGB565
// 3return a 16bit mediatype (rgb555)
// 4return 8 bit palettised format
// (iPosition > 4 is invalid)
//
HRESULT CBallStream::GetMediaType(int iPosition, CMediaType *pmt)
{
CAutoLock cAutoLock(m_pFilter->pStateLock());
if (iPosition < 0) {
return E_INVALIDARG;
}
// Have we run off the end of types
if (iPosition > 4) {
return VFW_S_NO_MORE_ITEMS;
}
VIDEOINFO *pvi = (VIDEOINFO *) pmt->AllocFormatBuffer(sizeof(VIDEOINFO));
if (NULL == pvi) {
return(E_OUTOFMEMORY);
}
ZeroMemory(pvi, sizeof(VIDEOINFO));
switch (iPosition) {
case 0: {// Return our highest quality 32bit format
// Place the RGB masks as the first 3 doublewords in the palette area
for (int i = 0; i < 3; i++)
pvi->TrueColorInfo.dwBitMasks[i] = bits888[i];
SetPaletteEntries(Yellow);
pvi->bmiHeader.biCompression = BI_BITFIELDS;
pvi->bmiHeader.biBitCount = 32;
}
break;
case 1: {// Return our 24bit format
SetPaletteEntries(Green);
pvi->bmiHeader.biCompression = BI_RGB;
pvi->bmiHeader.biBitCount = 24;
}
break;
case 2: { // 16 bit per pixel RGB565
// Place the RGB masks as the first 3 doublewords in the palette area
for (int i = 0; i < 3; i++)
pvi->TrueColorInfo.dwBitMasks[i] = bits565[i];
SetPaletteEntries(Blue);
pvi->bmiHeader.biCompression = BI_BITFIELDS;
pvi->bmiHeader.biBitCount = 16;
}
break;
case 3: {// 16 bits per pixel RGB555
// Place the RGB masks as the first 3 doublewords in the palette area
for (int i = 0; i < 3; i++)
pvi->TrueColorInfo.dwBitMasks[i] = bits555[i];
SetPaletteEntries(Blue);
pvi->bmiHeader.biCompression = BI_BITFIELDS;
pvi->bmiHeader.biBitCount = 16;
}
break;
case 4: {// 8 bit palettised
SetPaletteEntries(Red);
pvi->bmiHeader.biCompression = BI_RGB;
pvi->bmiHeader.biBitCount = 8;
pvi->bmiHeader.biClrUsed= iPALETTE_COLORS;
}
break;
}
// (Adjust the parameters common to all formats...)
// put the optimal palette in place
for (int i = 0; i < iPALETTE_COLORS; i++) {
pvi->TrueColorInfo.bmiColors[i].rgbRed = m_Palette[i].peRed;
pvi->TrueColorInfo.bmiColors[i].rgbBlue = m_Palette[i].peBlue;
pvi->TrueColorInfo.bmiColors[i].rgbGreen = m_Palette[i].peGreen;
pvi->TrueColorInfo.bmiColors[i].rgbReserved = 0;
}
pvi->bmiHeader.biSize= sizeof(BITMAPINFOHEADER);
pvi->bmiHeader.biWidth= m_iImageWidth;
pvi->bmiHeader.biHeight= m_iImageHeight;
pvi->bmiHeader.biPlanes= 1;
pvi->bmiHeader.biSizeImage= GetBitmapSize(&pvi->bmiHeader);
pvi->bmiHeader.biClrImportant= 0;
SetRectEmpty(&(pvi->rcSource));// we want the whole image area rendered.
SetRectEmpty(&(pvi->rcTarget));// no particular destination rectangle
pmt->SetType(&MEDIATYPE_Video);
pmt->SetFormatType(&FORMAT_VideoInfo);
pmt->SetTemporalCompression(FALSE);
// Work out the GUID for the subtype from the header info.
const GUID SubTypeGUID = GetBitmapSubtype(&pvi->bmiHeader);
pmt->SetSubtype(&SubTypeGUID);
pmt->SetSampleSize(pvi->bmiHeader.biSizeImage);
return NOERROR;
} // GetMediaType
//
// CheckMediaType
//
// We will accept 8, 16, 24 or 32 bit video formats, in any
// image size that gives room to bounce.
// Returns E_INVALIDARG if the mediatype is not acceptable
//
HRESULT CBallStream::CheckMediaType(const CMediaType *pMediaType)
{
CAutoLock cAutoLock(m_pFilter->pStateLock());
if ((*(pMediaType->Type()) != MEDIATYPE_Video)// we only output video!
|| !(pMediaType->IsFixedSize()) ) {// ...in fixed size samples
return E_INVALIDARG;
}
// Check for the subtypes we support
const GUID *SubType = pMediaType->Subtype();
if ((*SubType != MEDIASUBTYPE_RGB8)
&& (*SubType != MEDIASUBTYPE_RGB565)
&& (*SubType != MEDIASUBTYPE_RGB555)
&& (*SubType != MEDIASUBTYPE_RGB24)
&& (*SubType != MEDIASUBTYPE_RGB32)) {
return E_INVALIDARG;
}
// Get the format area of the media type
VIDEOINFO *pvi = (VIDEOINFO *) pMediaType->Format();
if (pvi == NULL)
return E_INVALIDARG;
// Check the image size. As my default ball is 10 pixels big
// look for at least a 20x20 image. This is an arbitary size constraint,
// but it avoids balls that are bigger than the picture...
if ((pvi->bmiHeader.biWidth < 20) || (pvi->bmiHeader.biHeight < 20) ) {
return E_INVALIDARG;
}
return S_OK; // This format is acceptable.
} // CheckMediaType
//
// DecideBufferSize
//
// This will always be called after the format has been sucessfully
// negotiated. So we have a look at m_mt to see what size image we agreed.
// Then we can ask for buffers of the correct size to contain them.
//
HRESULT CBallStream::DecideBufferSize(IMemAllocator *pAlloc,ALLOCATOR_PROPERTIES *pProperties)
{
CAutoLock cAutoLock(m_pFilter->pStateLock());
ASSERT(pAlloc);
ASSERT(pProperties);
HRESULT hr = NOERROR;
VIDEOINFO *pvi = (VIDEOINFO *) m_mt.Format();
pProperties->cBuffers = 1;
pProperties->cbBuffer = pvi->bmiHeader.biSizeImage;
ASSERT(pProperties->cbBuffer);
// Ask the allocator to reserve us some sample memory, NOTE the function
// can succeed (that is return NOERROR) but still not have allocated the
// memory that we requested, so we must check we got whatever we wanted
ALLOCATOR_PROPERTIES Actual;
hr = pAlloc->SetProperties(pProperties,&Actual);
if (FAILED(hr)) {
return hr;
}
// Is this allocator unsuitable
if (Actual.cbBuffer < pProperties->cbBuffer) {
return E_FAIL;
}
// Make sure that we have only 1 buffer (we erase the ball in the
// old buffer to save having to zero a 200k+ buffer every time
// we draw a frame)
ASSERT( Actual.cBuffers == 1 );
return NOERROR;
} // DecideBufferSize
//
// SetMediaType
//
// Called when a media type is agreed between filters
//
HRESULT CBallStream::SetMediaType(const CMediaType *pMediaType)
{
CAutoLock cAutoLock(m_pFilter->pStateLock());
// Pass the call up to my base class
HRESULT hr = CSourceStream::SetMediaType(pMediaType);
if (SUCCEEDED(hr)) {
VIDEOINFO * pvi = (VIDEOINFO *) m_mt.Format();
switch (pvi->bmiHeader.biBitCount) {
case 8:// Make a red pixel
m_BallPixel[0] = 10;// 0 is palette index of red
m_iPixelSize = 1;
SetPaletteEntries(Red);
break;
case 16:// Make a blue pixel
m_BallPixel[0] = 0xf8;// 00000000 00011111 is blue in rgb555 or rgb565
m_BallPixel[1] = 0x0;// don't forget the byte ordering within the mask word.
m_iPixelSize = 2;
SetPaletteEntries(Blue);
break;
case 24:// Make a green pixel
m_BallPixel[0] = 0x0;
m_BallPixel[1] = 0xff;
m_BallPixel[2] = 0x0;
m_iPixelSize = 3;
SetPaletteEntries(Green);
break;
case 32:// Make a yellow pixel
m_BallPixel[0] = 0x0;
m_BallPixel[1] = 0x0;
m_BallPixel[2] = 0xff;
m_BallPixel[3] = 0xff;
m_iPixelSize = 4;
SetPaletteEntries(Yellow);
break;
default:
// We should never agree any other pixel sizes
ASSERT("Tried to agree inappropriate format");
}
CBall *pNewBall = new CBall(pvi->bmiHeader.biWidth, pvi->bmiHeader.biHeight);
if( pNewBall ){
delete m_Ball;
m_Ball = pNewBall;
} else
hr = E_OUTOFMEMORY;
return NOERROR;
} else {
return hr;
}
} // SetMediaType
//
// OnThreadCreate
//
// As we go active reset the stream time to zero
//
HRESULT CBallStream::OnThreadCreate()
{
CAutoLock cAutoLockShared(&m_cSharedState);
m_rtSampleTime = 0;
// we need to also reset the repeat time in case the system
// clock is turned off after m_iRepeatTime gets very big
m_iRepeatTime = m_iDefaultRepeatTime;
// Zero the output buffer on the first frame.
m_bZeroMemory = TRUE;
return NOERROR;
} // OnThreadCreate
//
// SetPaletteEntries
//
// If we set our palette to the current system palette + the colours we want
// the system has the least amount of work to do whilst plotting our images,
// if this stream is rendered to the current display. The first non reserved
// palette slot is at m_Palette[10], so put our first colour there. Also
// guarantees that black is always represented by zero in the frame buffer
//
HRESULT CBallStream::SetPaletteEntries(Colour colour)
{
CAutoLock cAutoLock(m_pFilter->pStateLock());
HDC hdc = GetDC(NULL);// hdc for the current display.
UINT res = GetSystemPaletteEntries(hdc, 0, iPALETTE_COLORS, (LPPALETTEENTRY) &m_Palette);
ReleaseDC(NULL, hdc);
if (res == 0) {
return E_FAIL;
}
switch (colour) {
case Red:
m_Palette[10].peBlue = 0;
m_Palette[10].peGreen = 0;
m_Palette[10].peRed = 0xff;
break;
case Yellow:
m_Palette[10].peBlue = 0;
m_Palette[10].peGreen = 0xff;
m_Palette[10].peRed = 0xff;
break;
case Blue:
m_Palette[10].peBlue = 0xff;
m_Palette[10].peGreen = 0;
m_Palette[10].peRed = 0;
break;
case Green:
m_Palette[10].peBlue = 0;
m_Palette[10].peGreen = 0xff;
m_Palette[10].peRed = 0;
break;
}
m_Palette[10].peFlags = 0;
return NOERROR;
} // SetPaletteEntries