//==========================================================================; // // 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 #include #include #include #include #include // for SHGetSpecialFolderPath #include "resource.h" #ifdef FILTER_DLL #include #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 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 pFilter; HRESULT hr = m_pIFilterConfig->QueryInterface(IID_IBaseFilter, (void **) &pFilter); if (FAILED(hr) || !pFilter) { return; } CComPtr 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(); } }