//------------------------------------------------------------------------------
// File: PushSourceDesktop.cpp
//
// Desc: DirectShow sample code - In-memory push mode source filter
// Provides an image of the user's desktop as a continuously updating stream.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------
#include <streams.h>
#include "PushSourceDesktop.h"
#include "DibHelper.h"
/**********************************************
*
* CPushPinDesktop Class
*
*
**********************************************/
CPushPinDesktop::CPushPinDesktop(HRESULT *phr, CSource *pFilter)
: CSourceStream(NAME("Push Source Desktop"), phr, pFilter, L"Out"),
m_FramesWritten(0),
m_bZeroMemory(0),
m_iFrameNumber(0),
m_rtFrameLength(FPS_5), // Capture and display desktop 5 times per second
m_nCurrentBitDepth(32)
{
m_pParent = (CPushSourceDesktop*)pFilter;
m_hCursor = LoadCursor(0, MAKEINTRESOURCE(IDC_ARROW));
// The main point of this sample is to demonstrate how to take a DIB
// in host memory and insert it into a video stream.
// To keep this sample as simple as possible, we just read the desktop image
// from a file and copy it into every frame that we send downstream.
//
// In the filter graph, we connect this filter to the AVI Mux, which creates
// the AVI file with the video frames we pass to it. In this case,
// the end result is a screen capture video (GDI images only, with no
// support for overlay surfaces).
// Get the device context of the main display
HDC hDC;
hDC = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
// Get the dimensions of the main desktop window
m_rScreen.left = m_rScreen.top = 0;
m_rScreen.right = GetDeviceCaps(hDC, HORZRES);
m_rScreen.bottom = GetDeviceCaps(hDC, VERTRES);
// Save dimensions for later use in FillBuffer()
m_iImageWidth = m_rScreen.right - m_rScreen.left;
m_iImageHeight = m_rScreen.bottom - m_rScreen.top;
// Release the device context
DeleteDC(hDC);
}
CPushPinDesktop::~CPushPinDesktop()
{
DbgLog((LOG_TRACE, 3, TEXT("Frames written %d"), m_iFrameNumber));
}
//
// GetMediaType
//
// Prefer 5 formats - 8, 16 (*2), 24 or 32 bits per pixel
//
// Prefered types should be ordered by quality, with zero as highest quality.
// Therefore, iPosition =
// 0 Return a 32bit mediatype
// 1 Return a 24bit mediatype
// 2 Return 16bit RGB565
// 3 Return a 16bit mediatype (rgb555)
// 4 Return 8 bit palettised format
// >4 Invalid
//
HRESULT CPushPinDesktop::GetMediaType(int iPosition, CMediaType *pmt)
{
CheckPointer(pmt,E_POINTER);
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);
// Initialize the VideoInfo structure before configuring its members
ZeroMemory(pvi, sizeof(VIDEOINFO));
switch(iPosition)
{
case 0:
{
// Return our highest quality 32bit format
// Since we use RGB888 (the default for 32 bit), there is
// no reason to use BI_BITFIELDS to specify the RGB
// masks. Also, not everything supports BI_BITFIELDS
pvi->bmiHeader.biCompression = BI_RGB;
pvi->bmiHeader.biBitCount = 32;
break;
}
case 1:
{ // Return our 24bit format
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];
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];
pvi->bmiHeader.biCompression = BI_BITFIELDS;
pvi->bmiHeader.biBitCount = 16;
break;
}
case 4:
{ // 8 bit palettised
pvi->bmiHeader.biCompression = BI_RGB;
pvi->bmiHeader.biBitCount = 8;
pvi->bmiHeader.biClrUsed = iPALETTE_COLORS;
break;
}
}
// Adjust the parameters common to all formats
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;
pvi->AvgTimePerFrame = 10000000 / 24;
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 CPushPinDesktop::CheckMediaType(const CMediaType *pMediaType)
{
CheckPointer(pMediaType,E_POINTER);
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 == NULL)
return E_INVALIDARG;
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 if the image width & height have changed
if( pvi->bmiHeader.biWidth != m_iImageWidth ||
abs(pvi->bmiHeader.biHeight) != m_iImageHeight)
{
// If the image width/height is changed, fail CheckMediaType() to force
// the renderer to resize the image.
return E_INVALIDARG;
}
// Don't accept formats with negative height, which would cause the desktop
// image to be displayed upside down.
if (pvi->bmiHeader.biHeight < 0)
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 CPushPinDesktop::De