Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

1629 lines
33 KiB

// File: videview.cpp
#include "precomp.h"
#include "vidview.h"
#include "resource.h"
#include "confman.h"
#include "rtoolbar.h"
#include "pfndrawd.h"
#include "NmManager.h"
#include "cmd.h"
#define DibHdrSize(lpbi) ((lpbi)->biSize + (int)(lpbi)->biClrUsed * sizeof(RGBQUAD))
#define DibDataSize(lpbi) ((lpbi)->biSizeImage)
#define DibSize(lpbi) (DibHdrSize(lpbi) + DibDataSize(lpbi))
typedef struct
{
DWORD *pdwCapDevIDs;
LPTSTR pszCapDevNames;
DWORD dwNumCapDev;
} ENUM_CAP_DEV;
CSimpleArray<CVideoWindow *> *CVideoWindow::g_pVideos;
BOOL CVideoWindow::g_fMirror = FALSE;
BOOL CVideoWindow::g_bLocalOneTimeInited = FALSE;
static const TCHAR REGVAL_VIDEO_QUALITY[] = TEXT("ImageQuality");
static const UINT VIDEO_ZOOM_MIN = 50;
static const UINT VIDEO_ZOOM_MAX = 400;
static const UINT QCIF_WIDTH = 176;
static const UINT QCIF_HEIGHT = 144;
CVideoWindow::CVideoWindow(VideoType eType, BOOL bEmbedded) :
m_dwFrameSize(NM_VIDEO_MEDIUM),
m_hdd(NULL),
#ifdef DISPLAYFPS
m_cFrame (0),
m_dwTick (GetTickCount()),
#endif // DISPLAYFPS
m_pActiveChannel(NULL),
m_dwCookie(0),
m_dwImageQuality(NM_VIDEO_MIN_QUALITY),
m_pNotify(NULL),
m_fLocal(REMOTE!=eType),
m_hBitmapMirror(NULL),
m_hDCMirror(NULL),
m_fZoomable(TRUE),
m_bEmbedded(bEmbedded),
m_hGDIObj(NULL)
{
if (NULL == g_pVideos)
{
g_pVideos = new CSimpleArray<CVideoWindow*>;
}
if (NULL != g_pVideos)
{
CVideoWindow* p = static_cast<CVideoWindow*>(this);
g_pVideos->Add(p);
}
m_sizeVideo.cx = 0;
m_sizeVideo.cy = 0;
RegEntry reAudio(AUDIO_KEY); // HKCU
RegEntry reVideo( IsLocal() ? VIDEO_LOCAL_KEY : VIDEO_REMOTE_KEY,
HKEY_CURRENT_USER );
DWORD dwFrameSizeDefault = FRAME_QCIF;
DWORD dwImageQualityDefault = NM_VIDEO_DEFAULT_QUALITY;
int nVideoWidthDefault = VIDEO_WIDTH_QCIF;
int nVideoHeightDefault = VIDEO_HEIGHT_QCIF;
UINT uBandWidth = reAudio.GetNumber(REGVAL_TYPICALBANDWIDTH, BW_DEFAULT);
if (uBandWidth == BW_144KBS)
{
dwImageQualityDefault = NM_VIDEO_MIN_QUALITY;
}
m_dwFrameSize = reVideo.GetNumber(
REGVAL_VIDEO_FRAME_SIZE, dwFrameSizeDefault );
if (!IsLocal())
{
m_dwImageQuality = reVideo.GetNumber(
REGVAL_VIDEO_QUALITY, dwImageQualityDefault );
}
m_nXferOnConnect =
IsLocal() ? VIDEO_SEND_CONNECT_DEFAULT :
VIDEO_RECEIVE_CONNECT_DEFAULT;
m_nXferOnConnect = reVideo.GetNumber(
REGVAL_VIDEO_XFER_CONNECT,
m_nXferOnConnect);
m_zoom = 100;
}
VOID CVideoWindow::OnNCDestroy()
{
// remote channel will get released upon the NM_CHANNEL_REMOVED
// notification. The preview channel needs to be released here,
// and not in the destructor because of a circular ref count
if (NULL != m_pActiveChannel)
{
NmUnadvise(m_pActiveChannel, IID_INmChannelVideoNotify, m_dwCookie);
m_dwCookie = 0;
m_pActiveChannel->Release();
m_pActiveChannel = NULL;
}
if (NULL != m_pNotify)
{
m_pNotify->Release();
m_pNotify = NULL;
}
}
CVideoWindow::~CVideoWindow()
{
CVideoWindow* p = static_cast<CVideoWindow*>(this);
g_pVideos->Remove(p);
if (0 == g_pVideos->GetSize())
{
delete g_pVideos;
g_pVideos = NULL;
}
if (NULL != m_hdd)
{
DRAWDIB::DrawDibClose(m_hdd);
}
// BUGBUG PhilF: Does DrawDibClose() nuke the selected palette?
// release resources related to mirror preview
UnInitMirroring();
}
BOOL CVideoWindow::Create(HWND hwndParent, HPALETTE hpal, IVideoChange *pNotify)
{
if (FAILED(DRAWDIB::Init()))
{
return(FALSE);
}
if (!CGenWindow::Create(
hwndParent,
0,
IsLocal() ? TEXT("NMLocalVideo") : TEXT("NMRemoteVideo"),
WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
WS_EX_CLIENTEDGE))
{
return(FALSE);
}
SetWindowPos(GetWindow(), NULL, 0, 0, m_sizeVideo.cx, m_sizeVideo.cy,
SWP_NOZORDER|SWP_NOACTIVATE);
// We do NOT own this palette
m_hPal = hpal;
m_hdd = DRAWDIB::DrawDibOpen();
if (NULL != m_hdd)
{
// Use the Indeo palette in 8 bit mode only if the decompressed
// video data is H.261 or H.263. We will create this palette as
// an identity palette.
// PhilF: If the user is utilizing an installable codec, do we still want to do this?
// Update the palette in the DrawDib surface
if (NULL != hpal)
{
DRAWDIB::DrawDibSetPalette(m_hdd, hpal);
}
}
if (NULL != pNotify)
{
m_pNotify = pNotify;
m_pNotify->AddRef();
}
if (IsLocal())
{
// need to get PreviewChannel;
INmManager2 *pManager = CConfMan::GetNmManager();
ASSERT (NULL != pManager);
pManager->GetPreviewChannel(&m_pActiveChannel);
pManager->Release();
if (m_pActiveChannel)
{
NmAdvise(m_pActiveChannel, static_cast<INmChannelVideoNotify*>(this),
IID_INmChannelVideoNotify, &m_dwCookie);
DWORD_PTR dwFrameSize = GetFrameSize();
if (g_bLocalOneTimeInited)
{
m_pActiveChannel->GetProperty(NM_VIDPROP_IMAGE_PREFERRED_SIZE, &dwFrameSize);
}
else
{
DWORD dwSizes = GetFrameSizes();
// if frame size is not valid
if (!(m_dwFrameSize & dwSizes))
{
// find an alternate size
if (FRAME_QCIF & dwSizes)
{
dwFrameSize = FRAME_QCIF;
}
else if (FRAME_SQCIF & dwSizes)
{
dwFrameSize = FRAME_SQCIF;
}
else if (FRAME_CIF & dwSizes)
{
dwFrameSize = FRAME_CIF;
}
}
RegEntry reVideo( IsLocal() ? VIDEO_LOCAL_KEY : VIDEO_REMOTE_KEY,
HKEY_CURRENT_USER );
SetMirror(reVideo.GetNumber(REGVAL_VIDEO_MIRROR, TRUE));
g_bLocalOneTimeInited = TRUE;
}
SetFrameSize((DWORD)dwFrameSize);
}
}
ResizeWindowsToFrameSize();
return TRUE;
}
DWORD CVideoWindow::GetFrameSizes()
{
DWORD_PTR dwSizes = 0;
if (NULL != m_pActiveChannel)
{
m_pActiveChannel->GetProperty(NM_VIDPROP_IMAGE_SIZES, &dwSizes);
}
return (DWORD)dwSizes;
}
VOID CVideoWindow::SetFrameSize(DWORD dwSize)
{
DWORD dwPrevSize = m_dwFrameSize;
m_dwFrameSize = dwSize;
if (IsLocal())
{
if (NULL != m_pActiveChannel)
{
m_pActiveChannel->SetProperty(NM_VIDPROP_IMAGE_PREFERRED_SIZE, dwSize);
}
}
if (dwPrevSize != dwSize)
{
if ((NULL == m_pActiveChannel) ||
(!IsLocal() && (S_OK != m_pActiveChannel->IsActive())) ||
(IsLocal() && IsPaused()))
{
ResizeWindowsToFrameSize();
}
}
}
VOID CVideoWindow::ResizeWindowsToFrameSize()
{
SIZE size;
switch(m_dwFrameSize)
{
case FRAME_SQCIF:
size.cx = VIDEO_WIDTH_SQCIF;
size.cy = VIDEO_HEIGHT_SQCIF;
break;
case FRAME_CIF:
if (IsLocal())
{
size.cx = VIDEO_WIDTH_CIF;
size.cy = VIDEO_HEIGHT_CIF;
break;
}
// else fall through to QCIF
case FRAME_QCIF:
default:
size.cx = VIDEO_WIDTH_QCIF;
size.cy = VIDEO_HEIGHT_QCIF;
break;
}
SetVideoSize(&size);
}
STDMETHODIMP CVideoWindow::QueryInterface(REFIID riid, PVOID *ppv)
{
HRESULT hr = S_OK;
if ((riid == IID_INmChannelVideoNotify) || (riid == IID_IUnknown))
{
*ppv = static_cast<INmChannelVideoNotify *>(this);
DbgMsgApi("CVideoWindow::QueryInterface()");
}
else
{
hr = E_NOINTERFACE;
*ppv = NULL;
DbgMsgApi("CVideoWindow::QueryInterface(): Called on unknown interface.");
}
if (S_OK == hr)
{
AddRef();
}
return hr;
}
STDMETHODIMP CVideoWindow::NmUI(CONFN uNotify)
{
return S_OK;
}
STDMETHODIMP CVideoWindow::MemberChanged(NM_MEMBER_NOTIFY uNotify, INmMember *pMember)
{
return S_OK;
}
STDMETHODIMP CVideoWindow::StateChanged(NM_VIDEO_STATE uState)
{
InvalidateRect(GetWindow(), NULL, TRUE);
if (NULL != m_pNotify)
{
m_pNotify->StateChange(this, uState);
}
CNmManagerObj::VideoChannelStateChanged(uState, !m_fLocal);
return S_OK;
}
STDMETHODIMP CVideoWindow::PropertyChanged(DWORD dwReserved)
{
if (NM_VIDPROP_FRAME == dwReserved)
{
OnFrameAvailable();
}
return S_OK;
}
HRESULT CVideoWindow::OnChannelChanged(NM_CHANNEL_NOTIFY uNotify, INmChannel *pChannel)
{
INmChannelVideo* pChannelVideo;
if (SUCCEEDED(pChannel->QueryInterface(IID_INmChannelVideo, (void**)&pChannelVideo)))
{
BOOL bIncoming = (S_OK == pChannelVideo->IsIncoming());
if ((bIncoming && !IsLocal()) || (!bIncoming && IsLocal()))
{
switch (uNotify)
{
case NM_CHANNEL_ADDED:
if (NULL == m_pActiveChannel)
{
pChannelVideo->AddRef();
m_pActiveChannel = pChannelVideo;
NmAdvise(m_pActiveChannel,static_cast<INmChannelVideoNotify*>(this),
IID_INmChannelVideoNotify, &m_dwCookie);
SetImageQuality(m_dwImageQuality);
}
if (!_Module.InitControlMode())
{
switch(m_nXferOnConnect)
{
case VIDEO_XFER_START:
if (IsPaused())
{
Pause(FALSE);
}
break;
case VIDEO_XFER_STOP:
Pause(TRUE);
break;
default:
if (!IsLocal())
{
Pause(TRUE);
}
break;
}
}
break;
case NM_CHANNEL_REMOVED:
if (!IsLocal() && (pChannel == m_pActiveChannel))
{
NmUnadvise(m_pActiveChannel, IID_INmChannelVideoNotify, m_dwCookie);
m_pActiveChannel->Release();
m_pActiveChannel = NULL;
m_dwCookie = 0;
ResizeWindowsToFrameSize();
}
}
// just in case we missed a notification
// (in final version this should not be the case)
// m_VideoWindow.OnStateChange();
}
pChannelVideo->Release();
}
return S_OK;
}
HRESULT CVideoWindow::SetImageQuality(DWORD dwQuality)
{
HRESULT hr = E_FAIL;
m_dwImageQuality = dwQuality;
if (NULL != m_pActiveChannel)
{
hr = m_pActiveChannel->SetProperty(NM_VIDPROP_IMAGE_QUALITY, dwQuality);
CNmManagerObj::VideoPropChanged(NM_VIDPROP_IMAGE_QUALITY, !IsLocal());
}
return hr;
}
HRESULT CVideoWindow::SetCameraDialog(ULONG ul)
{
if(IsLocal())
{
return m_pActiveChannel->SetProperty(NM_VIDPROP_CAMERA_DIALOG, ul);
CNmManagerObj::VideoPropChanged(NM_VIDPROP_CAMERA_DIALOG, IsLocal());
}
return E_FAIL;
}
HRESULT CVideoWindow::GetCameraDialog(ULONG* pul)
{
HRESULT hr;
DWORD_PTR dwPropVal;
if(IsLocal())
{
hr = m_pActiveChannel->GetProperty(NM_VIDPROP_CAMERA_DIALOG, &dwPropVal);
*pul = (ULONG)dwPropVal;
return hr;
}
return E_FAIL;
}
BOOL CVideoWindow::IsXferAllowed()
{
if (IsLocal())
{
return FIsSendVideoAllowed() && (NULL != m_pActiveChannel);
}
else
{
return FIsReceiveVideoAllowed();
}
}
BOOL CVideoWindow::IsAutoXferEnabled()
{
return(VIDEO_XFER_START == m_nXferOnConnect);
}
BOOL CVideoWindow::IsXferEnabled()
{
if (NULL != m_pActiveChannel)
{
NM_VIDEO_STATE state;
if (SUCCEEDED(m_pActiveChannel->GetState(&state)))
{
switch (state)
{
case NM_VIDEO_PREVIEWING:
case NM_VIDEO_TRANSFERRING:
case NM_VIDEO_REMOTE_PAUSED:
return TRUE;
case NM_VIDEO_IDLE:
case NM_VIDEO_LOCAL_PAUSED:
case NM_VIDEO_BOTH_PAUSED:
default:
return FALSE;
}
}
}
return FALSE;
}
HRESULT CVideoWindow::GetVideoState(NM_VIDEO_STATE* puState)
{
if(m_pActiveChannel)
{
return m_pActiveChannel->GetState(puState);
}
return E_FAIL;
}
VOID CVideoWindow::Pause(BOOL fPause)
{
if (NULL != m_pActiveChannel)
{
m_pActiveChannel->SetProperty(NM_VIDPROP_PAUSE, (ULONG)fPause);
CNmManagerObj::VideoPropChanged(NM_VIDPROP_PAUSE, !IsLocal());
}
}
BOOL CVideoWindow::IsPaused()
{
if (NULL != m_pActiveChannel)
{
ULONG_PTR uPause;
if (SUCCEEDED(m_pActiveChannel->GetProperty(NM_VIDPROP_PAUSE, &uPause)))
{
return (BOOL)uPause;
}
}
return TRUE;
}
BOOL CVideoWindow::IsConnected()
{
if (NULL != m_pActiveChannel)
{
NM_VIDEO_STATE state;
if (SUCCEEDED(m_pActiveChannel->GetState(&state)))
{
switch (state)
{
case NM_VIDEO_LOCAL_PAUSED:
case NM_VIDEO_TRANSFERRING:
case NM_VIDEO_BOTH_PAUSED:
case NM_VIDEO_REMOTE_PAUSED:
return TRUE;
case NM_VIDEO_IDLE:
case NM_VIDEO_PREVIEWING:
default:
return FALSE;
}
}
}
return FALSE;
}
DWORD CVideoWindow::GetNumCapDev()
{
DWORD_PTR dwNumDevs = 0;
if (NULL != m_pActiveChannel)
{
m_pActiveChannel->GetProperty(NM_VIDPROP_NUM_CAPTURE_DEVS, &dwNumDevs);
}
return (DWORD)dwNumDevs;
}
DWORD CVideoWindow::GetMaxCapDevNameLen()
{
DWORD_PTR dwLen = 0;
if (NULL != m_pActiveChannel)
{
m_pActiveChannel->GetProperty(NM_VIDPROP_MAX_CAPTURE_NAME, &dwLen);
}
return (DWORD)dwLen;
}
DWORD CVideoWindow::GetCurrCapDevID()
{
DWORD_PTR dwID = 0;
if (NULL != m_pActiveChannel)
{
m_pActiveChannel->GetProperty(NM_VIDPROP_CAPTURE_DEV_ID, &dwID);
}
return (DWORD)dwID;
}
VOID CVideoWindow::SetCurrCapDevID(DWORD dwID)
{
if (NULL != m_pActiveChannel)
{
m_pActiveChannel->SetProperty(NM_VIDPROP_CAPTURE_DEV_ID, dwID);
InvalidateRect(GetWindow(), NULL, TRUE);
}
}
VOID CVideoWindow::EnableAutoXfer(BOOL fEnable)
{
m_nXferOnConnect = fEnable ? VIDEO_XFER_START : VIDEO_XFER_NOP;
RegEntry reXfer( IsLocal() ? VIDEO_LOCAL_KEY : VIDEO_REMOTE_KEY,
HKEY_CURRENT_USER );
reXfer.SetValue ( REGVAL_VIDEO_XFER_CONNECT, m_nXferOnConnect);
}
VOID CVideoWindow::EnumCapDev(DWORD *pdwCapDevIDs, LPTSTR pszCapDevNames, DWORD dwNumCapDev)
{
ENUM_CAP_DEV enumCapDev;
enumCapDev.pdwCapDevIDs = pdwCapDevIDs;
enumCapDev.pszCapDevNames = pszCapDevNames;
enumCapDev.dwNumCapDev = dwNumCapDev;
if (NULL != m_pActiveChannel)
{
m_pActiveChannel->GetProperty(NM_VIDPROP_CAPTURE_LIST, (DWORD_PTR *)&enumCapDev);
}
}
VOID CVideoWindow::GetDesiredSize(SIZE *ppt)
{
ppt->cx = m_sizeVideo.cx*m_zoom/100;
ppt->cy = m_sizeVideo.cy*m_zoom/100;
if (m_bEmbedded)
{
ppt->cx = min(ppt->cx, QCIF_WIDTH);
ppt->cy = min(ppt->cy, QCIF_HEIGHT);
}
SIZE sGen;
CGenWindow::GetDesiredSize(&sGen);
ppt->cx += sGen.cx;
ppt->cy += sGen.cy;
}
VOID CVideoWindow::SetVideoSize(LPSIZE lpsize)
{
m_sizeVideo = *lpsize;
OnDesiredSizeChanged();
}
#ifdef DEBUG
DWORD g_fDisplayFPS = FALSE;
#ifdef DISPLAYFPS
VOID CVideoWindow::UpdateFps(void)
{
DWORD dwTick = GetTickCount();
m_cFrame++;
// Update display every 4 seconds
if ((dwTick - m_dwTick) < 4000)
return;
TCHAR sz[32];
wsprintf(sz, "%d FPS", m_cFrame / 4);
SetWindowText(m_hwndParent, sz);
m_cFrame = 0;
m_dwTick = dwTick;
}
#endif /* DISPLAYFPS */
#endif // DEBUG
VOID CVideoWindow::OnFrameAvailable(void)
{
::InvalidateRect(GetWindow(), NULL, FALSE);
#ifdef DISPLAYFPS
if (g_fDisplayFPS)
{
UpdateFps();
}
#endif // DISPLAYFPS
}
VOID CVideoWindow::PaintDib(HDC hdc, FRAMECONTEXT *pFrame)
{
RECT rcVideo;
GetClientRect(GetWindow(), &rcVideo);
HPALETTE hpOld = NULL;
if (NULL != m_hPal)
{
hpOld = SelectPalette(hdc, m_hPal, FALSE);
RealizePalette(hdc);
}
// create the bitmap object, only if it doesn't exist
// and if the mirror bitmap object isn't the right size
if (!ShouldMirror() || !InitMirroring(rcVideo))
{
// ISSUE: (ChrisPi 2-19-97) should we use DDF_SAME_HDC?
DRAWDIB::DrawDibDraw(m_hdd,hdc,
rcVideo.left,
rcVideo.top,
RectWidth(rcVideo),
RectHeight(rcVideo),
&pFrame->lpbmi->bmiHeader,
pFrame->lpData,
pFrame->lpClipRect->left,
pFrame->lpClipRect->top,
RectWidth(pFrame->lpClipRect),
RectHeight(pFrame->lpClipRect),
0);
}
else
{
if (NULL != m_hPal)
{
SelectPalette(m_hDCMirror, m_hPal, FALSE);
RealizePalette(m_hDCMirror);
}
DRAWDIB::DrawDibDraw(m_hdd,
m_hDCMirror,
0,
0,
RectWidth(rcVideo),
RectHeight(rcVideo),
&pFrame->lpbmi->bmiHeader,
pFrame->lpData,
pFrame->lpClipRect->left,
pFrame->lpClipRect->top,
RectWidth(pFrame->lpClipRect),
RectHeight(pFrame->lpClipRect),
0);
::StretchBlt(hdc,
rcVideo.right,
rcVideo.top,
-RectWidth(rcVideo),
RectHeight(rcVideo),
m_hDCMirror,
0,
0,
RectWidth(rcVideo),
RectHeight(rcVideo),
SRCCOPY);
// HACKHACK georgep; don't worry about deselecting the palette in
// the temp DC
}
if (NULL != hpOld)
{
SelectPalette(hdc, hpOld, FALSE);
}
}
/* P A I N T L O G O */
/*-------------------------------------------------------------------------
%%Function: PaintLogo
Display the 256 color NetMeeting logo in the video window.
-------------------------------------------------------------------------*/
VOID CVideoWindow::PaintLogo(HDC hdc, UINT idbLargeLogo, UINT idbSmallLogo)
{
RECT rcVideo;
GetClientRect(GetWindow(), &rcVideo);
::FillRect(hdc, &rcVideo, (HBRUSH)::GetStockObject(WHITE_BRUSH));
// Create the memory DC
HDC hdcMem = ::CreateCompatibleDC(hdc);
if (NULL == hdcMem)
{
ERROR_OUT(("PaintLogo: Unable to CreateCompatibleDC"));
return;
}
// Load the bitmap (LoadBitmap doesn't work for 256 color images)
HANDLE hBitmap = LoadImage(::GetInstanceHandle(),
MAKEINTRESOURCE(idbLargeLogo), IMAGE_BITMAP, 0, 0,
LR_CREATEDIBSECTION);
if (NULL != hBitmap)
{
BITMAP bitmap;
::GetObject(hBitmap, sizeof(bitmap), &bitmap);
int cx = bitmap.bmWidth;
int cy = bitmap.bmHeight;
if (RectWidth(rcVideo) < cx || RectHeight(rcVideo) < cy)
{
HANDLE hNew = LoadImage(::GetInstanceHandle(),
MAKEINTRESOURCE(idbSmallLogo), IMAGE_BITMAP, 0, 0,
LR_CREATEDIBSECTION);
if (NULL != hNew)
{
DeleteObject(hBitmap);
hBitmap = hNew;
::GetObject(hBitmap, sizeof(bitmap), &bitmap);
cx = bitmap.bmWidth;
cy = bitmap.bmHeight;
}
}
HBITMAP hBmpTmp = (HBITMAP)::SelectObject(hdcMem, hBitmap);
// Select and realize the palette
HPALETTE hPalette = m_hPal;
if (NULL != hPalette)
{
SelectPalette(hdcMem, hPalette, FALSE);
RealizePalette(hdcMem);
SelectPalette(hdc, hPalette, FALSE);
RealizePalette(hdc);
}
int x = rcVideo.left + (RectWidth(rcVideo) - cx) / 2;
int y = rcVideo.top + (RectHeight(rcVideo) - cy) / 2;
::BitBlt(hdc, x, y, cx, cy, hdcMem, 0, 0, SRCCOPY);
::SelectObject(hdcMem, hBmpTmp);
::DeleteObject(hBitmap);
}
::DeleteDC(hdcMem);
}
VOID CVideoWindow::OnPaint()
{
PAINTSTRUCT ps;
HDC hdc;
hdc = ::BeginPaint(GetWindow(), &ps);
if( hdc )
{
if( RectWidth(ps.rcPaint) && RectHeight(ps.rcPaint) )
{
// This means that we have a non-zero surface area ( there may be something to paint )
DBGENTRY(CVideoWindow::ProcessPaint);
// paint the video rect
FRAMECONTEXT fc;
if ((S_OK == GetFrame(&fc)))
{
if (!IsPaused())
{
SIZE vidSize =
{
RectWidth(fc.lpClipRect),
RectHeight(fc.lpClipRect)
} ;
if ((vidSize.cx != m_sizeVideo.cx) || (vidSize.cy != m_sizeVideo.cy))
{
// save the new image size
SetVideoSize(&vidSize);
}
}
PaintDib(hdc, &fc);
ReleaseFrame(&fc);
}
else
{
PaintLogo(hdc, IDB_VIDEO_LOGO, IDB_VIDEO_LOGO_SMALL);
}
// check to see if needs painting outside the video rect
#if FALSE
// Currently just stretching the video to the window size
if (ps.rcPaint.left < m_rcVideo.left ||
ps.rcPaint.top < m_rcVideo.top ||
ps.rcPaint.right > m_rcVideo.right ||
ps.rcPaint.bottom > m_rcVideo.bottom)
{
HFONT hfOld;
int nBkModeOld;
RECT rc, rcClient;
::GetClientRect(GetWindow(), &rcClient);
// erase the background if requested
if (ps.fErase)
{
::ExcludeClipRect(hdc,
m_rcVideo.left,
m_rcVideo.top,
m_rcVideo.right,
m_rcVideo.bottom);
::FillRect(hdc, &rcClient, ::GetSysColorBrush(COLOR_BTNFACE));
}
nBkModeOld = ::SetBkMode(hdc, TRANSPARENT);
hfOld = (HFONT)::SelectObject(hdc, g_hfontDlg);
// paint the status text
// first erase the old text if not already done
if (!ps.fErase)
{
::FillRect(hdc, &m_rcStatusText, ::GetSysColorBrush(COLOR_BTNFACE));
}
TCHAR szState[MAX_PATH];
if (GetState(szState, CCHMAX(szState)))
{
COLORREF crOld;
crOld = ::SetTextColor(hdc, ::GetSysColor(COLOR_BTNTEXT));
rc = m_rcStatusText;
rc.left += STATUS_MARGIN;
::DrawText(hdc,
szState,
-1,
&rc,
DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX);
::SetTextColor(hdc, crOld);
}
// paint border around video
rc = m_rcVideo;
::InflateRect(&rc,
::GetSystemMetrics(SM_CXEDGE),
::GetSystemMetrics(SM_CYEDGE));
::DrawEdge(hdc, &rc, EDGE_SUNKEN , BF_RECT);
// restore DC stuff
::SelectObject(hdc, hfOld);
::SetBkMode(hdc, nBkModeOld);
}
#endif // FALSE
DBGEXIT(CVideoWindow::ProcessPaint);
}
::EndPaint(GetWindow(), &ps);
}
}
VOID CVideoWindow::SetZoom(UINT zoom)
{
if (m_zoom == zoom)
{
return;
}
m_zoom = zoom;
OnDesiredSizeChanged();
}
void CVideoWindow::OnCommand(int idCmd)
{
switch (idCmd)
{
case IDM_VIDEO_GETACAMERA:
CmdLaunchWebPage(idCmd);
break;
case IDM_VIDEO_COPY:
CopyToClipboard();
break;
case IDM_VIDEO_FREEZE:
Pause(!IsPaused());
break;
case IDM_VIDEO_UNDOCK:
CMainUI::NewVideoWindow(GetConfRoom());
break;
case IDM_VIDEO_ZOOM1:
SetZoom(100);
break;
case IDM_VIDEO_ZOOM2:
SetZoom(200);
break;
case IDM_VIDEO_ZOOM3:
SetZoom(300);
break;
case IDM_VIDEO_ZOOM4:
SetZoom(400);
break;
case IDM_VIDEO_PROPERTIES:
LaunchConfCpl(GetWindow(), OPTIONS_VIDEO_PAGE);
break;
}
}
void CVideoWindow::UpdateVideoMenu(HMENU hMenu)
{
EnableMenuItem(hMenu, IDM_VIDEO_COPY, MF_BYCOMMAND|(CanCopy() ? MF_ENABLED : MF_GRAYED|MF_DISABLED));
EnableMenuItem(hMenu, IDM_VIDEO_UNDOCK, MF_BYCOMMAND|(IsLocal() ? MF_ENABLED : MF_GRAYED|MF_DISABLED));
if (!FIsSendVideoAllowed() && !FIsReceiveVideoAllowed())
{
EnableMenuItem(hMenu, IDM_VIDEO_FREEZE, MF_BYCOMMAND | MF_GRAYED | MF_DISABLED);
EnableMenuItem(hMenu, IDM_VIDEO_PROPERTIES, MF_BYCOMMAND | MF_GRAYED | MF_DISABLED);
EnableMenuItem(hMenu, IDM_VIDEO_GETACAMERA, MF_BYCOMMAND | MF_GRAYED | MF_DISABLED);
}
else
{
CheckMenuItem(hMenu, IDM_VIDEO_FREEZE, MF_BYCOMMAND|(IsPaused() ? MF_CHECKED : MF_UNCHECKED));
UINT uEnable = ::CanShellExecHttp() ? MF_ENABLED : MF_GRAYED;
::EnableMenuItem(hMenu, IDM_VIDEO_GETACAMERA, uEnable);
}
int nZoom = GetZoom();
int idZoom;
if (350 < nZoom) idZoom = IDM_VIDEO_ZOOM4;
else if (250 < nZoom) idZoom = IDM_VIDEO_ZOOM3;
else if (150 < nZoom) idZoom = IDM_VIDEO_ZOOM2;
else idZoom = IDM_VIDEO_ZOOM1;
UINT uFlags = IsZoomable() && !m_bEmbedded
? MF_ENABLED : MF_GRAYED|MF_DISABLED;
for (int id=IDM_VIDEO_ZOOM1; id<=IDM_VIDEO_ZOOM4; ++id)
{
EnableMenuItem(hMenu, id, MF_BYCOMMAND|uFlags);
CheckMenuItem(hMenu, id, MF_BYCOMMAND|MF_UNCHECKED);
}
CheckMenuItem(hMenu, idZoom, MF_BYCOMMAND|MF_CHECKED);
uFlags = CanLaunchConfCpl()
? MF_ENABLED : MF_GRAYED|MF_DISABLED;
EnableMenuItem(hMenu, IDM_VIDEO_PROPERTIES, MF_BYCOMMAND|uFlags);
}
void CVideoWindow::OnContextMenu(HWND hwnd, HWND hwndContext, UINT xPos, UINT yPos)
{
HMENU hmLoad = LoadMenu(_Module.GetModuleInstance(), MAKEINTRESOURCE(IDR_VIDEO_POPUP));
if (NULL != hmLoad)
{
HMENU hmPopup = GetSubMenu(hmLoad, 0);
ASSERT(NULL != hmPopup);
UpdateVideoMenu(hmPopup);
int idCmd = TrackPopupMenu(hmPopup, TPM_RETURNCMD|TPM_RIGHTBUTTON,
xPos, yPos, 0, hwnd, NULL);
if (0 != idCmd)
{
OnCommand(idCmd);
}
DestroyMenu(hmLoad);
}
}
LRESULT CVideoWindow::ProcessMessage(HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
switch (message)
{
HANDLE_MSG(hwnd, WM_CONTEXTMENU, OnContextMenu);
case WM_ERASEBKGND:
return 0;
case WM_PAINT:
OnPaint();
return 0;
case WM_NCDESTROY:
OnNCDestroy();
return 0;
case WM_SIZE:
// Need to redraw the whole window
InvalidateRect(GetWindow(), NULL, FALSE);
break;
default:
break;
}
return CGenWindow::ProcessMessage(hwnd, message, wParam, lParam) ;
}
#if FALSE
int CVideoWindow::GetState(LPTSTR lpszState, int nStateMax)
{
int uStringID;
NM_VIDEO_STATE state = NM_VIDEO_IDLE;
if (NULL != m_pActiveChannel)
{
m_pActiveChannel->GetState(&state);
}
if (IsLocal())
{
switch (state)
{
case NM_VIDEO_PREVIEWING:
uStringID = IDS_VIDEO_STATE_PREVIEWING;
break;
case NM_VIDEO_TRANSFERRING:
uStringID = IDS_VIDEO_STATE_SENDING;
break;
case NM_VIDEO_REMOTE_PAUSED:
uStringID = IDS_VIDEO_STATE_REMOTEPAUSED;
break;
case NM_VIDEO_LOCAL_PAUSED:
case NM_VIDEO_BOTH_PAUSED:
uStringID = IDS_VIDEO_STATE_PAUSED;
break;
case NM_VIDEO_IDLE:
default:
uStringID = IDS_VIDEO_STATE_NOTSENDING;
break;
}
}
else
{
switch (state)
{
case NM_VIDEO_TRANSFERRING:
uStringID = IDS_VIDEO_STATE_RECEIVING;
break;
case NM_VIDEO_REMOTE_PAUSED:
uStringID = IDS_VIDEO_STATE_REMOTEPAUSED;
break;
case NM_VIDEO_LOCAL_PAUSED:
case NM_VIDEO_BOTH_PAUSED:
uStringID = IDS_VIDEO_STATE_PAUSED;
break;
case NM_VIDEO_IDLE:
case NM_VIDEO_PREVIEWING:
default:
uStringID = IDS_VIDEO_STATE_NOTRECEIVING;
break;
}
}
return ::LoadString(
::GetInstanceHandle(),
uStringID,
lpszState,
nStateMax);
}
#endif // FALSE
// Return value will be TRUE if Windows can handle the format
static BOOL IsKnownDIBFormat(BITMAPINFOHEADER *pbmih)
{
if (sizeof(BITMAPINFOHEADER) != pbmih->biSize)
{
return(FALSE);
}
if (1 != pbmih->biPlanes)
{
return(FALSE);
}
int bits = pbmih->biBitCount;
int comp = pbmih->biCompression;
switch (bits)
{
case 1:
if (BI_RGB == comp)
{
return(TRUE);
}
break;
case 4:
if (BI_RGB == comp || BI_RLE4 == comp)
{
return(TRUE);
}
break;
case 8:
if (BI_RGB == comp || BI_RLE8 == comp)
{
return(TRUE);
}
break;
case 16:
if (BI_RGB == comp || BI_BITFIELDS == comp)
{
return(TRUE);
}
break;
case 24:
case 32:
if (BI_RGB == comp)
{
return(TRUE);
}
break;
default:
break;
}
return(FALSE);
}
#ifdef TryPaintDIB
BOOL CVideoWindow::CopyToClipboard()
{
FRAMECONTEXT fc;
BOOL fSuccess = FALSE;
// Get the current frame and open the clipboard
if (S_OK == GetFrame(&fc))
{
HWND hwnd = GetWindow();
if (OpenClipboard(hwnd))
{
// Allocate memory that we will be giving to the clipboard
BITMAPINFOHEADER *pbmih = &fc.lpbmi->bmiHeader;
BOOL bCopy = IsKnownDIBFormat(pbmih);
// BUGBUG georgep; I think this doesn't work for 15 or 16 bit DIBs
int nColors = pbmih->biClrUsed;
if (0 == nColors && pbmih->biBitCount <= 8)
{
nColors = 1 << pbmih->biBitCount;
}
// Special case for 16-bit bitfield bitmaps
if (16 == pbmih->biBitCount && BI_BITFIELDS == pbmih->biCompression)
{
nColors = 3;
}
int nHdrSize = pbmih->biSize + nColors * sizeof(RGBQUAD);
int bitsPer = pbmih->biBitCount;
if (!bCopy)
{
bitsPer *= pbmih->biPlanes;
if (bitsPer > 24)
{
bitsPer = 32;
}
// LAZYLAZY georgep: Skipping 16-bit format
else if (bitsPer > 8)
{
bitsPer = 24;
}
else if (bitsPer > 4)
{
bitsPer = 8;
}
else if (bitsPer > 1)
{
bitsPer = 4;
}
else
{
bitsPer = 1;
}
}
int nDataSize = bCopy ? pbmih->biSizeImage : 0;
if (0 == nDataSize)
{
// Make an uncompressed DIB
int nByteWidth = (pbmih->biWidth*bitsPer+7) / 8;
nDataSize = ((nByteWidth + 3)&~3) * pbmih->biHeight;
}
// Allocate the total memory for the DIB
HGLOBAL hCopy = GlobalAlloc(GHND, nHdrSize + nDataSize);
if (NULL != hCopy)
{
BITMAPINFO *lpvCopy = reinterpret_cast<BITMAPINFO*>(GlobalLock(hCopy));
CopyMemory(lpvCopy, pbmih, nHdrSize);
// Create a temporary DC for drawing into
HDC hdc = GetDC(hwnd);
if (NULL != hdc)
{
HDC hdcTemp = CreateCompatibleDC(hdc);
ReleaseDC(hwnd, hdc);
hdc = hdcTemp;
}
if (NULL != hdc)
{
if (bCopy)
{
if (ShouldMirror())
{
// Create a DIB section for drawing into
LPVOID pData;
HBITMAP hDIB = CreateDIBSection(hdc, lpvCopy, DIB_RGB_COLORS, &pData, NULL, 0);
if (NULL != hDIB)
{
// Draw into the DIB, and then copy the bits
HGDIOBJ hOld = SelectObject(hdc, hDIB);
RECT rc = { 0, 0, pbmih->biWidth, pbmih->biHeight };
StretchDIBits(hdc, 0, 0, pbmih->biWidth, pbmih->biHeight,
pbmih->biWidth, 0, -pbmih->biWidth, pbmih->biHeight,
fc.lpData, lpvCopy, DIB_RGB_COLORS, SRCCOPY);
CopyMemory(&lpvCopy->bmiColors[nColors], pData, nDataSize);
// Start cleaning up
SelectObject(hdc, hOld);
DeleteObject(hDIB);
}
}
else
{
CopyMemory(&lpvCopy->bmiColors[nColors], fc.lpData, nDataSize);
}
fSuccess = TRUE;
}
else
{
lpvCopy->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
lpvCopy->bmiHeader.biPlanes = 1;
lpvCopy->bmiHeader.biBitCount = bitsPer;
lpvCopy->bmiHeader.biCompression = BI_RGB;
lpvCopy->bmiHeader.biSizeImage = nDataSize;
// Create a DIB section for drawing into
LPVOID pData;
HBITMAP hDIB = CreateDIBSection(hdc, lpvCopy, DIB_RGB_COLORS, &pData, NULL, 0);
if (NULL != hDIB)
{
// Draw into the DIB, and then copy the bits
HGDIOBJ hOld = SelectObject(hdc, hDIB);
RECT rc = { 0, 0, pbmih->biWidth, pbmih->biHeight };
PaintDib(hdc, &fc, &rc);
CopyMemory(&lpvCopy->bmiColors[nColors], pData, nDataSize);
// Start cleaning up
SelectObject(hdc, hOld);
DeleteObject(hDIB);
fSuccess = TRUE;
}
}
DeleteDC(hdc);
}
GlobalUnlock(hCopy);
if (fSuccess)
{
// Set the DIB into the clipboard
EmptyClipboard();
fSuccess = (NULL != SetClipboardData(CF_DIB, (HANDLE)hCopy));
}
if (!fSuccess)
{
GlobalFree(hCopy);
}
}
CloseClipboard();
}
ReleaseFrame(&fc);
}
return fSuccess;
}
#else // TryPaintDIB
BOOL CVideoWindow::CopyToClipboard()
{
FRAMECONTEXT fc;
BOOL fSuccess = FALSE;
if (S_OK == GetFrame(&fc))
{
if (OpenClipboard(GetWindow()))
{
EmptyClipboard();
{
HGLOBAL hCopy;
BITMAPINFOHEADER *pbmih;
pbmih = &fc.lpbmi->bmiHeader;
hCopy = GlobalAlloc(GHND, DibSize(pbmih));
if (NULL != hCopy)
{
LPVOID lpvCopy = GlobalLock(hCopy);
int nHdrSize = DibHdrSize(pbmih);
CopyMemory(lpvCopy, pbmih, nHdrSize);
CopyMemory((LPBYTE)lpvCopy + nHdrSize, fc.lpData, DibDataSize(pbmih));
GlobalUnlock(hCopy);
fSuccess = (NULL != SetClipboardData(CF_DIB, (HANDLE)hCopy));
if (!fSuccess)
{
GlobalFree(hCopy);
}
}
}
CloseClipboard();
}
ReleaseFrame(&fc);
}
return fSuccess;
}
#endif // TryPaintDIB
HRESULT CVideoWindow::GetFrame(FRAMECONTEXT *pFrameContext)
{
HRESULT hr = E_FAIL;
if (NULL != m_pActiveChannel)
{
hr = m_pActiveChannel->GetProperty(NM_VIDPROP_FRAME, (DWORD_PTR *)pFrameContext);
}
return hr;
}
HRESULT CVideoWindow::ReleaseFrame(FRAMECONTEXT *pFrameContext)
{
HRESULT hr = E_FAIL;
if (NULL != m_pActiveChannel)
{
hr = m_pActiveChannel->SetProperty(NM_VIDPROP_FRAME, (DWORD_PTR)pFrameContext);
}
return hr;
}
void CVideoWindow::SaveSettings()
{
RegEntry re( IsLocal() ? VIDEO_LOCAL_KEY : VIDEO_REMOTE_KEY,
HKEY_CURRENT_USER );
re.SetValue(REGVAL_VIDEO_FRAME_SIZE, m_dwFrameSize);
if (!IsLocal())
{
re.SetValue(REGVAL_VIDEO_QUALITY, m_dwImageQuality);
}
else
{
re.SetValue(REGVAL_VIDEO_MIRROR, GetMirror());
}
}
VOID CVideoWindow::ForwardSysChangeMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_PALETTECHANGED:
OnPaletteChanged();
break;
}
}
VOID CVideoWindow::OnPaletteChanged(void)
{
::RedrawWindow(GetWindow(), NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
}
BOOL CVideoWindow::HasDialog(DWORD dwDialog)
{
DWORD_PTR dwDialogs = 0;
if (NULL != m_pActiveChannel)
{
m_pActiveChannel->GetProperty(NM_VIDPROP_CAMERA_DIALOG, &dwDialogs);
}
return (BOOL)(dwDialogs & dwDialog);
}
VOID CVideoWindow::ShowDialog(DWORD dwDialog)
{
if (NULL != m_pActiveChannel)
{
m_pActiveChannel->SetProperty(NM_VIDPROP_CAMERA_DIALOG, dwDialog);
}
}
BOOL CVideoWindow::InitMirroring(RECT &rcVideo)
{
HDC hdcWindow;
if ((m_hBitmapMirror != NULL) &&
(RectWidth(rcVideo) == m_sizeBitmapMirror.cx) &&
(RectHeight(rcVideo) == m_sizeBitmapMirror.cy))
{
return TRUE;
}
hdcWindow = ::GetDC(NULL);
UnInitMirroring();
m_hBitmapMirror = ::CreateCompatibleBitmap(hdcWindow, RectWidth(rcVideo), RectHeight(rcVideo));
if (m_hBitmapMirror == NULL)
{
ReleaseDC(NULL, hdcWindow);
return FALSE;
}
m_hDCMirror = ::CreateCompatibleDC(hdcWindow);
if (m_hDCMirror == NULL)
{
UnInitMirroring();
ReleaseDC(NULL, hdcWindow);
return FALSE;
}
// preserve the handle of the object being replaced
m_hGDIObj = ::SelectObject(m_hDCMirror, m_hBitmapMirror);
::SetMapMode(m_hDCMirror, GetMapMode(hdcWindow));
m_sizeBitmapMirror.cx = RectWidth(rcVideo);
m_sizeBitmapMirror.cy = RectHeight(rcVideo);
ReleaseDC(NULL, hdcWindow);
return TRUE;
}
VOID CVideoWindow::UnInitMirroring()
{
if (m_hBitmapMirror)
{
if (m_hDCMirror)
{
::SelectObject(m_hDCMirror, m_hGDIObj);
}
::DeleteObject(m_hBitmapMirror);
m_hBitmapMirror = NULL;
}
if (m_hDCMirror)
{
::DeleteObject(m_hDCMirror);
m_hDCMirror = NULL;
}
}
VOID CVideoWindow::InvalidateAll()
{
for(int i = g_pVideos->GetSize()-1; i >= 0 ; --i)
{
CVideoWindow *pVideo = (*g_pVideos)[i];
HWND hwnd = pVideo->GetWindow();
if (NULL != hwnd)
{
InvalidateRect(hwnd, NULL, FALSE);
}
}
}
VOID CVideoWindow::SetMirror(BOOL bMirror)
{
bMirror = bMirror != FALSE;
if (g_fMirror != bMirror)
{
g_fMirror = bMirror;
InvalidateAll();
}
}
BOOL CVideoWindow::CanCopy()
{
BOOL bCopy = (IsXferEnabled() && IsLocal()) || IsConnected();
if (bCopy)
{
bCopy = FALSE;
FRAMECONTEXT fc;
if (S_OK == GetFrame(&fc))
{
BITMAPINFOHEADER *pbmih = &fc.lpbmi->bmiHeader;
bCopy = IsKnownDIBFormat(pbmih);
ReleaseFrame(&fc);
}
}
return(bCopy);
}
BOOL CVideoWindow::FDidNotDisplayIntelLogo()
{
return FALSE;
}
VOID CVideoWindow::DisplayIntelLogo( BOOL bDisplay )
{
}