www.pudn.com > VirtualVCR-src-v2.6.9.zip > VideoCrop.cpp
/*
Virtual VCR
Copyright (C) 2002 Shaun Faulds
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Acknowledgments:
This application and associated filters are based on the examples
from the Microsoft DirectX DirectShow SDK.
*/
#include
#include
#include
#if (1100 > _MSC_VER)
#include
#else
#include
#endif
#include "VideoCropUIDs.h"
#include "iVideoCrop.h"
#include "VideoCropProp.h"
#include "VideoCropPropData.h"
#include "VideoCrop.h"
#include "resource.h"
#include "VideoCrop.version"
#include "../Common/Filter/iFilterProperties.h"
/*
// not defined (currently) in the directshow headers
// remove this structure if it clashes in the future
typedef struct tagVIDEOINFOHEADER2 {
RECT rcSource;
RECT rcTarget;
DWORD dwBitRate;
DWORD dwBitErrorRate;
REFERENCE_TIME AvgTimePerFrame;
DWORD dwInterlaceFlags;
DWORD dwCopyProtectFlags;
DWORD dwPictAspectRatioX;
DWORD dwPictAspectRatioY;
DWORD dwReserved1;
DWORD dwReserved2;
BITMAPINFOHEADER bmiHeader;
} VIDEOINFOHEADER2;
*/
// End of define.
//
// Setup information
//
const AMOVIESETUP_MEDIATYPE sudPinTypes =
{
&MEDIATYPE_Video, // Major type
&MEDIASUBTYPE_NULL // Minor type
};
const AMOVIESETUP_PIN sudpPins[] =
{
{ L"Input", // Pins string name
FALSE, // Is it rendered
FALSE, // Is it an output
FALSE, // Are we allowed none
FALSE, // And allowed many
&CLSID_NULL, // Connects to filter
NULL, // Connects to pin
1, // Number of types
&sudPinTypes // Pin information
},
{ L"Output", // Pins string name
FALSE, // Is it rendered
TRUE, // Is it an output
FALSE, // Are we allowed none
FALSE, // And allowed many
&CLSID_NULL, // Connects to filter
NULL, // Connects to pin
1, // Number of types
&sudPinTypes // Pin information
}
};
const AMOVIESETUP_FILTER sudHistogram =
{
&CLSID_VideoCrop, // Filter CLSID
L"Video Crop Filter", // String name
MERIT_DO_NOT_USE, // Filter merit
2, // Number of pins
sudpPins // Pin information
};
// List of class IDs and creator functions for the class factory. This
// provides the link between the OLE entry point in the DLL and an object
// being created. The class factory will call the static CreateInstance
CFactoryTemplate g_Templates[] = {
{ L"Video Crop"
, &CLSID_VideoCrop
, CVideoCrop::CreateInstance
, NULL
, &sudHistogram }
,
{ L"Video Crop"
, &CLSID_VideoCropPropertyPage
, CVideoCropProperties::CreateInstance }
};
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
//
// DllRegisterServer
//
// Handles sample registry and unregistry
//
STDAPI DllRegisterServer()
{
return AMovieDllRegisterServer2( TRUE );
} // DllRegisterServer
//
// DllUnregisterServer
//
STDAPI DllUnregisterServer()
{
return AMovieDllRegisterServer2( FALSE );
} // DllUnregisterServer
//
// Constructor
//
CVideoCrop::CVideoCrop(TCHAR *tszName,
LPUNKNOWN punk,
HRESULT *phr) :
CTransformFilter(tszName, punk, CLSID_VideoCrop)
{
input_width = 0;
input_height = 0;
output_width = 0;
output_height = 0;
crop_left = 0;
crop_right = 0;
crop_top = 0;
crop_bottom = 0;
pixel_length_bytes = 0;
bottom_up = true;
} // (Constructor)
//
// CreateInstance
//
// Provide the way for COM to create a EZrgb24 object
//
CUnknown *CVideoCrop::CreateInstance(LPUNKNOWN punk, HRESULT *phr)
{
CVideoCrop *pNewObject = new CVideoCrop(NAME("Video Crop"), punk, phr);
if (pNewObject == NULL)
{
*phr = E_OUTOFMEMORY;
}
return pNewObject;
} // CreateInstance
//
// NonDelegatingQueryInterface
//
// Reveals IIPEffect and ISpecifyPropertyPages
//
STDMETHODIMP CVideoCrop::NonDelegatingQueryInterface(REFIID riid, void **ppv)
{
CheckPointer(ppv,E_POINTER);
if (riid == IID_IVideoCrop)
{
return GetInterface((IVideoCrop *) this, ppv);
}
else if (riid == IID_IFilterProperties)
{
return GetInterface((IFilterProperties *) this, ppv);
}
else if (riid == IID_ISpecifyPropertyPages)
{
return GetInterface((ISpecifyPropertyPages *) this, ppv);
}
else
{
return CTransformFilter::NonDelegatingQueryInterface(riid, ppv);
}
} // NonDelegatingQueryInterface
//
// On Join Filter Graph Check to see if this is the only instance of this
// filter.
//
/*
int FindMyself(IFilterGraph* pGB)
{
HRESULT hr;
IEnumFilters* pEnum = NULL;
IBaseFilter* pFilter = NULL;
IVideoCrop *pDVF = NULL;
hr = pGB->EnumFilters(&pEnum);
if(FAILED(hr)) return(0);
int nInstances = 0;
while(pEnum->Next(1, &pFilter, NULL) == S_OK)
{
hr = pFilter->QueryInterface(CLSID_VideoCrop, (LPVOID*)&pDVF);
pFilter->Release();
if(FAILED(hr)) continue;
nInstances++;
pDVF->Release();
}
pEnum->Release();
return(nInstances);
}
//
// On Join Filter Graph Check to see if there is less then 10 instances
// of this filter int he graph.
// This is just an example of what can be done.
//
HRESULT CVideoCrop::JoinFilterGraph(IFilterGraph* pGraph, LPCWSTR pName)
{
if(pGraph)
{
//MsgBOX(TEXT("Added To FilterGraph"));
if(FindMyself(pGraph) > 10)
{
return E_FAIL;
}
}
return CTransformFilter::JoinFilterGraph(pGraph, pName);
}
*/
//
// Verifies that the input pin supports the media type.
//
HRESULT CVideoCrop::CheckInputType(const CMediaType* mediaType)
{
// assertion
CheckPointer (mediaType, E_POINTER);
// major type supported?
if (!IsEqualGUID (*mediaType->Type (), MEDIATYPE_Video))
{
return E_FAIL;
}
// format type supported
if ((!IsEqualGUID (*mediaType->FormatType (), FORMAT_VideoInfo)) &&
(!IsEqualGUID (*mediaType->FormatType (), FORMAT_VideoInfo2)))
{
return E_FAIL;
}
// we can support this connection
return S_OK;
}
//
// Verifies that the output pin supports the media type.
//
HRESULT CVideoCrop::CheckOutputType(const CMediaType* mediaType)
{
// assertion
CheckPointer (mediaType, E_POINTER);
// major type supported?
if (!IsEqualGUID (*mediaType->Type (), MEDIATYPE_Video))
{
return E_FAIL;
}
// format type supported
if ((!IsEqualGUID (*mediaType->FormatType (), FORMAT_VideoInfo)) &&
(!IsEqualGUID (*mediaType->FormatType (), FORMAT_VideoInfo2)))
{
return E_FAIL;
}
// we can support this connection
return S_OK;
}
//
// Verifies that the input and output pins support the media type.
// Calls CheckOutputType and CheckInputType depending on
// the pin direction.
//
HRESULT CVideoCrop::CheckTransform (const CMediaType* inputMediaType, const CMediaType* outputMediaType)
{
HRESULT result = S_OK;
// check input media
if (inputMediaType != 0)
{
result = this->CheckInputType (inputMediaType);
if (result != S_OK)
{
return result;
}
}
// check output media
if (outputMediaType != 0)
{
result = this->CheckOutputType (outputMediaType);
if (result != S_OK)
{
return result;
}
}
// Found a vaild match
return S_OK;
}
//
// Sets the number and size of buffers required for the transfer.
// Works out the buffer size by multiplying the width by the height
// of the output video frame by the colour format bit byte size.
// BufferSize = Width * Height * (Colour Bit Depth in bytes)
//
HRESULT CVideoCrop::DecideBufferSize ( IMemAllocator* allocator, ALLOCATOR_PROPERTIES* properties )
{
// make sure the input pin is connected
if (this->m_pInput->IsConnected () == FALSE)
{
return E_UNEXPECTED;
}
// assertions
CheckPointer (allocator, E_POINTER);
CheckPointer (properties, E_POINTER);
properties->cBuffers = 1;
properties->cbBuffer = input_width * input_height * pixel_length_bytes;
//properties->cbBuffer = output_width * output_height * pixel_length_bytes;
// set properties
ALLOCATOR_PROPERTIES actualProperties;
HRESULT result = allocator->SetProperties (properties, &actualProperties);
if (result != S_OK)
{
return result;
}
// verify we got enough memory
if ((actualProperties.cBuffers < properties->cBuffers) ||
(actualProperties.cbBuffer < properties->cbBuffer))
{
return E_FAIL;
}
return S_OK;
}
//
// Returns one of the media types that the output pin supports
//
HRESULT CVideoCrop::GetMediaType(int position, CMediaType* mediaType)
{
// make sure the input pin is connected
if (this->m_pInput->IsConnected () == FALSE)
{
return E_UNEXPECTED;
}
// assertion
if (mediaType == 0)
{
return E_POINTER;
}
if (position < 0)
{
return E_INVALIDARG;
}
if (position > 1)
{
return VFW_S_NO_MORE_ITEMS;
}
// Alter the size of the width and height for the ouput
// also alter the sample size.
*mediaType = m_pInput->CurrentMediaType();
VIDEOINFOHEADER* header = reinterpret_cast (mediaType->Format ());
int pixSize = header->bmiHeader.biBitCount / 8;
header->bmiHeader.biWidth = output_width;
header->bmiHeader.biHeight = output_height;
header->bmiHeader.biSizeImage = output_width * output_height * pixSize;
mediaType->SetSampleSize(header->bmiHeader.biSizeImage);
return S_OK;
}
//
// Sets the input media type, this is called when SetMediaType
// is called with a pin direction of INPUT
//
HRESULT CVideoCrop::SetInputMediaType(const CMediaType* mediaType)
{
int width = 0;
int height = 0;
int bitsPerPixel = 0;
if (*mediaType->FormatType () == FORMAT_VideoInfo)
{
VIDEOINFOHEADER* header = reinterpret_cast (mediaType->Format ());
if ( header == 0 )
return E_FAIL;
width = (int)(header->bmiHeader.biWidth);
height = (int)(header->bmiHeader.biHeight);
bottom_up = (height > 0);
bitsPerPixel = (int)(header->bmiHeader.biBitCount);
}
/*
else if ( *mediaType->FormatType () == FORMAT_VideoInfo2)
{
// VIDEOINFOHEADER2
VIDEOINFOHEADER2* header = reinterpret_cast (mediaType->Format ());
if (header == 0)
return E_FAIL;
width = (int)(header->bmiHeader.biWidth);
height = (int)(header->bmiHeader.biHeight);
bottom_up = (height > 0);
bitsPerPixel = (int)(header->bmiHeader.biBitCount);
}
*/
else
{
return E_FAIL;
}
input_width = width;
input_height = height;
output_width = input_width - (crop_left + crop_right);
output_height = input_height - (crop_top + crop_bottom);
pixel_length_bytes = bitsPerPixel / 8;
//Reconnect the output pins to pass the new size downstream
ReconnectOutputPins();
//MsgBOX(TEXT("Pixel Depth %d width %d height %d"), bitsPerPixel, width, height);
return S_OK;
}
//
// Sets the output media type, this is called when SetMediaType
// is called with a pin direction of OUTPUT
//
HRESULT CVideoCrop::SetOutputMediaType (const CMediaType* mediaType)
{
// // copy media type
// this->outputMediaType = *mediaType;
// setup format info
if (*mediaType->FormatType () == FORMAT_VideoInfo)
{
//MsgBOX(TEXT("Set Output Video Info VIDEOINFOHEADER"));
VIDEOINFOHEADER* header = reinterpret_cast (mediaType->Format ());
int pixSize = header->bmiHeader.biBitCount / 8;
header->bmiHeader.biWidth = output_width;
header->bmiHeader.biHeight = output_height;
header->bmiHeader.biSizeImage = output_width * output_height * pixSize;
}
/*
// VIDEOINFOHEADER2
else if (*mediaType->FormatType () == FORMAT_VideoInfo2)
{
//MsgBOX(TEXT("Set Output Video Info VIDEOINFOHEADER2"));
VIDEOINFOHEADER2* header = reinterpret_cast (mediaType->Format ());
int pixSize = header->bmiHeader.biBitCount / 8;
header->bmiHeader.biWidth = output_width;
header->bmiHeader.biHeight = output_height;
header->bmiHeader.biSizeImage = output_width * output_height * pixSize;
}
*/
// unrecognized type
else
{
return E_FAIL;
}
return S_OK;
}
//
// Informs when the media type is established for the connection.
// This then calls the SetInputMediaType and SetOutputMediaType
// for each pin direction.
//
HRESULT CVideoCrop::SetMediaType(PIN_DIRECTION direction, const CMediaType* mediaType)
{
// assertion
CheckPointer (mediaType, E_POINTER);
// Call super method, pass it what we got here.
HRESULT result = this->CTransformFilter::SetMediaType (direction, mediaType);
if (result != S_OK)
{
return result;
}
// set media type for input pin?
if (direction == PINDIR_INPUT)
{
this->SetInputMediaType(mediaType);
}
// set media type for the output pin
else if (direction == PINDIR_OUTPUT)
{
this->SetOutputMediaType(mediaType);
}
// unrecognized pin direction
else
{
return E_FAIL;
}
return S_OK;
}
//
// Performs transform operations of the filter.
// Used PrepareOutputSample to copy the properties from
// the input sample to the output sample then does
// processes the video data by copying the input
// data from the input sample to the output sample.
// Only the data that is in the cropped are is copied!
//
HRESULT CVideoCrop::Transform(IMediaSample* inSample, IMediaSample* outSample)
{
//
// Perpare output sample.
// Use the local method to copy sample times etc
// from input sample to the output sample
//
HRESULT result = this->PrepareOutputSample(inSample, outSample);
if (result != S_OK) return result;
// Now do the data copy
BYTE *srcBuffer = 0;
BYTE *dstBuffer = 0;
inSample->GetPointer (&srcBuffer);
outSample->GetPointer (&dstBuffer);
// Copy the image data from the input to the output
// If the image it bottom up then work out the crop value
// from the bottom crop
if(bottom_up)
srcBuffer += (crop_bottom * input_width) * pixel_length_bytes;
else
srcBuffer += (crop_top * input_width) * pixel_length_bytes;
srcBuffer += crop_left * pixel_length_bytes;
for(int x = 0;x < output_height; x++)
{
CopyMemory((PVOID)dstBuffer,(PVOID)srcBuffer, output_width * pixel_length_bytes);
dstBuffer += output_width * pixel_length_bytes;
srcBuffer += input_width * pixel_length_bytes;
}
// set output datalength
outSample->SetActualDataLength(output_height * output_width * pixel_length_bytes);
return S_OK;
}
//
// Prepares the output sample.
// This copies the property from the
// input sample to the output sample.
//
HRESULT CVideoCrop::PrepareOutputSample (IMediaSample* inSample, IMediaSample* outSample)
{
// assertions
CheckPointer(inSample, E_POINTER);
CheckPointer(outSample, E_POINTER);
//.1
// set media type
//
// Copy the media type
//outSample->SetMediaType(&this->outputMediaType);
//.2
// copy the sample times
//
REFERENCE_TIME timeStart = 0;
REFERENCE_TIME timeEnd = 0;
if (inSample->GetTime (&timeStart, &timeEnd) == S_OK)
{
outSample->SetTime (&timeStart, &timeEnd);
}
LONGLONG mediaStart = 0;
LONGLONG mediaEnd = 0;
if (inSample->GetMediaTime (&mediaStart, &mediaEnd) == S_OK)
{
outSample->SetMediaTime (&mediaStart,&mediaEnd);
}
//.3
// copy the sync point property
//
HRESULT result = inSample->IsSyncPoint ();
if (result == S_OK)
{
outSample->SetSyncPoint (TRUE);
}
else if (result == S_FALSE)
{
outSample->SetSyncPoint (FALSE);
}
else
{
return E_UNEXPECTED;
}
//.4
// copy the preroll property
//
result = inSample->IsPreroll ();
if (result == S_OK)
{
outSample->SetPreroll (TRUE);
}
else if (result == S_FALSE)
{
outSample->SetPreroll (FALSE);
}
else
{
return E_UNEXPECTED;
}
//.5
// copy the discontinuity property
//
result = inSample->IsDiscontinuity ();
if (result == S_OK)
{
outSample->SetDiscontinuity (TRUE);
}
else if (result == S_FALSE)
{
outSample->SetDiscontinuity(FALSE);
}
else
{
return E_UNEXPECTED;
}
return S_OK;
}
//
// GetPages
//
// Returns the clsid's of the property pages we support
//
STDMETHODIMP CVideoCrop::GetPages(CAUUID *pPages)
{
pPages->cElems = 1;
pPages->pElems = (GUID *) CoTaskMemAlloc(sizeof(GUID));
if (pPages->pElems == NULL)
{
return E_OUTOFMEMORY;
}
*(pPages->pElems) = CLSID_VideoCropPropertyPage;
return NOERROR;
} // GetPages
//
// Interface Methods to get and set the crop data
//
//
// Gets the data for the crop
//
STDMETHODIMP CVideoCrop::get_Data(VideoCropPropData*& data)
{
data = new VideoCropPropData(crop_left, crop_right, crop_top, crop_bottom, input_width, input_height);
return NOERROR;
}
//
// Sets the Data for the crop
//
STDMETHODIMP CVideoCrop::set_Data(VideoCropPropData*& data)
{
crop_left = data->getLeft();
crop_right = data->getRight();
crop_top = data->getTop();
crop_bottom = data->getBottom();
output_width = input_width - (crop_left + crop_right);
output_height = input_height - (crop_top + crop_bottom);
//Reconnect the output pins to pass the new size downstream
ReconnectOutputPins();
return NOERROR;
}
//
// Method to display a message box to show data
//
int CVideoCrop::MsgBOX(LPTSTR sz,...)
{
static TCHAR tach[2000];
va_list va;
va_start(va, sz);
wvsprintf(tach, sz, va);
va_end(va);
MessageBox(HWND_DESKTOP, tach, "Filter Message Box", MB_OK|MB_ICONEXCLAMATION|MB_TASKMODAL);
return FALSE;
}
//
//Reconnect the output pins to pass the new size down the stream
//
BOOL CVideoCrop::ReconnectOutputPins()
{
FILTER_INFO filerInfo;
HRESULT hr = this->QueryFilterInfo(&filerInfo);
if(filerInfo.pGraph)
{
IPin *pP;
ULONG u;
IEnumPins *pins = NULL;
PIN_INFO pininfo;
hr = this->EnumPins(&pins);
pins->Reset();
while(hr == NOERROR)
{
hr = pins->Next(1, &pP, &u);
if(hr == S_OK && pP)
{
hr = pP->QueryPinInfo(&pininfo);
if(hr == NOERROR)
{
if(pininfo.dir == PINDIR_OUTPUT)
{
filerInfo.pGraph->Reconnect(pP);
}
if(pininfo.pFilter)
pininfo.pFilter->Release();
}
pP->Release();
}
}
if(pins)
pins->Release();
}
if(filerInfo.pGraph)
filerInfo.pGraph->Release();
filerInfo.pGraph = NULL;
return (hr == S_OK);
}
//This is for the common interface IFilterProperties
//
// Gets the data properties
//
STDMETHODIMP CVideoCrop::get_Prop(char*& data)
{
char filterData[1024];
wsprintf(filterData, "%d, %d, %d ,%d", crop_left, crop_right, crop_top, crop_bottom);
data = filterData;
return NOERROR;
}
//
// Sets the Data for properties
//
STDMETHODIMP CVideoCrop::set_Prop(char* data)
{
int value = 0;
int destination = 0;
char *pch;
pch = strtok (data,",");
while (pch != NULL)
{
value = atol(pch);
if(destination == 0)
crop_left = value;
if(destination == 1)
crop_right = value;
if(destination == 2)
crop_top = value;
if(destination == 3)
crop_bottom = value;
destination++;
pch = strtok (NULL, ",");
}
return NOERROR;
}
STDMETHODIMP CVideoCrop::get_Version(char *data)
{
wsprintf(data, "%s", MAIN_VERSION);
return NOERROR;
}