You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
665 lines
17 KiB
665 lines
17 KiB
//==========================================================================;
|
|
//
|
|
// 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) 2001 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
//--------------------------------------------------------------------------;
|
|
//----------------------------------------------------------------------------
|
|
// VMRProp.cpp
|
|
//
|
|
// Created 3/18/2001
|
|
// Author: Steve Rowe [StRowe]
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include <windowsx.h>
|
|
#include <streams.h>
|
|
#include <atlbase.h>
|
|
#include <commctrl.h>
|
|
#include <stdio.h>
|
|
#include <shlobj.h> // for SHGetSpecialFolderPath
|
|
#include "resource.h"
|
|
|
|
#ifdef FILTER_DLL
|
|
#include <initguid.h>
|
|
#endif
|
|
|
|
#include "vmrprop.h"
|
|
|
|
|
|
#ifdef FILTER_DLL
|
|
|
|
STDAPI DllRegisterServer()
|
|
{
|
|
AMTRACE((TEXT("DllRegisterServer")));
|
|
return AMovieDllRegisterServer2( TRUE );
|
|
}
|
|
|
|
STDAPI DllUnregisterServer()
|
|
{
|
|
AMTRACE((TEXT("DllUnregisterServer")));
|
|
return AMovieDllRegisterServer2( FALSE );
|
|
}
|
|
|
|
CFactoryTemplate g_Templates[] = {
|
|
{
|
|
L"",
|
|
&CLSID_VMRFilterConfigProp,
|
|
CVMRFilterConfigProp::CreateInstance
|
|
}
|
|
};
|
|
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
|
|
|
|
#endif // #ifdef FILTER_DLL
|
|
|
|
|
|
//
|
|
// Constructor
|
|
//
|
|
CVMRFilterConfigProp::CVMRFilterConfigProp(LPUNKNOWN pUnk, HRESULT *phr) :
|
|
CBasePropertyPage(NAME("Filter Config Page"),pUnk,IDD_FILTERCONFIG,IDS_TITLE_FILTERCONFIG),
|
|
m_pIFilterConfig(NULL),
|
|
m_pIMixerControl(NULL),
|
|
m_dwNumPins(1),
|
|
m_pEventSink(NULL),
|
|
m_CurPin(0),
|
|
m_XPos(0.0F),
|
|
m_YPos(0.0F),
|
|
m_XSize(1.0F),
|
|
m_YSize(1.0F),
|
|
m_Alpha(1.0F)
|
|
{
|
|
ASSERT(phr);
|
|
}
|
|
|
|
|
|
//
|
|
// Create a quality properties object
|
|
//
|
|
CUnknown * CVMRFilterConfigProp::CreateInstance(LPUNKNOWN pUnk, HRESULT *phr)
|
|
{
|
|
ASSERT(phr);
|
|
|
|
CUnknown * pUnknown = new CVMRFilterConfigProp(pUnk, phr);
|
|
if (pUnknown == NULL)
|
|
{
|
|
*phr = E_OUTOFMEMORY;
|
|
}
|
|
return pUnknown;
|
|
}
|
|
|
|
|
|
//
|
|
// OnConnect
|
|
//
|
|
// Override CBasePropertyPage method.
|
|
// Notification of which filter this property page will communicate with
|
|
// We query the object for the IVMRFilterConfig interface.
|
|
//
|
|
HRESULT CVMRFilterConfigProp::OnConnect(IUnknown *pUnknown)
|
|
{
|
|
ASSERT(NULL != pUnknown);
|
|
ASSERT(NULL == m_pIFilterConfig);
|
|
ASSERT(NULL == m_pIMixerControl);
|
|
|
|
HRESULT hr = pUnknown->QueryInterface(IID_IVMRFilterConfig, (void **) &m_pIFilterConfig);
|
|
if (FAILED(hr) || NULL == m_pIFilterConfig)
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
// Get the IMediaEventSink interface. We use this later to tell graphedit that we updated the number of pins
|
|
CComPtr<IBaseFilter> pFilter;
|
|
hr = pUnknown->QueryInterface(IID_IBaseFilter, (void **) &pFilter);
|
|
if (FAILED(hr) || !pFilter)
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
FILTER_INFO Info;
|
|
hr = pFilter->QueryFilterInfo(&Info);
|
|
if (FAILED(hr))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
hr = Info.pGraph->QueryInterface(IID_IMediaEventSink, (void**) &m_pEventSink);
|
|
Info.pGraph->Release(); // the IFilterGraph pointer is ref counted. We need to release it or leak.
|
|
if (FAILED(hr) || NULL == m_pEventSink)
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
return NOERROR;
|
|
} // OnConnect
|
|
|
|
|
|
//
|
|
// OnDisconnect
|
|
//
|
|
// Override CBasePropertyPage method.
|
|
// Release all interfaces we referenced in OnConnect
|
|
//
|
|
HRESULT CVMRFilterConfigProp::OnDisconnect(void)
|
|
{
|
|
if (m_pIFilterConfig)
|
|
{
|
|
m_pIFilterConfig->Release();
|
|
m_pIFilterConfig = NULL;
|
|
}
|
|
if (m_pIMixerControl)
|
|
{
|
|
m_pIMixerControl->Release();
|
|
m_pIMixerControl = NULL;
|
|
}
|
|
if (m_pEventSink)
|
|
{
|
|
m_pEventSink->Release();
|
|
m_pEventSink = NULL;
|
|
}
|
|
return NOERROR;
|
|
} // OnDisconnect
|
|
|
|
|
|
//
|
|
// OnReceiveMessage
|
|
//
|
|
// Override CBasePropertyPage method.
|
|
// Handles the messages for our property window
|
|
//
|
|
INT_PTR CVMRFilterConfigProp::OnReceiveMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg)
|
|
{
|
|
HANDLE_MSG(hwnd, WM_COMMAND, OnCommand);
|
|
HANDLE_MSG(hwnd, WM_HSCROLL, OnHScroll);
|
|
} // switch
|
|
return CBasePropertyPage::OnReceiveMessage(hwnd,uMsg,wParam,lParam);
|
|
} // OnReceiveMessage
|
|
|
|
|
|
//
|
|
// OnCommand
|
|
//
|
|
// Handles the command messages for our property window
|
|
//
|
|
void CVMRFilterConfigProp::OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
|
|
{
|
|
switch(id)
|
|
{
|
|
case IDC_NUMPINS:
|
|
if (EN_CHANGE == codeNotify)
|
|
{
|
|
SetDirty();
|
|
break;
|
|
}
|
|
break;
|
|
|
|
// the selected pin changed
|
|
case IDC_PINSELECT:
|
|
if (CBN_SELCHANGE == codeNotify)
|
|
{
|
|
m_CurPin = ComboBox_GetCurSel(GetDlgItem(m_Dlg, IDC_PINSELECT));
|
|
InitConfigControls(m_CurPin);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
// Reset X position to center
|
|
case IDC_XPOS_STATIC:
|
|
if (STN_CLICKED == codeNotify)
|
|
{
|
|
m_XPos = 0.0F;
|
|
UpdatePinPos(m_CurPin);
|
|
|
|
HWND hwndT;
|
|
int pos;
|
|
TCHAR sz[32];
|
|
hwndT = GetDlgItem(m_Dlg, IDC_XPOS_SLIDER );
|
|
pos = int(1000 * m_XPos) + 1000;
|
|
SendMessage(hwndT, TBM_SETPOS, TRUE, (LPARAM)(pos));
|
|
_stprintf(sz, TEXT("%.3f"), m_XPos);
|
|
SetDlgItemText(m_Dlg, IDC_XPOS, sz);
|
|
}
|
|
break;
|
|
|
|
// Reset Y position to center
|
|
case IDC_YPOS_STATIC:
|
|
if (STN_CLICKED == codeNotify)
|
|
{
|
|
m_YPos = 0.0F;
|
|
UpdatePinPos(m_CurPin);
|
|
|
|
HWND hwndT;
|
|
int pos;
|
|
TCHAR sz[32];
|
|
pos = int(1000 * m_YPos) + 1000;
|
|
hwndT = GetDlgItem(m_Dlg, IDC_YPOS_SLIDER );
|
|
SendMessage(hwndT, TBM_SETPOS, TRUE, (LPARAM)(pos));
|
|
_stprintf(sz, TEXT("%.3f"), m_YPos);
|
|
SetDlgItemText(m_Dlg, IDC_YPOS, sz);
|
|
}
|
|
break;
|
|
|
|
// Capture the current video image
|
|
case IDC_SNAPSHOT:
|
|
CaptureCurrentImage();
|
|
break;
|
|
|
|
}
|
|
} // OnCommand
|
|
|
|
|
|
//
|
|
// OnApplyChanges
|
|
//
|
|
// Override CBasePropertyPage method.
|
|
// Called when the user clicks ok or apply.
|
|
// We update the number of pins on the VMR.
|
|
//
|
|
HRESULT CVMRFilterConfigProp::OnApplyChanges()
|
|
{
|
|
ASSERT(m_pIFilterConfig);
|
|
|
|
BOOL Success;
|
|
m_dwNumPins = GetDlgItemInt(m_Dlg, IDC_NUMPINS, &Success, FALSE);
|
|
|
|
//
|
|
// Set Number of Streams
|
|
//
|
|
HRESULT hr = m_pIFilterConfig->SetNumberOfStreams(m_dwNumPins);
|
|
if (SUCCEEDED(hr) && !m_pIMixerControl)
|
|
{
|
|
hr = m_pIFilterConfig->QueryInterface(IID_IVMRMixerControl, (void **) &m_pIMixerControl);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// select the last pin connected because this will be highest in the z-order
|
|
m_CurPin = m_dwNumPins - 1;
|
|
InitConfigControls(m_CurPin);
|
|
}
|
|
|
|
}
|
|
|
|
// Notify the graph so it will draw the new pins
|
|
if (m_pEventSink)
|
|
{
|
|
hr = m_pEventSink->Notify(EC_GRAPH_CHANGED, 0, 0);
|
|
}
|
|
|
|
return NOERROR;
|
|
} // OnApplyChanges
|
|
|
|
|
|
//
|
|
// SetDirty
|
|
//
|
|
// Sets m_hrDirtyFlag and notifies the property page site of the change
|
|
//
|
|
void CVMRFilterConfigProp::SetDirty()
|
|
{
|
|
m_bDirty = TRUE;
|
|
if (m_pPageSite)
|
|
{
|
|
m_pPageSite->OnStatusChange(PROPPAGESTATUS_DIRTY);
|
|
}
|
|
|
|
} // SetDirty
|
|
|
|
|
|
//
|
|
// OnActivate
|
|
//
|
|
// Override CBasePropertyPage method.
|
|
// Called when the page is being displayed. Used to initialize page contents.
|
|
//
|
|
HRESULT CVMRFilterConfigProp::OnActivate()
|
|
{
|
|
ASSERT(m_pIFilterConfig);
|
|
|
|
HRESULT hr = m_pIFilterConfig->GetNumberOfStreams(&m_dwNumPins);
|
|
if (NULL == m_pIMixerControl)
|
|
{
|
|
hr = m_pIFilterConfig->QueryInterface(IID_IVMRMixerControl, (void **) &m_pIMixerControl);
|
|
// if IMixerControl is exposed, the VMR is in mixing mode
|
|
if (S_OK == hr && m_pIMixerControl)
|
|
{
|
|
// if this is the first time, select the last pin connected because this will be highest in the z-order
|
|
m_CurPin = m_dwNumPins - 1;
|
|
InitConfigControls(m_CurPin);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
InitConfigControls(m_CurPin);
|
|
}
|
|
|
|
BOOL bSet = SetDlgItemInt(m_Dlg, IDC_NUMPINS, m_dwNumPins, 0);
|
|
ASSERT(bSet);
|
|
|
|
// Set the range of the spin control
|
|
HWND hSpin = GetDlgItem(m_Dlg, IDC_PINSPIN);
|
|
if(hSpin)
|
|
{
|
|
SendMessage(hSpin, UDM_SETRANGE32, 1, 16);
|
|
}
|
|
return NOERROR;
|
|
} // OnActivate
|
|
|
|
|
|
|
|
//
|
|
// InitConfigControls
|
|
//
|
|
// Enable and update the content of the configuration controls .
|
|
//
|
|
void CVMRFilterConfigProp::InitConfigControls(DWORD pin)
|
|
{
|
|
// If this call fails, the pins are not connected or there is no mixing control.
|
|
if (FAILED(UpdateMixingData(pin)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Populate Combo List Box and Enable Pin Config Controls
|
|
//
|
|
CComPtr<IBaseFilter> pFilter;
|
|
HRESULT hr = m_pIFilterConfig->QueryInterface(IID_IBaseFilter, (void **) &pFilter);
|
|
if (FAILED(hr) || !pFilter)
|
|
{
|
|
return;
|
|
}
|
|
CComPtr<IEnumPins> pEnum;
|
|
hr = pFilter->EnumPins(&pEnum);
|
|
if (FAILED(hr) || !pEnum)
|
|
{
|
|
return;
|
|
}
|
|
HWND hCtl = GetDlgItem(m_Dlg, IDC_PINSELECT);
|
|
ComboBox_ResetContent(GetDlgItem(m_Dlg, IDC_PINSELECT));
|
|
pEnum->Reset();
|
|
IPin * pPin;
|
|
PIN_INFO Info;
|
|
TCHAR szPinName[255]; // pin names are 32 characters or less. This should be sufficient for a long time to come.
|
|
while (S_OK == pEnum->Next(1, &pPin, NULL))
|
|
{
|
|
hr = pPin->QueryPinInfo(&Info);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
#ifdef UNICODE
|
|
_tcscpy(szPinName, Info.achName);
|
|
#else
|
|
WideCharToMultiByte(CP_ACP, NULL, Info.achName, -1, szPinName, 255, NULL, NULL);
|
|
#endif
|
|
ComboBox_AddString(GetDlgItem(m_Dlg, IDC_PINSELECT), szPinName);
|
|
pPin->Release();
|
|
Info.pFilter->Release();
|
|
}
|
|
}
|
|
|
|
ComboBox_SetCurSel(GetDlgItem(m_Dlg, IDC_PINSELECT), pin); // select the pin
|
|
ComboBox_Enable(GetDlgItem(m_Dlg, IDC_PINSELECT), TRUE);
|
|
ComboBox_Enable(GetDlgItem(m_Dlg, IDC_XPOS_SLIDER), TRUE);
|
|
ComboBox_Enable(GetDlgItem(m_Dlg, IDC_YPOS_SLIDER), TRUE);
|
|
ComboBox_Enable(GetDlgItem(m_Dlg, IDC_XSIZE_SLIDER), TRUE);
|
|
ComboBox_Enable(GetDlgItem(m_Dlg, IDC_YSIZE_SLIDER), TRUE);
|
|
ComboBox_Enable(GetDlgItem(m_Dlg, IDC_ALPHA_SLIDER), TRUE);
|
|
|
|
// Initialize the sliders
|
|
HWND hwndT;
|
|
int pos;
|
|
TCHAR sz[32];
|
|
|
|
hwndT = GetDlgItem(m_Dlg, IDC_XPOS_SLIDER );
|
|
pos = int(1000 * m_XPos) + 1000;
|
|
SendMessage(hwndT, TBM_SETRANGE, TRUE, MAKELONG(0, (WORD)(2000)));
|
|
SendMessage(hwndT, TBM_SETPOS, TRUE, (LPARAM)(pos));
|
|
_stprintf(sz, TEXT("%.3f"), m_XPos);
|
|
SetDlgItemText(m_Dlg, IDC_XPOS, sz);
|
|
|
|
pos = int(1000 * m_YPos) + 1000;
|
|
hwndT = GetDlgItem(m_Dlg, IDC_YPOS_SLIDER );
|
|
SendMessage(hwndT, TBM_SETRANGE, TRUE, MAKELONG(0, (WORD)(2000)));
|
|
SendMessage(hwndT, TBM_SETPOS, TRUE, (LPARAM)(pos));
|
|
_stprintf(sz, TEXT("%.3f"), m_YPos);
|
|
SetDlgItemText(m_Dlg, IDC_YPOS, sz);
|
|
|
|
pos = int(1000 * m_XSize) + 1000;
|
|
hwndT = GetDlgItem(m_Dlg, IDC_XSIZE_SLIDER );
|
|
SendMessage(hwndT, TBM_SETRANGE, TRUE, MAKELONG(0, (WORD)(2000)));
|
|
SendMessage(hwndT, TBM_SETPOS, TRUE, (LPARAM)(pos));
|
|
_stprintf(sz, TEXT("%.3f"), m_XSize);
|
|
SetDlgItemText(m_Dlg, IDC_XSIZE, sz);
|
|
|
|
pos = int(1000 * m_YSize) + 1000;
|
|
hwndT = GetDlgItem(m_Dlg, IDC_YSIZE_SLIDER );
|
|
SendMessage(hwndT, TBM_SETRANGE, TRUE, MAKELONG(0, (WORD)(2000)));
|
|
SendMessage(hwndT, TBM_SETPOS, TRUE, (LPARAM)(pos));
|
|
_stprintf(sz, TEXT("%.3f"), m_YSize);
|
|
SetDlgItemText(m_Dlg, IDC_YSIZE, sz);
|
|
|
|
pos = int(1000 * m_Alpha);
|
|
hwndT = GetDlgItem(m_Dlg, IDC_ALPHA_SLIDER );
|
|
SendMessage(hwndT, TBM_SETRANGE, TRUE, MAKELONG(0, (WORD)(1000)));
|
|
SendMessage(hwndT, TBM_SETPOS, TRUE, (LPARAM)(pos));
|
|
_stprintf(sz, TEXT("%.3f"), m_Alpha);
|
|
SetDlgItemText(m_Dlg, IDC_ALPHA, sz);
|
|
}// InitConfigControls
|
|
|
|
|
|
//
|
|
// OnHScroll
|
|
//
|
|
// Handles the scroll messages for our property window
|
|
//
|
|
void CVMRFilterConfigProp::OnHScroll(HWND hwnd, HWND hwndCtrl, UINT code, int pos)
|
|
{
|
|
ASSERT(m_pIMixerControl);
|
|
|
|
TCHAR sz[32];
|
|
|
|
if (GetDlgItem(m_Dlg, IDC_ALPHA_SLIDER ) == hwndCtrl) {
|
|
pos = (int)SendMessage(hwndCtrl, TBM_GETPOS, 0, 0);
|
|
m_Alpha = (FLOAT)pos / 1000.0F;
|
|
UpdatePinAlpha(m_CurPin);
|
|
_stprintf(sz, TEXT("%.3f"), m_Alpha);
|
|
SetDlgItemText(m_Dlg, IDC_ALPHA, sz);
|
|
}
|
|
else if (GetDlgItem(m_Dlg, IDC_XPOS_SLIDER ) == hwndCtrl) {
|
|
pos = (int)SendMessage(hwndCtrl, TBM_GETPOS, 0, 0);
|
|
m_XPos = ((FLOAT)pos - 1000.0F) / 1000.0F;
|
|
UpdatePinPos(m_CurPin);
|
|
_stprintf(sz, TEXT("%.3f"), m_XPos);
|
|
SetDlgItemText(m_Dlg, IDC_XPOS, sz);
|
|
}
|
|
else if (GetDlgItem(m_Dlg, IDC_YPOS_SLIDER ) == hwndCtrl) {
|
|
pos = (int)SendMessage(hwndCtrl, TBM_GETPOS, 0, 0);
|
|
m_YPos = ((FLOAT)pos - 1000.0F) / 1000.0F;
|
|
UpdatePinPos(m_CurPin);
|
|
_stprintf(sz, TEXT("%.3f"), m_YPos);
|
|
SetDlgItemText(m_Dlg, IDC_YPOS, sz);
|
|
}
|
|
else if (GetDlgItem(m_Dlg, IDC_XSIZE_SLIDER ) == hwndCtrl) {
|
|
pos = (int)SendMessage(hwndCtrl, TBM_GETPOS, 0, 0);
|
|
m_XSize = ((FLOAT)pos - 1000.0F) / 1000.0F;
|
|
UpdatePinPos(m_CurPin);
|
|
_stprintf(sz, TEXT("%.3f"), m_XSize);
|
|
SetDlgItemText(m_Dlg, IDC_XSIZE, sz);
|
|
}
|
|
else if (GetDlgItem(m_Dlg, IDC_YSIZE_SLIDER ) == hwndCtrl) {
|
|
pos = (int)SendMessage(hwndCtrl, TBM_GETPOS, 0, 0);
|
|
m_YSize = ((FLOAT)pos - 1000.0F) / 1000.0F;
|
|
UpdatePinPos(m_CurPin);
|
|
_stprintf(sz, TEXT("%.3f"), m_YSize);
|
|
SetDlgItemText(m_Dlg, IDC_YSIZE, sz);
|
|
}
|
|
} // OnHScroll
|
|
|
|
|
|
//
|
|
// UpdatePinAlpha
|
|
//
|
|
// Update the alpha value of a stream
|
|
//
|
|
void CVMRFilterConfigProp::UpdatePinAlpha(DWORD dwStreamID)
|
|
{
|
|
if (m_pIMixerControl)
|
|
{
|
|
m_pIMixerControl->SetAlpha(dwStreamID, m_Alpha);
|
|
}
|
|
} // UpdatePinAlpha
|
|
|
|
|
|
//
|
|
// UpdatePinPos
|
|
//
|
|
// Update the position rectangle of a stream
|
|
//
|
|
void CVMRFilterConfigProp::UpdatePinPos(DWORD dwStreamID)
|
|
{
|
|
NORMALIZEDRECT r = {m_XPos, m_YPos, m_XPos + m_XSize, m_YPos + m_YSize};
|
|
|
|
if (m_pIMixerControl)
|
|
{
|
|
m_pIMixerControl->SetOutputRect(dwStreamID, &r);
|
|
}
|
|
} // UpdatePinPos
|
|
|
|
|
|
//
|
|
// UpdateMixingData
|
|
//
|
|
// Query the filter for the current alpha value and position of a stream
|
|
//
|
|
HRESULT CVMRFilterConfigProp::UpdateMixingData(DWORD dwStreamID)
|
|
{
|
|
NORMALIZEDRECT r;
|
|
|
|
if (m_pIMixerControl)
|
|
{
|
|
HRESULT hr = m_pIMixerControl->GetOutputRect(dwStreamID, &r);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
m_XPos = r.left;
|
|
m_YPos = r.top;
|
|
m_XSize = r.right - r.left;
|
|
m_YSize = r.bottom - r.top;
|
|
|
|
return m_pIMixerControl->GetAlpha(dwStreamID, &m_Alpha);
|
|
}
|
|
return E_NOINTERFACE;
|
|
} // UpdateMixingData
|
|
|
|
|
|
//
|
|
// Data types and macros used for image capture
|
|
//
|
|
typedef LPBITMAPINFOHEADER PDIB;
|
|
|
|
#define BFT_BITMAP 0x4d42 /* 'BM' */
|
|
#define DibNumColors(lpbi) ((lpbi)->biClrUsed == 0 && (lpbi)->biBitCount <= 8 \
|
|
? (int)(1 << (int)(lpbi)->biBitCount) \
|
|
: (int)(lpbi)->biClrUsed)
|
|
|
|
#define DibSize(lpbi) ((lpbi)->biSize + (lpbi)->biSizeImage + \
|
|
(int)(lpbi)->biClrUsed * sizeof(RGBQUAD))
|
|
|
|
#define DibPaletteSize(lpbi) (DibNumColors(lpbi) * sizeof(RGBQUAD))
|
|
|
|
|
|
//
|
|
// SaveCapturedImage
|
|
//
|
|
// Save a captured image (bitmap) to a file
|
|
//
|
|
bool CVMRFilterConfigProp::SaveCapturedImage(TCHAR* szFile, BYTE* lpCurrImage)
|
|
{
|
|
|
|
BITMAPFILEHEADER hdr;
|
|
DWORD dwSize;
|
|
PDIB pdib = (PDIB)lpCurrImage;
|
|
|
|
//fh = OpenFile(szFile,&of,OF_CREATE|OF_READWRITE);
|
|
HANDLE hFile = CreateFile(szFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if (INVALID_HANDLE_VALUE == hFile)
|
|
return FALSE;
|
|
|
|
dwSize = DibSize(pdib);
|
|
|
|
hdr.bfType = BFT_BITMAP;
|
|
hdr.bfSize = dwSize + sizeof(BITMAPFILEHEADER);
|
|
hdr.bfReserved1 = 0;
|
|
hdr.bfReserved2 = 0;
|
|
hdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + pdib->biSize +
|
|
DibPaletteSize(pdib);
|
|
|
|
DWORD dwWritten;
|
|
WriteFile(hFile, (LPVOID)&hdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);
|
|
if (sizeof(BITMAPFILEHEADER) != dwWritten)
|
|
return FALSE;
|
|
WriteFile(hFile, (LPVOID)pdib, dwSize, &dwWritten, NULL);
|
|
if (dwSize != dwWritten)
|
|
return FALSE;
|
|
|
|
CloseHandle(hFile);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// CaptureCurrentImage
|
|
//
|
|
// Captures the current VMR image and save it to a file
|
|
//
|
|
void CVMRFilterConfigProp::CaptureCurrentImage(void)
|
|
{
|
|
IBasicVideo* iBV;
|
|
BYTE* lpCurrImage = NULL;
|
|
|
|
HRESULT hr = m_pIFilterConfig->QueryInterface(IID_IBasicVideo, (LPVOID*)&iBV);
|
|
if (SUCCEEDED(hr)) {
|
|
LONG BuffSize = 0;
|
|
hr = iBV->GetCurrentImage(&BuffSize, NULL);
|
|
if (SUCCEEDED(hr)) {
|
|
lpCurrImage = new BYTE[BuffSize];
|
|
if (lpCurrImage) {
|
|
hr = iBV->GetCurrentImage(&BuffSize, (long*)lpCurrImage);
|
|
if (FAILED(hr)) {
|
|
delete lpCurrImage;
|
|
lpCurrImage = NULL;
|
|
}
|
|
}
|
|
}
|
|
} // QI
|
|
|
|
if (lpCurrImage) {
|
|
// Get the path to the My Pictures folder. Create it if it doesn't exist.
|
|
// If we can't get it, don't use a path. Picture will then be saved in
|
|
// current working directory.
|
|
TCHAR tszPath[MAX_PATH];
|
|
if (!SHGetSpecialFolderPath(NULL, tszPath, CSIDL_MYPICTURES, TRUE))
|
|
{
|
|
tszPath[0]=TEXT('\0');
|
|
}
|
|
|
|
DWORD dwTime = timeGetTime();
|
|
|
|
TCHAR szFile[MAX_PATH];
|
|
wsprintf(szFile, TEXT("%s\\VMRImage%X.bmp"), tszPath, dwTime);
|
|
SaveCapturedImage(szFile, lpCurrImage);
|
|
|
|
delete lpCurrImage;
|
|
}
|
|
|
|
if (iBV) {
|
|
iBV->Release();
|
|
}
|
|
}
|