Windows2003-3790/enduser/netmeeting/core/video.cpp
2020-09-30 16:53:55 +02:00

1405 lines
31 KiB
C++

/****************************************************************************
*
* FILE: Videoui.cpp
*
* CREATED: Mark MacLin (MMacLin) 10-17-96
*
* CONTENTS: CVideo object
*
****************************************************************************/
// File: VideoUI.cpp
#include "precomp.h"
#include "avdefs.h"
#include "video.h"
#include "h323.h"
#include <mperror.h>
#include <initguid.h>
#include <nacguids.h>
#define INITIAL_FRAMERATE 700
#define DibHdrSize(lpbi) ((lpbi)->biSize + (int)(lpbi)->biClrUsed * sizeof(RGBQUAD))
#define DibDataSize(lpbi) ((lpbi)->biSizeImage)
#define DibSize(lpbi) (DibHdrSize(lpbi) + DibDataSize(lpbi))
//
// SortOrder() and SetVideoSize() are helper functions for reordering the video
// based on the notion of a "small", "medium", or "large" user preference that is
// exposed by a property setting in INmChannelVideo::Setproperty. This
// notion is flawed because there may be more or less than three sizes.
// We should expose the possible sizes and let the application choose
// a format. Unil then, this hack has to be here. The code for these two functions
// was originally in vidstrm.cpp (in NAC.DLL)
//
//
// types & globals used by SortOrder() and SetVideoSize()
//
// Used to translate between frame sizes and the FRAME_* bit flags
#define NON_STANDARD 0x80000000
#define SIZE_TO_FLAG(s) (s == Small ? FRAME_SQCIF : s == Medium ? FRAME_QCIF: s == Large ? FRAME_CIF : NON_STANDARD)
// FORMATORDER: structure used in ::SetVideoSize to
// use predefined frame size orders for different set size requests
typedef struct _FORMATORDER
{
WORD indexCIF;
WORD indexQCIF;
WORD indexSQCIF;
} FORMATORDER;
// Table of sizes in order
const FORMATORDER g_fmtOrderTable[3] =
{
{ 0, 1, 2 }, // requestor asked for CIF
{ 2, 0, 1 }, // requestor asked for QCIF
{ 2, 1, 0 } // requestor asked for SQCIF
};
// SortOrder
// Helper function to search for the specific format type and set its sort
// order to the desired number
// THIS WAS MOVED HERE FROM vidstrm.cpp
//
BOOL
SortOrder(
IAppVidCap *pavc,
BASIC_VIDCAP_INFO* pvidcaps,
DWORD dwcFormats,
DWORD dwFlags,
WORD wDesiredSortOrder,
int nNumFormats
)
{
int i, j;
int nNumSizes = 0;
int *aFrameSizes = (int *)NULL;
int *aMinFrameSizes = (int *)NULL;
int iMaxPos;
WORD wTempPos, wMaxSortIndex;
// Scale sort value
wDesiredSortOrder *= (WORD)nNumFormats;
// Local buffer of sizes that match dwFlags
if (!(aFrameSizes = (int *)LocalAlloc(LPTR,nNumFormats * sizeof (int))))
goto out;
// Look through all the formats until we find the ones we want
// Save the position of these entries
for (i=0; i<(int)dwcFormats; i++)
if (SIZE_TO_FLAG(pvidcaps[i].enumVideoSize) == dwFlags)
aFrameSizes[nNumSizes++] = i;
// Now order those entries from highest to lowest sort index
for (i=0; i<nNumSizes; i++)
{
for (iMaxPos = -1L, wMaxSortIndex=0UL, j=i; j<nNumSizes; j++)
{
if (pvidcaps[aFrameSizes[j]].wSortIndex > wMaxSortIndex)
{
wMaxSortIndex = pvidcaps[aFrameSizes[j]].wSortIndex;
iMaxPos = j;
}
}
if (iMaxPos != -1L)
{
wTempPos = (WORD)aFrameSizes[i];
aFrameSizes[i] = aFrameSizes[iMaxPos];
aFrameSizes[iMaxPos] = wTempPos;
}
}
// Change the sort index of the sorted entries
for (; nNumSizes--;)
pvidcaps[aFrameSizes[nNumSizes]].wSortIndex = wDesiredSortOrder++;
// Release memory
LocalFree(aFrameSizes);
return TRUE;
out:
return FALSE;
}
// ::SetVideoSize
//
// THIS WAS MOVED HERE FROM vidstrm.cpp
HRESULT
SetVideoSize(
IH323CallControl *pH323CallControl,
DWORD dwSizeFlags
)
{
IAppVidCap* pavc;
DWORD dwcFormats;
DWORD dwcFormatsReturned;
BASIC_VIDCAP_INFO* pvidcaps = NULL;
BASIC_VIDCAP_INFO* pmin;
DWORD *pvfx = NULL;
DWORD i, j;
int k;
HRESULT hr = S_OK;
int nNumFormatTags;
// Validate parameters
if (dwSizeFlags != FRAME_CIF && dwSizeFlags != FRAME_QCIF && dwSizeFlags != FRAME_SQCIF)
return S_FALSE;;
// Prepare for error
hr = S_FALSE;
// Get a vid cap interface
if (pH323CallControl->QueryInterface(IID_IAppVidCap, (void **)&pavc) != S_OK)
goto out;
// Get the number of BASIC_VIDCAP_INFO structures available
if (pavc->GetNumFormats((UINT*)&dwcFormats) != S_OK)
goto out;
// Allocate some memory to hold the list in
if (!(pvidcaps = (BASIC_VIDCAP_INFO*)LocalAlloc(LPTR,dwcFormats * sizeof (BASIC_VIDCAP_INFO))))
goto out;
// Get the list
if (pavc->EnumFormats(pvidcaps, dwcFormats * sizeof (BASIC_VIDCAP_INFO),
(UINT*)&dwcFormatsReturned) != S_OK)
goto out;
// Use the preformatted list of choice here
switch (dwSizeFlags)
{
default:
case FRAME_CIF: i = 0; break;
case FRAME_QCIF: i = 1; break;
case FRAME_SQCIF: i = 2; break;
}
// Get the number of different format tags
if (!(pvfx = (DWORD*)LocalAlloc(LPTR,dwcFormatsReturned * sizeof (DWORD))))
goto out;
ZeroMemory(pvfx,dwcFormatsReturned * sizeof (DWORD));
if (dwcFormatsReturned)
{
for (nNumFormatTags = 1, pvfx[0] = pvidcaps[0].dwFormatTag, j=1; j<dwcFormatsReturned; j++)
{
for (k=0; k<nNumFormatTags; k++)
if (pvidcaps[j].dwFormatTag == pvfx[k])
break;
if (k==nNumFormatTags)
pvfx[nNumFormatTags++] = pvidcaps[j].dwFormatTag;
}
}
// Set the sort order for the desired item
if (!SortOrder(pavc, pvidcaps, dwcFormatsReturned, FRAME_CIF, g_fmtOrderTable[i].indexCIF, nNumFormatTags) ||
!SortOrder(pavc, pvidcaps, dwcFormatsReturned, FRAME_QCIF, g_fmtOrderTable[i].indexQCIF, nNumFormatTags) ||
!SortOrder(pavc, pvidcaps, dwcFormatsReturned, FRAME_SQCIF, g_fmtOrderTable[i].indexSQCIF, nNumFormatTags))
{
goto out;
}
// Always pack indices
for (i=0; i<dwcFormatsReturned; i++)
{
// First find an entry with a sort index larger or equal to i
for (j=0; j<dwcFormatsReturned; j++)
{
// if ((pvidcaps[j].wSortIndex >= i) || (!i && (pvidcaps[j].wSortIndex == 0)))
if (pvidcaps[j].wSortIndex >= i)
{
pmin = &pvidcaps[j];
break;
}
}
// First the smallest entry larger or equal to i
for (; j<dwcFormatsReturned; j++)
{
if ((pvidcaps[j].wSortIndex < pmin->wSortIndex) && (pvidcaps[j].wSortIndex >= i))
pmin = &pvidcaps[j];
}
// Update sort index
pmin->wSortIndex = (WORD)i;
}
// Ok, now submit this list
if (pavc->ApplyAppFormatPrefs(pvidcaps, dwcFormats) != S_OK)
{
goto out;
}
hr = S_OK;
out:
// Free the memory, we're done
if (pvidcaps)
LocalFree(pvidcaps);
if (pvfx)
LocalFree(pvfx);
// let the interface go
if (pavc)
pavc->Release();
return hr;
}
CVideoPump::CVideoPump(BOOL fLocal) :
m_fPaused(FALSE),
m_dwUser(0),
m_pfnCallback(NULL),
m_dwLastFrameRate(0),
m_fLocal(fLocal),
m_fChannelOpen(FALSE),
m_pImage(NULL),
m_pVideoRender(NULL),
m_BestFormat(INVALID_MEDIA_FORMAT),
m_NewFormat(INVALID_MEDIA_FORMAT),
m_fOpenPending(FALSE),
m_fReopenPending(FALSE),
m_fClosePending(FALSE)
{
}
CVideoPump::~CVideoPump()
{
if (NULL != m_pVideoRender)
{
m_pVideoRender->Done();
m_pVideoRender->Release();
}
ReleaseImage();
if (NULL != m_pIVideoDevice)
{
m_pIVideoDevice->Release();
}
if (NULL != m_pMediaStream)
{
m_pMediaStream->Release();
}
if (NULL != m_pPreviewChannel)
{
m_pPreviewChannel->Release();
}
if (NULL != m_pCommChannel)
{
m_pCommChannel->Release();
}
}
BOOL CVideoPump::ChanInitialize(ICommChannel* pCommChannel)
{
HRESULT hr;
BOOL bRet = TRUE;
if(m_pPreviewChannel && m_pPreviewChannel == pCommChannel)
{
ASSERT(m_pVideoRender && m_pCommChannel == pCommChannel);
//m_pCommChannel = pCommChannel;
}
else
{
m_pCommChannel = pCommChannel;
m_pCommChannel->AddRef();
}
return bRet;
}
BOOL CVideoPump::Initialize(IH323CallControl *pH323CallControl, IMediaChannel *pMC,
IVideoDevice *pVideoDevice, DWORD_PTR dwUser, LPFNFRAMEREADY pfnCallback)
{
HRESULT hr;
m_dwUser = dwUser;
m_pfnCallback = pfnCallback;
m_pMediaStream = pMC;
m_pMediaStream->AddRef();
if(m_fLocal)
{
GUID mediaID = MEDIA_TYPE_H323VIDEO;
hr = pH323CallControl->CreateLocalCommChannel(&m_pPreviewChannel, &mediaID, m_pMediaStream);
if(FAILED(hr))
{
ASSERT(0);
return FALSE;
}
m_pCommChannel = m_pPreviewChannel;
m_pCommChannel->AddRef();
}
hr = m_pMediaStream->QueryInterface(IID_IVideoRender, (void **)&m_pVideoRender);
if(FAILED(hr))
{
ASSERT(0);
return FALSE;
}
hr = m_pVideoRender->Init(m_dwUser, m_pfnCallback);
if(FAILED(hr))
{
ASSERT(0);
m_pVideoRender->Release();
m_pVideoRender = NULL;
return FALSE;
}
ASSERT(pVideoDevice);
m_pIVideoDevice = pVideoDevice;
m_pIVideoDevice->AddRef();
m_fChannelOpen = FALSE;
m_dwLastFrameRate = INITIAL_FRAMERATE;
m_fPaused = TRUE;
EnableXfer(FALSE); // need to store state locally, set it in OnChannelOpen
return TRUE;
}
HRESULT CVideoPump::GetFrame(FRAMECONTEXT *pFrameContext)
{
HRESULT hr;
// if we are paused m_pImage will be a pointer to the saved DIB
if (NULL != m_pImage)
{
*pFrameContext = m_FrameContext;
hr = S_OK;
}
else
{
if(m_pVideoRender)
{
hr = m_pVideoRender->GetFrame(pFrameContext);
}
else
{
hr = S_FALSE;
}
if (S_OK == hr)
{
// data pump may be sending a bogus lpClipRect, so ...
// if lpClipRect is NULL, calculate rect from bmiHeader
if (NULL == pFrameContext->lpClipRect) {
// calculate clip rect from BITMAPINFOHEADER
m_ClipRect.left = m_ClipRect.top = 0;
m_ClipRect.right = pFrameContext->lpbmi->bmiHeader.biWidth;
m_ClipRect.bottom = pFrameContext->lpbmi->bmiHeader.biHeight;
pFrameContext->lpClipRect = &m_ClipRect;
}
}
}
return hr;
}
HRESULT CVideoPump::ReleaseFrame(FRAMECONTEXT *pFrameContext)
{
// release the frame if it is not the saved DIB
if ((m_pImage != (LPBYTE)pFrameContext->lpbmi) && m_pVideoRender)
{
// if lpClipRect was NULL (see GetFrame), restore it
if (&m_ClipRect == pFrameContext->lpClipRect)
{
pFrameContext->lpClipRect = NULL;
}
return m_pVideoRender->ReleaseFrame(pFrameContext);
}
return S_OK;
}
VOID CVideoPump::SnapImage ()
{
FRAMECONTEXT FrameContext;
if ((NULL == m_pImage) && m_pVideoRender)
{
if (S_OK == m_pVideoRender->GetFrame(&FrameContext))
{
BITMAPINFOHEADER *pbmih;
pbmih = &FrameContext.lpbmi->bmiHeader;
m_pImage = (LPBYTE)LocalAlloc(LPTR, DibSize(pbmih));
if (NULL != m_pImage)
{
int nHdrSize = DibHdrSize(pbmih);
CopyMemory(m_pImage, pbmih, nHdrSize);
CopyMemory(m_pImage + nHdrSize, FrameContext.lpData, DibDataSize(pbmih));
m_FrameContext.lpbmi = (LPBITMAPINFO)m_pImage;
m_FrameContext.lpData = (LPBYTE)m_pImage + nHdrSize;
if (NULL != FrameContext.lpClipRect)
{
m_ClipRect = *FrameContext.lpClipRect;
}
else
{
m_ClipRect.left = m_ClipRect.top = 0;
m_ClipRect.right = m_FrameContext.lpbmi->bmiHeader.biWidth;
m_ClipRect.bottom = m_FrameContext.lpbmi->bmiHeader.biHeight;
}
m_FrameContext.lpClipRect = &m_ClipRect;
}
m_pVideoRender->ReleaseFrame(&FrameContext);
}
}
}
VOID CVideoPump::ReleaseImage ()
{
if (NULL != m_pImage)
{
LocalFree(m_pImage);
m_pImage = NULL;
}
}
VOID CVideoPump::Pause(BOOL fPause)
{
m_fPaused = fPause;
// ideally we would like the data pump to hold onto the last frame
// so that we don't have to do this
if (m_fPaused)
{
if (m_fChannelOpen)
{
SnapImage();
}
EnableXfer(FALSE);
}
else
{
EnableXfer(TRUE);
ReleaseImage();
}
}
BOOL CVideoPump::IsXferEnabled()
{
if (m_fLocal)
{
return IsSendEnabled();
}
return IsReceiveEnabled();
}
VOID CVideoPump::Open(MEDIA_FORMAT_ID format_id)
{
if(!m_pCommChannel)
{
return;
}
m_pCommChannel->PauseNetworkStream(FALSE);
m_pCommChannel->EnableOpen(TRUE);
if (m_fLocal)
{
HRESULT hr;
// if the channel is not open and a call is in progress, now is the time
if(m_pConnection && m_pCommChannel)
{
// a call is in progress
if(!IsChannelOpen()
&& !m_fOpenPending)
{
// so, the channel is not open
if(format_id != INVALID_MEDIA_FORMAT)
{
// try to open a channel using specified format
m_fOpenPending = TRUE; // do this first (callbacks!)
hr = m_pCommChannel->Open(format_id, m_pConnection);
if(FAILED(hr))
m_fOpenPending = FALSE;
}
}
else if (m_fClosePending)
{
m_NewFormat = format_id;
if(format_id != INVALID_MEDIA_FORMAT)
{
m_fClosePending = FALSE;
m_fReopenPending = TRUE;
hr = m_pCommChannel->Close();
}
}
}
}
}
VOID CVideoPump::Close()
{
HRESULT hr;
hr = m_pCommChannel->Close();
// what to do about an error?
}
VOID CVideoPump::EnableXfer(BOOL fEnable)
{
if (m_fLocal)
{
if (fEnable)
{
HRESULT hr;
SetFrameRate(m_dwLastFrameRate);
EnablePreview(TRUE);
EnableSend(TRUE);
// if the channel is not open and a call is in progress, now is the time
if(m_pConnection && m_pCommChannel)
{
// a call is in progress
if(!IsChannelOpen()
&& !m_fOpenPending)
{
// so, the channel is not open
m_BestFormat = m_NewFormat = CVideoProp::GetBestFormat();
if(m_BestFormat != INVALID_MEDIA_FORMAT)
{
// try to open a channel using format m_BestFormat
m_fOpenPending = TRUE; // do this first (callbacks!)
hr = m_pCommChannel->Open(m_BestFormat, m_pConnection);
if(FAILED(hr))
m_fOpenPending = FALSE;
}
// else no common video formats exist and a channel cannot
// be opened.
}
else if (m_fClosePending)
{
m_BestFormat = m_NewFormat = CVideoProp::GetBestFormat();
if(m_BestFormat != INVALID_MEDIA_FORMAT)
{
m_fClosePending = FALSE;
m_fReopenPending = TRUE;
hr = m_pCommChannel->Close();
}
}
}
}
else
{
if (IsSendEnabled())
{
m_dwLastFrameRate = GetFrameRate();
}
EnablePreview(FALSE);
EnableSend(FALSE);
}
}
else
{
EnableReceive(fEnable);
}
}
VOID CVideoPump::SetFrameSize(DWORD dwValue)
{
CVideoProp::SetFrameSize(dwValue);
ForceCaptureChange();
}
VOID CVideoPump::OnConnected(IH323Endpoint * lpConnection, ICommChannel *pIChannel)
{
m_pConnection = lpConnection;
m_fOpenPending = m_fReopenPending = m_fClosePending = FALSE;
}
VOID CVideoPump::OnChannelOpened(ICommChannel *pIChannel)
{
HRESULT hr;
m_fChannelOpen = TRUE;
m_fOpenPending = FALSE;
ChanInitialize(pIChannel);
ASSERT(m_pMediaStream);
if (m_fLocal)
{
m_fSend = TRUE;
EnableXfer(TRUE);
// if video size changed while waiting for the channel to be opened,
// then need to close again, then reopen again using the new format
if(m_BestFormat != m_NewFormat)
{
ForceCaptureChange();
}
else // make sure to track the video size
{
GetFrameSize();
}
}
else
{
EnableXfer(m_fReceive);
SetReceiveQuality(m_dwImageQuality);
}
ReleaseImage();
}
VOID CVideoPump::OnChannelError()
{
m_fOpenPending = FALSE;
}
NM_VIDEO_STATE CVideoPump::GetState()
{
NM_VIDEO_STATE state = NM_VIDEO_IDLE;
if (IsChannelOpen())
{
if (IsXferEnabled())
{
if (IsRemotePaused())
{
state = NM_VIDEO_REMOTE_PAUSED;
}
else
{
state = NM_VIDEO_TRANSFERRING;
}
}
else
{
if (IsRemotePaused())
{
state = NM_VIDEO_BOTH_PAUSED;
}
else
{
state = NM_VIDEO_LOCAL_PAUSED;
}
}
}
else
{
if (IsXferEnabled())
{
state = NM_VIDEO_PREVIEWING;
}
}
return state;
}
VOID CVideoPump::OnChannelClosed()
{
m_fChannelOpen = FALSE;
HRESULT hr;
if(m_pPreviewChannel)
{
if(m_fReopenPending)
{
m_fReopenPending = FALSE;
if(m_BestFormat != INVALID_MEDIA_FORMAT )
{
m_fOpenPending = TRUE;
hr = m_pCommChannel->Open(m_BestFormat, m_pConnection);
if(FAILED(hr))
m_fOpenPending = FALSE;
}
}
else if(CVideoProp::IsPreviewEnabled())
{
EnablePreview(TRUE);
}
}
else
{
if(m_pCommChannel)
{
m_pCommChannel->Release();
m_pCommChannel = NULL;
}
}
}
VOID CVideoPump::OnDisconnected()
{
m_pConnection = NULL;
if(m_dwFrameSize != m_dwPreferredFrameSize)
{
SetFrameSize(m_dwPreferredFrameSize);
}
if (!IsLocal())
{
EnableXfer(FALSE);
}
ReleaseImage();
}
CVideoProp::CVideoProp() :
m_pCommChannel(NULL),
m_pPreviewChannel(NULL),
m_pConnection(NULL),
m_pMediaStream(NULL),
m_pIVideoDevice(NULL)
{
}
VOID CVideoProp::EnableSend(BOOL fEnable)
{
m_fSend = fEnable;
BOOL bPause = (fEnable)? FALSE :TRUE;
ASSERT(m_pCommChannel);
m_pCommChannel->PauseNetworkStream(bPause);
m_pCommChannel->EnableOpen(fEnable);
}
BOOL CVideoProp::IsSendEnabled()
{
return m_fSend;
}
VOID CVideoProp::EnableReceive(BOOL fEnable)
{
m_fReceive = fEnable;
BOOL fPause = !fEnable;
if(m_pCommChannel)
{
m_pCommChannel->PauseNetworkStream(fPause);
}
}
BOOL CVideoProp::IsReceiveEnabled()
{
return m_fReceive;
}
VOID CVideoProp::EnablePreview(BOOL fEnable)
{
m_fPreview = fEnable;
MEDIA_FORMAT_ID FormatID;
if(m_pCommChannel)
{
if(m_fPreview)
{
// get format to preview, then do it
FormatID = GetBestFormat();
if(FormatID != INVALID_MEDIA_FORMAT)
{
m_pCommChannel->Preview(FormatID, m_pMediaStream);
}
}
else
{
m_pCommChannel->Preview(INVALID_MEDIA_FORMAT, NULL);
}
}
}
BOOL CVideoProp::IsPreviewEnabled()
{
return m_fPreview;
}
BOOL CVideoProp::IsRemotePaused()
{
if(m_pCommChannel)
return m_pCommChannel->IsRemotePaused();
else
return FALSE;
}
VOID CVideoProp::SetFrameRate(DWORD dwValue)
{
m_dwFrameRate = dwValue;
ASSERT(m_pMediaStream);
m_pMediaStream->SetProperty(
PROP_VIDEO_FRAME_RATE,
&dwValue,
sizeof(dwValue));
}
DWORD CVideoProp::GetFrameRate()
{
DWORD dwValue;
UINT uSize = sizeof(dwValue);
ASSERT(m_pMediaStream);
m_pMediaStream->GetProperty(
PROP_VIDEO_FRAME_RATE,
&dwValue,
&uSize);
TRACE_OUT(("GetFrameRate returns %d", dwValue));
return dwValue;
}
MEDIA_FORMAT_ID CVideoProp::GetBestFormat()
{
IAppVidCap* pavc = NULL;
UINT cFormats;
BASIC_VIDCAP_INFO* pvidcaps = NULL;
MEDIA_FORMAT_ID FormatID = INVALID_MEDIA_FORMAT;
// Get a vid cap interface. If in a call, use the best common format
//
if(!m_pConnection)
{
// not in a call - use the best local format period
if(!m_pCommChannel)
goto out;
if (m_pCommChannel->QueryInterface(IID_IAppVidCap, (void **)&pavc) != S_OK)
goto out;
// Get the number of BASIC_VIDCAP_INFO structures available
if (pavc->GetNumFormats(&cFormats) != S_OK)
goto out;
if(cFormats < 1)
goto out;
// Allocate some memory to hold the list in
if (!(pvidcaps = (BASIC_VIDCAP_INFO*)LocalAlloc(LPTR, cFormats * sizeof (BASIC_VIDCAP_INFO))))
goto out;
// Get the list of local capabilities
// (by the way, this is never called for receive video)
if (pavc->EnumFormats(pvidcaps, cFormats * sizeof (BASIC_VIDCAP_INFO),
&cFormats) != S_OK)
goto out;
// the output of EnumCommonFormats is in preferred order
FormatID = pvidcaps[0].Id;
}
else
{
if (m_pConnection->QueryInterface(IID_IAppVidCap, (void **)&pavc) != S_OK)
goto out;
// Get the number of BASIC_VIDCAP_INFO structures available
if (pavc->GetNumFormats(&cFormats) != S_OK)
goto out;
if(cFormats < 1)
goto out;
// Allocate some memory to hold the list in
if (!(pvidcaps = (BASIC_VIDCAP_INFO*)LocalAlloc(LPTR, cFormats * sizeof (BASIC_VIDCAP_INFO))))
goto out;
// Get the list of viable transmit capabilities
// (by the way, this is never called for receive video)
if (pavc->EnumCommonFormats(pvidcaps, cFormats * sizeof (BASIC_VIDCAP_INFO),
&cFormats, TRUE) != S_OK)
goto out;
// the output of EnumCommonFormats is in preferred order
FormatID = pvidcaps[0].Id;
}
out:
// Free the memory, we're done
if (pvidcaps)
LocalFree(pvidcaps);
// let the interface go
if (pavc)
pavc->Release();
return FormatID;
}
VOID CVideoProp::SetFrameSize(DWORD dwValue)
{
IH323CallControl * pH323CallControl = g_pH323UI->GetH323CallControl();
m_dwFrameSize = m_dwPreferredFrameSize = dwValue;
::SetVideoSize(pH323CallControl, m_dwFrameSize);
}
DWORD CVideoProp::GetFrameSize()
{
MEDIA_FORMAT_ID idCurrent;
if(m_pCommChannel)
{
idCurrent = m_pCommChannel->GetConfiguredFormatID();
m_dwFrameSize = GetFrameSizes(idCurrent);
}
return m_dwFrameSize;
}
DWORD CVideoProp::GetFrameSizes(MEDIA_FORMAT_ID idSpecificFormat)
{
DWORD dwValue = 0; //FRAME_SQCIF | FRAME_QCIF | FRAME_CIF;
HRESULT hr;
BOOL bOpen = FALSE;
ASSERT(m_pCommChannel);
// Used to translate between frame sizes and the FRAME_* bit flags
#define NON_STANDARD 0x80000000
#define SIZE_TO_FLAG(s) (s == Small ? FRAME_SQCIF : s == Medium ? FRAME_QCIF: s == Large ? FRAME_CIF : NON_STANDARD)
IAppVidCap* pavc = NULL;
DWORD dwcFormats;
DWORD dwcFormatsReturned;
BASIC_VIDCAP_INFO* pvidcaps = NULL;
DWORD i;
DWORD dwSizes = 0;
DWORD dwThisSize;
// Get a vid cap interface
hr = m_pCommChannel->QueryInterface(IID_IAppVidCap, (void **)&pavc);
if (hr != S_OK)
goto out;
// Get the number of BASIC_VIDCAP_INFO structures available
hr = pavc->GetNumFormats((UINT*)&dwcFormats);
if (hr != S_OK)
goto out;
// Allocate some memory to hold the list in
if (!(pvidcaps = (BASIC_VIDCAP_INFO*)LocalAlloc(LPTR, dwcFormats * sizeof (BASIC_VIDCAP_INFO))))
{
// report that no sizes are available?
// dwValue = 0FRAME_SQCIF | FRAME_QCIF | FRAME_CIF;
goto out;
}
// if an active session, use common caps from that session
hr = m_pCommChannel->IsChannelOpen(&bOpen);
// if hr is an error, so what. it will take the channel not open path
if(bOpen)
{
// Get the list of common formats
hr = pavc->EnumCommonFormats(pvidcaps, dwcFormats * sizeof (BASIC_VIDCAP_INFO),
(UINT*)&dwcFormatsReturned, m_fSend);
if(hr != S_OK)
{
// if the error is simply because there are no remote video caps, get the local formats
if(hr == CAPS_E_NOCAPS)
{
hr = pavc->EnumFormats(pvidcaps, dwcFormats * sizeof (BASIC_VIDCAP_INFO),
(UINT*)&dwcFormatsReturned);
if (hr != S_OK)
goto out;
}
else
goto out;
}
}
else
{
hr = pavc->EnumFormats(pvidcaps, dwcFormats * sizeof (BASIC_VIDCAP_INFO),
(UINT*)&dwcFormatsReturned);
if (hr != S_OK)
goto out;
}
if(bOpen && (idSpecificFormat != INVALID_MEDIA_FORMAT ))
{
// Now walk through the list to see what sizes are supported
for (i = 0 ; i < dwcFormatsReturned ; i++)
{
if(pvidcaps[i].Id == idSpecificFormat)
{
dwThisSize = SIZE_TO_FLAG(pvidcaps[i].enumVideoSize);
// As long as the macro found the size, return it to the property requester
if (dwThisSize != NON_STANDARD)
{
dwSizes |= dwThisSize;
}
break;
}
}
}
else
{
// Now walk through the list to see what sizes are supported
for (i = 0 ; i < dwcFormatsReturned ; i++)
{
if(m_fSend)
{
if(!pvidcaps[i].bSendEnabled)
continue;
}
else
{
if(!pvidcaps[i].bRecvEnabled)
continue;
}
// Convert to bit field sizes or NON_STANDARD
dwThisSize = SIZE_TO_FLAG(pvidcaps[i].enumVideoSize);
// As long as the macro found the size, return it to the property requester
if (dwThisSize != NON_STANDARD)
dwSizes |= dwThisSize;
}
}
// Now that we've accumulated all the sizes, return them
dwValue = dwSizes;
out:
// Free the memory, we're done
if (pvidcaps)
LocalFree(pvidcaps);
// let the interface go
if (pavc)
pavc->Release();
return dwValue;
}
BOOL CVideoProp::HasSourceDialog()
{
HRESULT hr;
IVideoChannel *pVideoChannel=NULL;
DWORD dwFlags;
ASSERT(m_pMediaStream);
hr = m_pMediaStream->QueryInterface(IID_IVideoChannel, (void**)&pVideoChannel);
ASSERT(pVideoChannel);
if (FAILED(hr))
{
return FALSE;
}
pVideoChannel->GetDeviceDialog(&dwFlags);
pVideoChannel->Release();
return dwFlags & CAPTURE_DIALOG_SOURCE;
}
BOOL CVideoProp::HasFormatDialog()
{
HRESULT hr;
IVideoChannel *pVideoChannel=NULL;
DWORD dwFlags;
ASSERT(m_pMediaStream);
hr = m_pMediaStream->QueryInterface(IID_IVideoChannel, (void**)&pVideoChannel);
ASSERT(pVideoChannel);
if (FAILED(hr))
{
return FALSE;
}
pVideoChannel->GetDeviceDialog(&dwFlags);
pVideoChannel->Release();
return dwFlags & CAPTURE_DIALOG_FORMAT;
}
VOID CVideoProp::ShowSourceDialog()
{
DWORD dwFlags = CAPTURE_DIALOG_SOURCE;
HRESULT hr;
IVideoChannel *pVideoChannel=NULL;
ASSERT(m_pMediaStream);
hr = m_pMediaStream->QueryInterface(IID_IVideoChannel, (void**)&pVideoChannel);
ASSERT(pVideoChannel);
if (SUCCEEDED(hr))
{
pVideoChannel->ShowDeviceDialog(dwFlags);
pVideoChannel->Release();
}
}
VOID CVideoProp::ShowFormatDialog()
{
DWORD dwFlags = CAPTURE_DIALOG_FORMAT;
HRESULT hr;
IVideoChannel *pVideoChannel=NULL;
ASSERT(m_pMediaStream);
hr = m_pMediaStream->QueryInterface(IID_IVideoChannel, (void**)&pVideoChannel);
ASSERT(pVideoChannel);
if (SUCCEEDED(hr))
{
pVideoChannel->ShowDeviceDialog(dwFlags);
pVideoChannel->Release();
}
}
VOID CVideoProp::SetReceiveQuality(DWORD dwValue)
{
m_dwImageQuality = dwValue;
if(m_pCommChannel)
{
dwValue = MAX_VIDEO_QUALITY - dwValue;
m_pCommChannel->SetProperty(
PROP_TS_TRADEOFF,
&dwValue,
sizeof(dwValue));
}
}
DWORD CVideoProp::GetReceiveQuality()
{
return m_dwImageQuality;
}
BOOL CVideoProp::IsCaptureAvailable()
{
ULONG uNumCapDevs;
ASSERT(m_pIVideoDevice);
uNumCapDevs = m_pIVideoDevice->GetNumCapDev();
return (uNumCapDevs > 0);
}
BOOL CVideoProp::IsCaptureSuspended()
{
BOOL fStandby;
UINT uSize = sizeof(fStandby);
ASSERT(m_pCommChannel);
m_pCommChannel->GetProperty(
PROP_VIDEO_PREVIEW_STANDBY,
&fStandby,
&uSize);
return fStandby;
}
VOID CVideoProp::SuspendCapture(BOOL fSuspend)
{
ASSERT(m_pCommChannel);
if (fSuspend)
{
// Enable standby
m_pCommChannel->SetProperty(
PROP_VIDEO_PREVIEW_STANDBY,
&fSuspend,
sizeof(fSuspend));
m_pCommChannel->Preview(INVALID_MEDIA_FORMAT, NULL);
}
else
{
if(m_fPreview)
{
// get format to preview, then do it
MEDIA_FORMAT_ID FormatID = GetBestFormat();
if(FormatID != INVALID_MEDIA_FORMAT)
{
m_pCommChannel->Preview(FormatID, m_pMediaStream);
}
// Disable standby
m_pCommChannel->SetProperty(
PROP_VIDEO_PREVIEW_STANDBY,
&fSuspend,
sizeof(fSuspend));
}
else
{
m_pCommChannel->Preview(INVALID_MEDIA_FORMAT, NULL);
}
}
}
// Gets the number of enabled capture devices
// Returns -1L on error
int CVideoProp::GetNumCapDev()
{
ASSERT(m_pIVideoDevice);
return (m_pIVideoDevice->GetNumCapDev());
}
// Gets the max size of the captuire device name
// Returns -1L on error
int CVideoProp::GetMaxCapDevNameLen()
{
ASSERT(m_pIVideoDevice);
return (m_pIVideoDevice->GetMaxCapDevNameLen());
}
// Enum list of enabled capture devices
// Fills up 1st buffer with device IDs, 2nd buffer with device names
// Third parameter is the number of devices to enum
// Returns -1L on error
BOOL CVideoProp::EnumCapDev(DWORD *pdwCapDevIDs, TCHAR *pszCapDevNames, DWORD dwNumCapDev)
{
ASSERT(m_pIVideoDevice);
return (S_OK != m_pIVideoDevice->EnumCapDev(pdwCapDevIDs, pszCapDevNames, dwNumCapDev));
}
// Returns the ID of the currently selected device
// Returns -1L on error
int CVideoProp::GetCurrCapDevID()
{
ASSERT(m_pIVideoDevice);
return (m_pIVideoDevice->GetCurrCapDevID());
}
// Selects the current capture device
// Returns -1L on error
BOOL CVideoProp::SetCurrCapDevID(int nCapDevID)
{
ASSERT(m_pIVideoDevice);
return (S_OK != m_pIVideoDevice->SetCurrCapDevID(nCapDevID));
}
// Selects the current capture device
// Returns -1L on error
BOOL CVideoPump::SetCurrCapDevID(int nCapDevID)
{
if (nCapDevID == -1)
{
WARNING_OUT(("CVideoPump::SetCurrCapDevID called with %d", nCapDevID));
// This will release the capture device for Exchange RTC video stuff
if (m_pMediaStream)
{
m_pMediaStream->Configure(NULL, 0, NULL, 0, NULL);
}
return TRUE;
}
else
{
HRESULT hr;
IDualPubCap *pCapability = NULL;
LPAPPVIDCAPPIF pVidCap = NULL;
IH323CallControl * pH323CallControl = g_pH323UI->GetH323CallControl();
// change the capture device
CVideoProp::SetCurrCapDevID(nCapDevID);
// reinitialize local capability data
hr = pH323CallControl->QueryInterface(IID_IDualPubCap, (void **)&pCapability);
if(FAILED(hr))
goto out;
ASSERT(pCapability);
hr = pCapability->QueryInterface(IID_IAppVidCap, (void **)&pVidCap);
if(FAILED(hr))
goto out;
ASSERT(pVidCap);
hr = pVidCap->SetDeviceID(nCapDevID);
if(FAILED(hr))
goto out;
hr = pCapability->ReInitialize();
out:
if (pVidCap)
pVidCap->Release();
if (pCapability)
pCapability->Release();
return ForceCaptureChange();
}
}
BOOL CVideoPump::ForceCaptureChange()
{
HRESULT hr = S_OK;
if (m_fLocal)
{
if (m_pConnection)
{
if (IsXferEnabled())
{
if (!m_fReopenPending && !m_fOpenPending)
{
// if the send channel, and a call exists and the channel is open, and not
// already closing or opening .....
if(IsChannelOpen())
{
ASSERT(m_pCommChannel);
// need to close and re-open
// don't lose a good channel if there is no longer
// a compatible format, otherwise, close and reopen
m_BestFormat = m_NewFormat = CVideoProp::GetBestFormat();
if(m_BestFormat != INVALID_MEDIA_FORMAT)
{
m_fReopenPending = TRUE;
hr = m_pCommChannel->Close();
}
}
else
{
if(m_BestFormat != INVALID_MEDIA_FORMAT )
{
m_fOpenPending = TRUE;
hr = m_pCommChannel->Open(m_BestFormat, m_pConnection);
if(FAILED(hr))
m_fOpenPending = FALSE;
}
}
}
else // already waiting for channel to be opened using some format
{
m_NewFormat = CVideoProp::GetBestFormat();
}
}
else
{
if(IsChannelOpen())
{
m_fClosePending = TRUE;
}
}
}
else
{
if (!IsChannelOpen() && IsPreviewEnabled())
{
// togle preview to commit size change
EnablePreview(FALSE);
EnablePreview(TRUE);
}
}
}
if (FAILED(hr))
return FALSE;
else
return TRUE;
}