blob: 034f7106bc1c94aead55f3f0fa4dc981c0614c96 [file] [log] [blame]
/*
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "video_render_directdraw.h"
#include "video_render_windows_impl.h"
#include "Windows.h"
#include <ddraw.h>
#include <assert.h>
#include <initguid.h>
#include <MMSystem.h> // timeGetTime
DEFINE_GUID( IID_IDirectDraw7,0x15e65ec0,0x3b9c,0x11d2,0xb9,0x2f,0x00,0x60,0x97,0x97,0xea,0x5b );
#include "thread_wrapper.h"
#include "event_wrapper.h"
#include "trace.h"
#include "critical_section_wrapper.h"
//#include "VideoErrors.h"
// Added
#include "module_common_types.h"
#pragma warning(disable: 4355) // 'this' : used in base member initializer list
// picture in picture do we need overlay? answer no we can blit directly
// conference is easy since we can blt the quadrants seperatly
// To determine if the driver supports DMA, retrieve the driver capabilities by calling the IDirectDraw::GetCaps method,
// then look for DDBLTCAPS_READSYSMEM and/or DDBLTCAPS_WRITESYSMEM. If either of these flags is set, the device supports DMA.
// Blt with SRCCOPY should do this can we use it?
// investigate DDLOCK_NOSYSLOCK
namespace webrtc {
#define EXTRACT_BITS_RL(the_val, bits_start, bits_len) ((the_val >> (bits_start - 1)) & ((1 << bits_len) - 1))
WindowsThreadCpuUsage::WindowsThreadCpuUsage() :
_lastGetCpuUsageTime(0),
_lastCpuUsageTime(0),
_hThread(::GetCurrentThread()),
_cores(0),
_lastCpuUsage(0)
{
DWORD_PTR pmask, smask;
DWORD access = PROCESS_QUERY_INFORMATION;
if (GetProcessAffinityMask(
OpenProcess(access, false, GetCurrentProcessId()),
&pmask, &smask) != 0)
{
for (int i = 1; i < 33; i++)
{
if (EXTRACT_BITS_RL(pmask,i,1) == 0)
{
break;
}
_cores++;
}
//sanity
if (_cores > 32)
{
_cores = 32;
}
if (_cores < 1)
{
_cores = 1;
}
}
else
{
_cores = 1;
}
GetCpuUsage();
}
//in % since last call
int WindowsThreadCpuUsage::GetCpuUsage()
{
DWORD now = timeGetTime();
_int64 newTime = 0;
FILETIME creationTime;
FILETIME exitTime;
_int64 kernelTime = 0;
_int64 userTime = 0;
if (GetThreadTimes(_hThread, (FILETIME*) &creationTime, &exitTime,
(FILETIME*) &kernelTime, (FILETIME*) &userTime) != 0)
{
newTime = (kernelTime + userTime);
}
if (newTime == 0)
{
_lastGetCpuUsageTime = now;
return _lastCpuUsage;
}
// calculate the time difference since last call
const DWORD diffTime = (now - _lastGetCpuUsageTime);
_lastGetCpuUsageTime = now;
if (newTime < _lastCpuUsageTime)
{
_lastCpuUsageTime = newTime;
return _lastCpuUsage;
}
const int cpuDiff = (int) (newTime - _lastCpuUsageTime) / 10000;
_lastCpuUsageTime = newTime;
// calculate the CPU usage
_lastCpuUsage = (int) (float((cpuDiff * 100)) / (diffTime * _cores) + 0.5f);
if (_lastCpuUsage > 100)
{
_lastCpuUsage = 100;
}
return _lastCpuUsage;
}
DirectDrawStreamSettings::DirectDrawStreamSettings() :
_startWidth(0.0F),
_stopWidth(1.0F),
_startHeight(0.0F),
_stopHeight(1.0F),
_cropStartWidth(0.0F),
_cropStopWidth(1.0F),
_cropStartHeight(0.0F),
_cropStopHeight(1.0F)
{
}
;
DirectDrawBitmapSettings::DirectDrawBitmapSettings() :
_transparentBitMap(NULL),
_transparentBitmapLeft(0.0f),
_transparentBitmapRight(1.0f),
_transparentBitmapTop(0.0f),
_transparentBitmapBottom(1.0f),
_transparentBitmapWidth(0),
_transparentBitmapHeight(0),
_transparentBitmapColorKey(NULL),
_transparentBitmapSurface(NULL)
{
}
;
DirectDrawBitmapSettings::~DirectDrawBitmapSettings()
{
if (_transparentBitmapColorKey)
{
delete _transparentBitmapColorKey;
}
if (_transparentBitmapSurface)
{
_transparentBitmapSurface->Release();
}
_transparentBitmapColorKey = NULL;
_transparentBitmapSurface = NULL;
}
;
int DirectDrawBitmapSettings::SetBitmap(Trace* _trace,
DirectDraw* directDraw)
{
VideoFrame tempVideoBuffer;
HGDIOBJ oldhand;
BITMAPINFO pbi;
BITMAP bmap;
HDC hdcNew;
hdcNew = CreateCompatibleDC(0);
// Fill out the BITMAP structure.
GetObject(_transparentBitMap, sizeof(bmap), &bmap);
//Select the bitmap handle into the new device context.
oldhand = SelectObject(hdcNew, (HGDIOBJ) _transparentBitMap);
// we are done with this object
DeleteObject(oldhand);
pbi.bmiHeader.biSize = 40;
pbi.bmiHeader.biWidth = bmap.bmWidth;
pbi.bmiHeader.biHeight = bmap.bmHeight;
pbi.bmiHeader.biPlanes = 1;
pbi.bmiHeader.biBitCount = bmap.bmBitsPixel;
pbi.bmiHeader.biCompression = BI_RGB;
pbi.bmiHeader.biSizeImage = bmap.bmWidth * bmap.bmHeight * 3;
tempVideoBuffer.VerifyAndAllocate(bmap.bmWidth * bmap.bmHeight * 4);
// the original un-stretched image in RGB24
// todo is there another struct for pbi purify reports read of 24 bytes larger than size
int pixelHeight = GetDIBits(hdcNew, _transparentBitMap, 0, bmap.bmHeight,
tempVideoBuffer.Buffer(), &pbi, DIB_RGB_COLORS);
if (pixelHeight == 0)
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw failed to GetDIBits in SetBitmap.");
return -1;
//return VIDEO_DIRECT_DRAW_FAILURE;
}
DeleteDC(hdcNew);
if (pbi.bmiHeader.biBitCount != 24 && pbi.bmiHeader.biBitCount != 32)
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw failed to SetBitmap invalid bit depth");
return -1;
//return VIDEO_DIRECT_DRAW_FAILURE;
}
DirectDrawSurfaceDesc ddsd;
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
ddsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY;
ddsd.dwHeight = bmap.bmHeight;
ddsd.dwWidth = bmap.bmWidth;
ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB;
_transparentBitmapWidth = bmap.bmWidth;
_transparentBitmapHeight = bmap.bmHeight;
ddsd.ddpfPixelFormat.dwRGBBitCount = 32;
ddsd.ddpfPixelFormat.dwRBitMask = 0xff0000;
ddsd.ddpfPixelFormat.dwGBitMask = 0xff00;
ddsd.ddpfPixelFormat.dwBBitMask = 0xff;
ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0;
if (_transparentBitmapSurface)
{
_transparentBitmapSurface->Release();
_transparentBitmapSurface = NULL;
}
HRESULT ddrval =
directDraw->CreateSurface(&ddsd, &_transparentBitmapSurface, NULL);
if (FAILED(ddrval))
{
WEBRTC_TRACE(
kTraceError,
kTraceVideo,
-1,
"DirectDraw failed to CreateSurface _transparentBitmapSurface: 0x%x",
ddrval);
return -1;
//return VIDEO_DIRECT_DRAW_FAILURE;
}
memset(&ddsd, 0, sizeof(DDSURFACEDESC));
ddsd.dwSize = sizeof(DDSURFACEDESC);
ddrval = _transparentBitmapSurface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL);
if (ddrval == DDERR_SURFACELOST)
{
ddrval = _transparentBitmapSurface->Restore();
if (ddrval != DD_OK)
{
WEBRTC_TRACE(kTraceWarning, kTraceVideo, -1,
"DirectDraw failed to restore lost _transparentBitmapSurface");
return -1;
//return VIDEO_DIRECT_DRAW_FAILURE;
}
WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
"DirectDraw restored lost _transparentBitmapSurface");
ddrval
= _transparentBitmapSurface->Lock(NULL, &ddsd, DDLOCK_WAIT,
NULL);
if (ddrval != DD_OK)
{
WEBRTC_TRACE(
kTraceInfo,
kTraceVideo,
-1,
"DirectDraw lock error 0x%x _transparentBitmapSurface",
ddrval);
return -1;
//return VIDEO_DIRECT_DRAW_FAILURE;
}
}
unsigned char* dstPtr = (unsigned char*) ddsd.lpSurface;
unsigned char* srcPtr = (unsigned char*) tempVideoBuffer.Buffer();
int pitch = bmap.bmWidth * 4;
if (ddsd.dwFlags & DDSD_PITCH)
{
pitch = ddsd.lPitch;
}
if (pbi.bmiHeader.biBitCount == 24)
{
ConvertRGB24ToARGB(srcPtr, dstPtr, bmap.bmWidth, bmap.bmHeight,
0);
}
else
{
srcPtr += (bmap.bmWidth * 4) * (bmap.bmHeight - 1);
for (int i = 0; i < bmap.bmHeight; ++i)
{
memcpy(dstPtr, srcPtr, bmap.bmWidth * 4);
srcPtr -= bmap.bmWidth * 4;
dstPtr += pitch;
}
}
_transparentBitmapSurface->Unlock(NULL);
return 0;
}
/**
*
* DirectDrawTextSettings
*
*/
DirectDrawTextSettings::DirectDrawTextSettings() :
_ptrText(NULL),
_textLength(0),
_colorRefText(RGB(255, 255, 255)), // white
_colorRefBackground(RGB(0, 0, 0)), // black
_textLeft(0.0f),
_textRight(0.0f),
_textTop(0.0f),
_textBottom(0.0f),
_transparent(true)
{
}
DirectDrawTextSettings::~DirectDrawTextSettings()
{
if (_ptrText)
{
delete[] _ptrText;
}
}
int DirectDrawTextSettings::SetText(const char* text, int textLength,
COLORREF colorText, COLORREF colorBg,
float left, float top, float right,
float bottom)
{
if (_ptrText)
{
delete[] _ptrText;
}
_ptrText = new char[textLength];
memcpy(_ptrText, text, textLength);
_textLength = textLength;
_colorRefText = colorText;
_colorRefBackground = colorBg;
//_transparent = transparent;
_textLeft = left;
_textRight = right;
_textTop = top;
_textBottom = bottom;
return 0;
}
/**
*
* DirectDrawChannel
*
*
*/
// this need to have a refcount dueto multiple HWNDS demux
DirectDrawChannel::DirectDrawChannel(DirectDraw* directDraw,
VideoType blitVideoType,
VideoType incomingVideoType,
VideoType screenVideoType,
VideoRenderDirectDraw* owner) :
_critSect(CriticalSectionWrapper::CreateCriticalSection()), _refCount(1),
_width(0), _height(0), _numberOfStreams(0), _doubleBuffer(false),
_directDraw(directDraw), _offScreenSurface(NULL),
_offScreenSurfaceNext(NULL), _incomingVideoType(incomingVideoType),
_blitVideoType(blitVideoType),
_originalBlitVideoType(blitVideoType),
_screenVideoType(screenVideoType), _deliverInScreenType(false),
_owner(owner)
{
_directDraw->AddRef();
}
DirectDrawChannel::~DirectDrawChannel()
{
if (_directDraw)
{
_directDraw->Release();
}
if (_offScreenSurface)
{
_offScreenSurface->Release();
}
if (_offScreenSurfaceNext)
{
_offScreenSurfaceNext->Release();
}
std::map<unsigned long long, DirectDrawStreamSettings*>::iterator it =
_streamIdToSettings.begin();
while (it != _streamIdToSettings.end())
{
DirectDrawStreamSettings* streamSettings = it->second;
if (streamSettings)
{
delete streamSettings;
}
it = _streamIdToSettings.erase(it);
}
delete _critSect;
}
void DirectDrawChannel::AddRef()
{
CriticalSectionScoped cs(*_critSect);
_refCount++;
}
void DirectDrawChannel::Release()
{
bool deleteObj = false;
_critSect->Enter();
_refCount--;
if (_refCount == 0)
{
deleteObj = true;
}
_critSect->Leave();
if (deleteObj)
{
delete this;
}
}
void DirectDrawChannel::SetStreamSettings(VideoRenderDirectDraw* DDobj,
short streamId, float startWidth,
float startHeight,
float stopWidth, float stopHeight)
{
// we can save 5 bits due to 16 byte alignment of the pointer
unsigned long long lookupID = reinterpret_cast<unsigned long long> (DDobj);
lookupID &= 0xffffffffffffffe0;
lookupID <<= 11;
lookupID += streamId;
CriticalSectionScoped cs(*_critSect);
DirectDrawStreamSettings* streamSettings = NULL;
std::map<unsigned long long, DirectDrawStreamSettings*>::iterator it =
_streamIdToSettings.find(lookupID);
if (it == _streamIdToSettings.end())
{
streamSettings = new DirectDrawStreamSettings();
_streamIdToSettings[lookupID] = streamSettings;
}
else
{
streamSettings = it->second;
}
streamSettings->_startHeight = startHeight;
streamSettings->_startWidth = startWidth;
streamSettings->_stopWidth = stopWidth;
streamSettings->_stopHeight = stopHeight;
_offScreenSurfaceUpdated = false;
}
void DirectDrawChannel::SetStreamCropSettings(VideoRenderDirectDraw* DDObj,
short streamId,
float startWidth,
float startHeight,
float stopWidth,
float stopHeight)
{
unsigned long long lookupID = reinterpret_cast<unsigned long long> (DDObj);
lookupID &= 0xffffffffffffffe0;
lookupID <<= 11;
lookupID += streamId;
CriticalSectionScoped cs(*_critSect);
DirectDrawStreamSettings* streamSettings = NULL;
std::map<unsigned long long, DirectDrawStreamSettings*>::iterator it =
_streamIdToSettings.find(lookupID);
if (it == _streamIdToSettings.end())
{
streamSettings = new DirectDrawStreamSettings();
_streamIdToSettings[streamId] = streamSettings;
}
else
{
streamSettings = it->second;
}
streamSettings->_cropStartWidth = startWidth;
streamSettings->_cropStopWidth = stopWidth;
streamSettings->_cropStartHeight = startHeight;
streamSettings->_cropStopHeight = stopHeight;
}
int DirectDrawChannel::GetStreamSettings(VideoRenderDirectDraw* DDObj,
short streamId, float& startWidth,
float& startHeight,
float& stopWidth,
float& stopHeight)
{
CriticalSectionScoped cs(*_critSect);
unsigned long long lookupID = reinterpret_cast<unsigned long long> (DDObj);
lookupID &= 0xffffffffffffffe0;
lookupID <<= 11;
lookupID += streamId;
DirectDrawStreamSettings* streamSettings = NULL;
std::map<unsigned long long, DirectDrawStreamSettings*>::iterator it =
_streamIdToSettings.find(lookupID);
if (it == _streamIdToSettings.end())
{
// Didn't find this stream...
return -1;
}
streamSettings = it->second;
startWidth = streamSettings->_startWidth;
startHeight = streamSettings->_startHeight;
stopWidth = streamSettings->_stopWidth;
stopHeight = streamSettings->_stopHeight;
return 0;
}
bool DirectDrawChannel::IsOffScreenSurfaceUpdated(VideoRenderDirectDraw* DDobj)
{
CriticalSectionScoped cs(*_critSect);
return _offScreenSurfaceUpdated;
}
void DirectDrawChannel::GetLargestSize(RECT* mixingRect)
{
CriticalSectionScoped cs(*_critSect);
if (mixingRect)
{
if (mixingRect->bottom < _height)
{
mixingRect->bottom = _height;
}
if (mixingRect->right < _width)
{
mixingRect->right = _width;
}
}
}
int DirectDrawChannel::ChangeDeliverColorFormat(bool useScreenType)
{
_deliverInScreenType = useScreenType;
return FrameSizeChange(0, 0, 0);
}
WebRtc_Word32 DirectDrawChannel::RenderFrame(const WebRtc_UWord32 streamId,
VideoFrame& videoFrame)
{
CriticalSectionScoped cs(*_critSect);
if (_width != videoFrame.Width() || _height != videoFrame.Height())
{
if (FrameSizeChange(videoFrame.Width(), videoFrame.Height(), 1) == -1)
{
return -1;
}
}
return DeliverFrame(videoFrame.Buffer(), videoFrame.Length(),
videoFrame.TimeStamp());
}
int DirectDrawChannel::FrameSizeChange(int width, int height,
int numberOfStreams)
{
CriticalSectionScoped cs(*_critSect);
if (_directDraw == NULL)
{
return -1; // signal that we are not ready for the change
}
if (_width == width && _height == height && _offScreenSurface
&& _offScreenSurfaceNext)
{
_numberOfStreams = numberOfStreams;
return 0;
}
if (_offScreenSurface)
{
_offScreenSurface->Release();
_offScreenSurface = NULL;
}
if (_offScreenSurfaceNext)
{
_offScreenSurfaceNext->Release();
_offScreenSurfaceNext = NULL;
}
if (width && height)
{
_width = width;
_height = height;
_numberOfStreams = numberOfStreams;
}
// create this channels offscreen buffer
DirectDrawSurfaceDesc ddsd;
HRESULT ddrval = DD_OK;
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
ddsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY;
ddsd.dwHeight = _height;
ddsd.dwWidth = _width;
/*
char logStr[256];
_snprintf(logStr,256, "offscreen H:%d W:%d \n",_height, _width);
OutputDebugString(logStr);
*/
//Fix for bad video driver on HP Mini. If it takes to long time to deliver a frame - try to blit using the same pixel format as used by the screen.
if (_deliverInScreenType && _screenVideoType != kUnknown)
{
//The HP mini netbook, which this fix for, uses the VIA processor.
//The measuring shows that this fix will impact systems with Intel processor, including Atom.
//So let's disable it here. If we really need this for VIA processor, we should have additional logic to detect
//the processor model.
//WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, "DirectDrawChannel changing to screen video type");
//_blitVideoType=_screenVideoType;
}
else
{
WEBRTC_TRACE(
kTraceInfo,
kTraceVideo,
-1,
"DirectDrawChannel changing to originial blit video type %d",
_originalBlitVideoType);
_blitVideoType = _originalBlitVideoType;
}
WEBRTC_TRACE(
kTraceInfo,
kTraceVideo,
-1,
"DirectDrawChannel::FrameSizeChange height %d, width %d, _blitVideoType %d",
ddsd.dwHeight, ddsd.dwWidth, _blitVideoType);
switch (_blitVideoType)
{
case kYV12:
{
ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
ddsd.ddpfPixelFormat.dwFourCC = MAKEFOURCC('Y', 'V', '1', '2');
}
break;
case kYUY2:
{
ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
ddsd.ddpfPixelFormat.dwFourCC = MAKEFOURCC('Y', 'U', 'Y', '2');
}
break;
case kUYVY:
{
ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
ddsd.ddpfPixelFormat.dwFourCC = MAKEFOURCC('U', 'Y', 'V', 'Y');
}
break;
case kIYUV:
{
ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
ddsd.ddpfPixelFormat.dwFourCC = MAKEFOURCC('I', 'Y', 'U', 'V');
}
break;
case kARGB:
{
ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB;
ddsd.ddpfPixelFormat.dwRGBBitCount = 32;
ddsd.ddpfPixelFormat.dwRBitMask = 0xff0000;
ddsd.ddpfPixelFormat.dwGBitMask = 0xff00;
ddsd.ddpfPixelFormat.dwBBitMask = 0xff;
ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0;
}
break;
case kRGB24:
{
ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB;
ddsd.ddpfPixelFormat.dwRGBBitCount = 24;
ddsd.ddpfPixelFormat.dwRBitMask = 0xff0000;
ddsd.ddpfPixelFormat.dwGBitMask = 0xff00;
ddsd.ddpfPixelFormat.dwBBitMask = 0xff;
ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0;
}
break;
case kRGB565:
{
ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB;
ddsd.ddpfPixelFormat.dwRGBBitCount = 16;
ddsd.ddpfPixelFormat.dwRBitMask = 0x0000F800;
ddsd.ddpfPixelFormat.dwGBitMask = 0x000007e0;
ddsd.ddpfPixelFormat.dwBBitMask = 0x0000001F;
ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0;
}
break;
case kARGB4444:
{
ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB;
ddsd.ddpfPixelFormat.dwRGBBitCount = 16;
ddsd.ddpfPixelFormat.dwRBitMask = 0x00000f00;
ddsd.ddpfPixelFormat.dwGBitMask = 0x000000f0;
ddsd.ddpfPixelFormat.dwBBitMask = 0x0000000f;
ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0;
break;
}
case kARGB1555:
{
ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
ddsd.ddpfPixelFormat.dwFlags = DDPF_RGB;
ddsd.ddpfPixelFormat.dwRGBBitCount = 16;
ddsd.ddpfPixelFormat.dwRBitMask = 0x00007C00;
ddsd.ddpfPixelFormat.dwGBitMask = 0x3E0;
ddsd.ddpfPixelFormat.dwBBitMask = 0x1F;
ddsd.ddpfPixelFormat.dwRGBAlphaBitMask = 0;
break;
}
case kI420:
{
ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
ddsd.ddpfPixelFormat.dwFlags = DDPF_FOURCC;
ddsd.ddpfPixelFormat.dwFourCC = MAKEFOURCC('I', '4', '2', '0');
}
break;
default:
ddrval = S_FALSE;
}
if (ddrval == DD_OK)
{
if (!_owner->IsPrimaryOrMixingSurfaceOnSystem())
{
ddrval
= _directDraw->CreateSurface(&ddsd, &_offScreenSurface,
NULL);
if (FAILED(ddrval))
{
WEBRTC_TRACE(
kTraceInfo,
kTraceVideo,
-1,
"CreateSurface failed for _offScreenSurface on VideoMemory, trying on System Memory");
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
ddsd.dwHeight = _height;
ddsd.dwWidth = _width;
ddsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY;
_blitVideoType = kARGB;
ddrval = _directDraw->CreateSurface(&ddsd, &_offScreenSurface,
NULL);
if (FAILED(ddrval))
{
WEBRTC_TRACE(
kTraceError,
kTraceVideo,
-1,
"DirectDraw failed to CreateSurface _offScreenSurface using SystemMemory: 0x%x",
ddrval);
}
ddrval = _directDraw->CreateSurface(&ddsd,
&_offScreenSurfaceNext,
NULL);
if (FAILED(ddrval))
{
WEBRTC_TRACE(
kTraceError,
kTraceVideo,
-1,
"DirectDraw failed to CreateSurface _offScreenSurfaceNext using SystemMemory: 0x%x",
ddrval);
}
}
else
{
ddrval = _directDraw->CreateSurface(&ddsd,
&_offScreenSurfaceNext,
NULL);
if (ddrval == DDERR_OUTOFVIDEOMEMORY)
{
WEBRTC_TRACE(
kTraceInfo,
kTraceVideo,
-1,
"CreateSurface failed for _offScreenSurfaceNext on VideoMemory, trying on System Memory");
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
ddsd.dwHeight = _height;
ddsd.dwWidth = _width;
ddsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY;
_blitVideoType = kARGB;
ddrval = _directDraw->CreateSurface(&ddsd,
&_offScreenSurfaceNext,
NULL);
if (FAILED(ddrval))
{
WEBRTC_TRACE(
kTraceError,
kTraceVideo,
-1,
"DirectDraw failed to CreateSurface _offScreenSurfaceNext using SystemMemory: 0x%x",
ddrval);
}
}
}
}
else
{
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
ddsd.dwHeight = _height;
ddsd.dwWidth = _width;
ddsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY;
if (_owner->CanBltFourCC())
{
_blitVideoType = kARGB;
}
else
{
_blitVideoType = _originalBlitVideoType;
}
ddrval
= _directDraw->CreateSurface(&ddsd, &_offScreenSurface,
NULL);
if (FAILED(ddrval))
{
WEBRTC_TRACE(
kTraceError,
kTraceVideo,
-1,
"DirectDraw failed to CreateSurface _offScreenSurface using SystemMemory: 0x%x",
ddrval);
}
ddrval = _directDraw->CreateSurface(&ddsd, &_offScreenSurfaceNext,
NULL);
if (FAILED(ddrval))
{
WEBRTC_TRACE(
kTraceError,
kTraceVideo,
-1,
"DirectDraw failed to CreateSurface _offScreenSurfaceNext using SystemMemory: 0x%x",
ddrval);
}
}
}
if (FAILED(ddrval))
{
// failed to change size
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw failed to CreateSurface : 0x%x", ddrval);
return -1;
}
return 0;
}
int DirectDrawChannel::DeliverFrame(unsigned char* buffer, int bufferSize,
unsigned int /*timeStamp90KHz*/)
{
CriticalSectionScoped cs(*_critSect);
if (CalcBufferSize(_incomingVideoType, _width, _height)
!= bufferSize)
{
// sanity
return -1;
}
if (!_offScreenSurface || !_offScreenSurfaceNext)
{
if (_width && _height && _numberOfStreams)
{
// our surface was lost recreate it
FrameSizeChange(_width, _height, _numberOfStreams);
}
return -1;
}
if (_offScreenSurface->IsLost() == DDERR_SURFACELOST)
{
HRESULT ddrval = _offScreenSurface->Restore();
if (ddrval != DD_OK)
{
// failed to restore our surface remove it and it will be re-created in next frame
_offScreenSurface->Release();
_offScreenSurface = NULL;
_offScreenSurfaceNext->Release();
_offScreenSurfaceNext = NULL;
return -1;
}
ddrval = _offScreenSurfaceNext->Restore();
if (ddrval != DD_OK)
{
// failed to restore our surface remove it and it will be re-created in next frame
_offScreenSurface->Release();
_offScreenSurface = NULL;
_offScreenSurfaceNext->Release();
_offScreenSurfaceNext = NULL;
return -1;
}
}
_doubleBuffer = false;
// check if _offScreenSurfaceUpdated is true
DirectDrawSurface* offScreenSurface = _offScreenSurface;
{
if (_offScreenSurfaceUpdated)
{
// this frame is not yet rendered
offScreenSurface = _offScreenSurfaceNext;
_doubleBuffer = true;
}
}
DirectDrawSurfaceDesc ddsd;
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
HRESULT ddrval = offScreenSurface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL);
if (ddrval == DDERR_SURFACELOST)
{
WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
"DirectDrawChannel::DeliverFrame offScreenSurface lost");
ddrval = offScreenSurface->Restore();
if (ddrval != DD_OK)
{
// failed to restore our surface remove it and it will be re-created in next frame
_offScreenSurface->Release();
_offScreenSurface = NULL;
_offScreenSurfaceNext->Release();
_offScreenSurfaceNext = NULL;
return -1;
}
return 0;
}
if (ddrval != DD_OK)
{
WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
"DirectDrawChannel::DeliverFrame failed to lock");
// failed to lock our surface remove it and it will be re-created in next frame
_offScreenSurface->Release();
_offScreenSurface = NULL;
_offScreenSurfaceNext->Release();
_offScreenSurfaceNext = NULL;
return -1;
}
unsigned char* ptr = (unsigned char*) ddsd.lpSurface;
// ddsd.lPitch; distance in bytes
switch (_incomingVideoType)
{
case kI420:
{
switch (_blitVideoType)
{
case kYUY2:
case kUYVY:
case kIYUV: // same as kYV12
case kYV12:
ConvertFromI420(buffer, _width,
_blitVideoType, 0,
_width, _height,
ptr);
break;
case kRGB24:
{
_tempRenderBuffer.VerifyAndAllocate(_width * _height * 3);
unsigned char *ptrTempBuffer = _tempRenderBuffer.Buffer();
ConvertFromI420(buffer, _width, kRGB24, 0, _width, _height,
ptrTempBuffer);
for (int i = 0; i < _height; i++)
{
memcpy(ptr, ptrTempBuffer, _width * 3);
ptrTempBuffer += _width * 3;
ptr += ddsd.lPitch;
}
break;
}
case kARGB:
ConvertFromI420(buffer, ddsd.lPitch, kARGB, 0,
_width, _height, ptr);
break;
case kARGB4444:
ConvertI420ToARGB4444(buffer, ptr, _width, _height,
(ddsd.lPitch >> 1) - _width);
break;
case kARGB1555:
ConvertI420ToARGB1555(buffer, ptr, _width, _height,
(ddsd.lPitch >> 1) - _width);
break;
case kRGB565:
{
_tempRenderBuffer.VerifyAndAllocate(_width * _height * 2);
unsigned char *ptrTempBuffer = _tempRenderBuffer.Buffer();
ConvertI420ToRGB565(buffer, ptrTempBuffer, _width, _height);
ptr += ddsd.lPitch * (_height - 1);
for (int i = 0; i < _height; i++)
{
memcpy(ptr, ptrTempBuffer, _width * 2);
ptrTempBuffer += _width * 2;
ptr -= ddsd.lPitch;
}
break;
}
default:
assert(!"DirectDrawChannel::DeliverFrame unknown blitVideoType");
WEBRTC_TRACE(
kTraceError,
kTraceVideo,
-1,
"DirectDrawChannel::DeliverFrame unknown blitVideoType %d",
_blitVideoType);
}
break;
}
default:
assert(!"DirectDrawChannel::DeliverFrame wrong incomming video type");
WEBRTC_TRACE(
kTraceError,
kTraceVideo,
-1,
"DirectDrawChannel::DeliverFrame wrong incomming %d",
_incomingVideoType);
}
_offScreenSurfaceUpdated = true;
offScreenSurface->Unlock(NULL);
return 0;
}
int DirectDrawChannel::BlitFromOffscreenBufferToMixingBuffer(
VideoRenderDirectDraw* DDobj,
short streamID,
DirectDrawSurface* mixingSurface,
RECT &hwndRect,
bool demuxing)
{
HRESULT ddrval;
RECT srcRect;
RECT dstRect;
DirectDrawStreamSettings* streamSettings = NULL;
unsigned long long lookupID = reinterpret_cast<unsigned long long> (DDobj);
lookupID &= 0xffffffffffffffe0;
lookupID <<= 11;
lookupID += streamID;
CriticalSectionScoped cs(*_critSect);
if (_offScreenSurface == NULL)
{
// The offscreen surface has been deleted but not restored yet
return 0;
}
if (mixingSurface == NULL)
{
// Not a valid input argument
return 0;
}
std::map<unsigned long long, DirectDrawStreamSettings*>::iterator it =
_streamIdToSettings.find(lookupID);
if (it == _streamIdToSettings.end())
{
// ignore this stream id
return 0;
}
streamSettings = it->second;
int numberOfStreams = _numberOfStreams;
if (!demuxing)
{
numberOfStreams = 1; // treat as one stream if we only have one config
}
switch (numberOfStreams)
{
case 0:
return 0;
case 1:
{
// no demux
if (streamID > 0)
return 0;
::SetRect(&srcRect, int(_width * streamSettings->_cropStartWidth),
int(_height * streamSettings->_cropStartHeight),
int(_width * streamSettings->_cropStopWidth), int(_height
* streamSettings->_cropStopHeight));
::SetRect(&dstRect, int(hwndRect.right
* streamSettings->_startWidth), int(hwndRect.bottom
* streamSettings->_startHeight), int(hwndRect.right
* streamSettings->_stopWidth), int(hwndRect.bottom
* streamSettings->_stopHeight));
}
break;
case 2:
case 3:
case 4:
// classic quadrant demux
{
int width = _width >> 1;
int height = _height >> 1;
::SetRect(&srcRect, int(width * streamSettings->_cropStartWidth),
int(height * streamSettings->_cropStartHeight), int(width
* streamSettings->_cropStopWidth), int(height
* streamSettings->_cropStopHeight));
::SetRect(&dstRect, int(hwndRect.right
* streamSettings->_startWidth), int(hwndRect.bottom
* streamSettings->_startHeight), int(hwndRect.right
* streamSettings->_stopWidth), int(hwndRect.bottom
* streamSettings->_stopHeight));
// stream id to select quadrant
if (streamID == 1)
{
::OffsetRect(&srcRect, width, 0);
}
if (streamID == 2)
{
::OffsetRect(&srcRect, 0, height);
}
if (streamID == 3)
{
::OffsetRect(&srcRect, width, height);
}
}
break;
case 5:
case 6:
{
const int width = (_width / (3 * 16)) * 16;
const int widthMidCol = width + ((_width % (16 * 3)) / 16) * 16;
const int height = _height / (2 * 16) * 16;
if (streamID == 1 || streamID == 4)
{
::SetRect(&srcRect, int(widthMidCol
* streamSettings->_cropStartWidth), int(height
* streamSettings->_cropStartHeight), int(widthMidCol
* streamSettings->_cropStopWidth), int(height
* streamSettings->_cropStopHeight));
}
else
{
::SetRect(&srcRect,
int(width * streamSettings->_cropStartWidth),
int(height * streamSettings->_cropStartHeight),
int(width * streamSettings->_cropStopWidth),
int(height * streamSettings->_cropStopHeight));
}
::SetRect(&dstRect, int(hwndRect.right
* streamSettings->_startWidth), int(hwndRect.bottom
* streamSettings->_startHeight), int(hwndRect.right
* streamSettings->_stopWidth), int(hwndRect.bottom
* streamSettings->_stopHeight));
// stream id to select quadrant
switch (streamID)
{
case 1:
::OffsetRect(&srcRect, width, 0);
break;
case 2:
::OffsetRect(&srcRect, width + widthMidCol, 0);
break;
case 3:
::OffsetRect(&srcRect, 0, height);
break;
case 4:
::OffsetRect(&srcRect, width, height);
break;
case 5:
::OffsetRect(&srcRect, width + widthMidCol, height);
break;
}
}
break;
case 7:
case 8:
case 9:
{
const int width = (_width / (3 * 16)) * 16;
const int widthMidCol = width + ((_width % (16 * 3)) / 16) * 16;
const int height = _height / (3 * 16) * 16;
const int heightMidRow = height + ((_height % (16 * 3)) / 16) * 16;
::SetRect(&dstRect, int(hwndRect.right
* streamSettings->_startWidth), int(hwndRect.bottom
* streamSettings->_startHeight), int(hwndRect.right
* streamSettings->_stopWidth), int(hwndRect.bottom
* streamSettings->_stopHeight));
switch (streamID)
{
case 0:
//Size
::SetRect(&srcRect, int(width
* streamSettings->_cropStartWidth), int(height
* streamSettings->_cropStartHeight), int(width
* streamSettings->_cropStopWidth), int(height
* streamSettings->_cropStopHeight));
//Position
::OffsetRect(&srcRect, 0, 0);
break;
case 1:
::SetRect(
&srcRect,
int(widthMidCol * streamSettings->_cropStartWidth),
int(height * streamSettings->_cropStartHeight),
int(widthMidCol * streamSettings->_cropStopWidth),
int(height * streamSettings->_cropStopHeight));
::OffsetRect(&srcRect, width, 0);
break;
case 2:
::SetRect(&srcRect, int(width
* streamSettings->_cropStartWidth), int(height
* streamSettings->_cropStartHeight), int(width
* streamSettings->_cropStopWidth), int(height
* streamSettings->_cropStopHeight));
::OffsetRect(&srcRect, width + widthMidCol, 0);
break;
case 3:
::SetRect(&srcRect, int(width
* streamSettings->_cropStartWidth),
int(heightMidRow
* streamSettings->_cropStartHeight),
int(width * streamSettings->_cropStopWidth),
int(heightMidRow
* streamSettings->_cropStopHeight));
::OffsetRect(&srcRect, 0, height);
break;
case 4:
::SetRect(
&srcRect,
int(widthMidCol * streamSettings->_cropStartWidth),
int(heightMidRow
* streamSettings->_cropStartHeight),
int(widthMidCol * streamSettings->_cropStopWidth),
int(heightMidRow
* streamSettings->_cropStopHeight));
::OffsetRect(&srcRect, width, height);
break;
case 5:
::SetRect(&srcRect, int(width
* streamSettings->_cropStartWidth),
int(heightMidRow
* streamSettings->_cropStartHeight),
int(width * streamSettings->_cropStopWidth),
int(heightMidRow
* streamSettings->_cropStopHeight));
::OffsetRect(&srcRect, width + widthMidCol, height);
break;
case 6:
::SetRect(&srcRect, int(width
* streamSettings->_cropStartWidth), int(height
* streamSettings->_cropStartHeight), int(width
* streamSettings->_cropStopWidth), int(height
* streamSettings->_cropStopHeight));
::OffsetRect(&srcRect, 0, height + heightMidRow);
break;
case 7:
::SetRect(
&srcRect,
int(widthMidCol * streamSettings->_cropStartWidth),
int(height * streamSettings->_cropStartHeight),
int(widthMidCol * streamSettings->_cropStopWidth),
int(height * streamSettings->_cropStopHeight));
::OffsetRect(&srcRect, width, height + heightMidRow);
break;
case 8:
::SetRect(&srcRect, int(width
* streamSettings->_cropStartWidth), int(height
* streamSettings->_cropStartHeight), int(width
* streamSettings->_cropStopWidth), int(height
* streamSettings->_cropStopHeight));
::OffsetRect(&srcRect, width + widthMidCol, height
+ heightMidRow);
break;
}
}
break;
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
default:
{
::SetRect(&srcRect, int(_width * streamSettings->_cropStartWidth),
int(_height * streamSettings->_cropStartHeight),
int(_width * streamSettings->_cropStopWidth), int(_height
* streamSettings->_cropStopHeight));
::SetRect(&dstRect, int(hwndRect.right
* streamSettings->_startWidth), int(hwndRect.bottom
* streamSettings->_startHeight), int(hwndRect.right
* streamSettings->_stopWidth), int(hwndRect.bottom
* streamSettings->_stopHeight));
}
}
if (dstRect.right > hwndRect.right)
{
srcRect.right -= (int) ((float) (srcRect.right - srcRect.left)
* ((float) (dstRect.right - hwndRect.right)
/ (float) (dstRect.right - dstRect.left)));
dstRect.right = hwndRect.right;
}
if (dstRect.left < hwndRect.left)
{
srcRect.left += (int) ((float) (srcRect.right - srcRect.left)
* ((float) (hwndRect.left - dstRect.left)
/ (float) (dstRect.right - dstRect.left)));
dstRect.left = hwndRect.left;
}
if (dstRect.bottom > hwndRect.bottom)
{
srcRect.bottom -= (int) ((float) (srcRect.bottom - srcRect.top)
* ((float) (dstRect.bottom - hwndRect.bottom)
/ (float) (dstRect.bottom - dstRect.top)));
dstRect.bottom = hwndRect.bottom;
}
if (dstRect.top < hwndRect.top)
{
srcRect.top += (int) ((float) (srcRect.bottom - srcRect.top)
* ((float) (hwndRect.top - dstRect.top)
/ (float) (dstRect.bottom - dstRect.top)));
dstRect.top = hwndRect.top;
}
DDBLTFX ddbltfx;
ZeroMemory(&ddbltfx, sizeof(ddbltfx));
ddbltfx.dwSize = sizeof(ddbltfx);
ddbltfx.dwDDFX = DDBLTFX_NOTEARING;
// wait for the _mixingSurface to be available
ddrval = mixingSurface->Blt(&dstRect, _offScreenSurface, &srcRect,
DDBLT_WAIT | DDBLT_DDFX, &ddbltfx);
if (ddrval == DDERR_SURFACELOST)
{
WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
"mixingSurface->Blt surface lost");
ddrval = mixingSurface->Restore();
if (ddrval != DD_OK)
{
// we dont own the surface just report the error
return -1;
}
}
else if (ddrval == DDERR_INVALIDRECT)
{
WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
"mixingSurface->Blt DDERR_INVALIDRECT");
WEBRTC_TRACE(
kTraceError,
kTraceVideo,
-1,
"dstRect co-ordinates - top: %d left: %d bottom: %d right: %d",
dstRect.top, dstRect.left, dstRect.bottom, dstRect.right);
WEBRTC_TRACE(
kTraceError,
kTraceVideo,
-1,
"srcRect co-ordinates - top: %d left: %d bottom: %d right: %d",
srcRect.top, srcRect.left, srcRect.bottom, srcRect.right);
// ignore
}
else if (ddrval != DD_OK)
{
WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
"mixingSurface->Blt !DD_OK");
WEBRTC_TRACE(
kTraceError,
kTraceVideo,
-1,
"DirectDraw blt mixingSurface BlitFromOffscreenBufferToMixingBuffer error 0x%x ",
ddrval);
//logging the co-ordinates and hwnd
WEBRTC_TRACE(
kTraceError,
kTraceVideo,
-1,
"dstRect co-ordinates - top: %d left: %d bottom: %d right: %d",
dstRect.top, dstRect.left, dstRect.bottom, dstRect.right);
WEBRTC_TRACE(
kTraceError,
kTraceVideo,
-1,
"srcRect co-ordinates - top: %d left: %d bottom: %d right: %d",
srcRect.top, srcRect.left, srcRect.bottom, srcRect.right);
/* char logStr[256];
_snprintf(logStr,256, "srcRect T:%d L:%d B:%d R:%d\n",srcRect.top, srcRect.left, srcRect.bottom, srcRect.right);
OutputDebugString(logStr);
char logStr1[256];
_snprintf(logStr1,256, "dstRect T:%d L:%d B:%d R:%d\n",dstRect.top, dstRect.left, dstRect.bottom, dstRect.right);
OutputDebugString(logStr1);
char logStr2[256];
_snprintf(logStr2,256, "error 0x:%x \n",ddrval);
OutputDebugString(logStr2);
*/
// we dont own the surface just report the error
return -1;
}
if (_doubleBuffer)
{
DirectDrawSurface* oldOffScreenSurface = _offScreenSurface;
_offScreenSurface = _offScreenSurfaceNext;
_offScreenSurfaceNext = oldOffScreenSurface;
_doubleBuffer = false;
}
else
{
_offScreenSurfaceUpdated = false;
}
return 0;
}
/**
*
* VideoRenderDirectDraw
*
*
*/
VideoRenderDirectDraw::VideoRenderDirectDraw(Trace* trace,
HWND hWnd, bool fullscreen) :
_trace(trace),
_confCritSect(CriticalSectionWrapper::CreateCriticalSection()),
_fullscreen(fullscreen),
_demuxing(false),
_transparentBackground(false),
_supportTransparency(false),
_canStretch(false),
_canMirrorLeftRight(false),
_clearMixingSurface(false),
_deliverInScreenType(false),
_renderModeWaitForCorrectScanLine(false),
_deliverInHalfFrameRate(false),
_deliverInQuarterFrameRate(false),
_bCanBltFourcc(true),
_frameChanged(false),
_processCount(0),
_hWnd(hWnd),
_screenRect(),
_mixingRect(),
_incomingVideoType(kUnknown),
_blitVideoType(kUnknown),
_rgbVideoType(kUnknown),
_directDraw(NULL),
_primarySurface(NULL),
_backSurface(NULL),
_mixingSurface(NULL),
_bitmapSettings(),
_textSettings(),
_directDrawChannels(),
_directDrawZorder(),
_fullScreenWaitEvent(EventWrapper::Create()),
_screenEvent(EventWrapper::Create()),
_screenRenderThread(
ThreadWrapper::CreateThread(
RemoteRenderingThreadProc,
this,
kRealtimePriority,
"Video_directdraw_thread")),
_blit(true), _lastRenderModeCpuUsage(-1), _totalMemory(-1),
_availableMemory(-1), _systemCPUUsage(0), _maxAllowedRenderTime(0),
_nrOfTooLongRenderTimes(0),
_isPrimaryOrMixingSurfaceOnSystem(false)
{
SetRect(&_screenRect, 0, 0, 0, 0);
SetRect(&_mixingRect, 0, 0, 0, 0);
SetRect(&_originalHwndRect, 0, 0, 0, 0);
::GetClientRect(_hWnd, &_hwndRect);
}
VideoRenderDirectDraw::~VideoRenderDirectDraw()
{
ThreadWrapper* temp = _screenRenderThread;
_screenRenderThread = NULL;
if (temp)
{
temp->SetNotAlive();
_screenEvent->Set();
_screenEvent->StopTimer();
_fullScreenWaitEvent->StopTimer();
if (temp->Stop())
{
delete temp;
}
}
delete _screenEvent;
delete _fullScreenWaitEvent;
std::map<int, DirectDrawChannel*>::iterator it;
it = _directDrawChannels.begin();
while (it != _directDrawChannels.end())
{
it->second->Release();
it = _directDrawChannels.erase(it);
}
if (_primarySurface)
{
_primarySurface->Release();
}
if (_mixingSurface)
{
_mixingSurface->Release();
}
std::map<unsigned char, DirectDrawBitmapSettings*>::iterator bitIt;
bitIt = _bitmapSettings.begin();
while (_bitmapSettings.end() != bitIt)
{
delete bitIt->second;
bitIt = _bitmapSettings.erase(bitIt);
}
std::map<unsigned char, DirectDrawTextSettings*>::iterator textIt;
textIt = _textSettings.begin();
while (_textSettings.end() != textIt)
{
delete textIt->second;
textIt = _textSettings.erase(textIt);
}
if (_directDraw)
{
_directDraw->Release();
if (_fullscreen)
{
// restore hwnd to original size and position
::SetWindowPos(_hWnd, HWND_NOTOPMOST, _originalHwndRect.left,
_originalHwndRect.top, _originalHwndRect.right
- _originalHwndRect.left,
_originalHwndRect.bottom - _originalHwndRect.top,
SWP_FRAMECHANGED);
::RedrawWindow(_hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW
| RDW_ERASE);
::RedrawWindow(NULL, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW
| RDW_ERASE);
}
}
delete _confCritSect;
}
WebRtc_Word32 VideoRenderDirectDraw::Init()
{
int retVal = 0;
HRESULT ddrval = DirectDrawCreateEx(NULL, (void**) &_directDraw,
IID_IDirectDraw7, NULL);
if (FAILED(ddrval) || NULL == _directDraw)
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"Failed to created DirectDraw7 object");
return -1;
//return VIDEO_DIRECT_DRAW_FAILURE;
}
retVal = CheckCapabilities();
if (retVal != 0)
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw CheckCapabilities failed");
return retVal;
}
if (_hWnd)
{
retVal = CreatePrimarySurface();
if (retVal != 0)
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw failed to CreatePrimarySurface");
return retVal;
}
retVal = CreateMixingSurface();
if (retVal != 0)
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw failed to CreateMixingSurface");
return retVal;
}
if (_screenRenderThread)
{
unsigned int tid;
_screenRenderThread->Start(tid);
WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
"Screen Render thread started, thread id: %d", tid);
}
DWORD freq = 0;
_directDraw->GetMonitorFrequency(&freq);
if (freq == 0)
{
freq = 60;
}
// Do this now to not do it in each render process loop
_maxAllowedRenderTime = (int) (1000 / freq * 0.8F);
_nrOfTooLongRenderTimes = 0;
_screenEvent->StartTimer(true, 1000 / freq);
_deliverInScreenType = false;
_renderModeWaitForCorrectScanLine = false;
_deliverInHalfFrameRate = false;
_deliverInQuarterFrameRate = false;
_lastRenderModeCpuUsage = -1;
if (_fullscreen)
{
_fullScreenWaitEvent->StartTimer(true, 1);
}
WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
"Screen freq %d", freq);
}
WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
"Created DirectDraw object");
return 0;
}
WebRtc_Word32 VideoRenderDirectDraw::GetGraphicsMemory(
WebRtc_UWord64& totalMemory,
WebRtc_UWord64& availableMemory)
{
CriticalSectionScoped cs(*_confCritSect);
if (_totalMemory == -1 || _availableMemory == -1)
{
totalMemory = 0;
availableMemory = 0;
return -1;
}
totalMemory = _totalMemory;
availableMemory = _availableMemory;
return 0;
}
int VideoRenderDirectDraw::GetScreenResolution(int& screenWidth,
int& screenHeight)
{
CriticalSectionScoped cs(*_confCritSect);
screenWidth = _screenRect.right - _screenRect.left;
screenHeight = _screenRect.bottom - _screenRect.top;
return 0;
}
int VideoRenderDirectDraw::UpdateSystemCPUUsage(int systemCPU)
{
CriticalSectionScoped cs(*_confCritSect);
if (systemCPU <= 100 && systemCPU >= 0)
{
_systemCPUUsage = systemCPU;
}
return 0;
}
int VideoRenderDirectDraw::CheckCapabilities()
{
HRESULT ddrval = DD_OK;
DDCAPS ddcaps;
DDCAPS ddcapsEmul;
memset(&ddcaps, 0, sizeof(ddcaps));
memset(&ddcapsEmul, 0, sizeof(ddcapsEmul));
ddcaps.dwSize = sizeof(ddcaps);
ddcapsEmul.dwSize = sizeof(ddcapsEmul);
if (_directDraw == NULL)
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw object not created");
return -1;
//return VIDEO_DIRECT_DRAW_FAILURE;
}
if (IsRectEmpty(&_screenRect))
{
::GetWindowRect(GetDesktopWindow(), &_screenRect);
}
// Log Screen resolution
WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
"ScreenRect. Top: %d, left: %d, bottom: %d, right: %d",
_screenRect.top, _screenRect.left, _screenRect.bottom,
_screenRect.right);
bool fullAccelerationEnabled = false;
bool badDriver = false;
VideoRenderWindowsImpl::CheckHWDriver(badDriver, fullAccelerationEnabled);
if (!fullAccelerationEnabled)
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"Direct draw Hardware acceleration is not enabled.");
return -1;
//return VIDEO_DIRECT_DRAW_HWACC_NOT_ENABLED;
}
// ddcaps supported by the HW
// ddcapsEmul supported by the OS emulating the HW
ddrval = _directDraw->GetCaps(&ddcaps, &ddcapsEmul);
if (ddrval != DD_OK)
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw HW: could not get capabilities: %x", ddrval);
return -1;
//return VIDEO_DIRECT_DRAW_FAILURE;
}
unsigned int minVideoMemory = 3 * 4 * (_screenRect.right
* _screenRect.bottom); // assuming ARGB size (4 bytes)
// Store the memory for possible calls to GetMemory()
_totalMemory = ddcaps.dwVidMemTotal;
_availableMemory = ddcaps.dwVidMemFree;
if (ddcaps.dwVidMemFree < minVideoMemory)
{
WEBRTC_TRACE(
kTraceError,
kTraceVideo,
-1,
"DirectDraw HW does not have enough memory, freeMem:%d, requiredMem:%d",
ddcaps.dwVidMemFree, minVideoMemory);
// If memory is not available on the Video Card...allocate it on RAM
}
else
{
WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
"DirectDraw video memory, freeMem:%d, totalMem:%d",
ddcaps.dwVidMemFree, ddcaps.dwVidMemTotal);
}
/*
DirectDrawCaps ddsCaps ;
ZeroMemory(&ddsCaps, sizeof(ddsCaps)) ;
ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY;
DWORD memTotal=0;
DWORD memFree=0;
ddrval = _directDraw->GetAvailableVidMem(&ddsCaps, &memTotal, &memFree);
if(ddrval == DD_OK)
{
WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1, "DirectDraw video memory, freeMem:%d, totalMem:%d", memFree, memTotal);
}
*/
// Determine if the hardware supports overlay deinterlacing
// bCanDeinterlace = (ddcaps.dwCaps2 & DDCAPS2_CANFLIPODDEVEN) ? 1 : 0;
// this fail since we check before we set the mode
// bool bCanFlip =(ddcaps.dwCaps & DDSCAPS_FLIP) ? 1 : 0;
// Determine if the hardware supports colorkeying
_supportTransparency = (ddcaps.dwCaps & DDCAPS_COLORKEY) ? 1 : 0;
if (_supportTransparency)
{
WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
"DirectDraw support colorkey");
}
else
{
WEBRTC_TRACE(kTraceWarning, kTraceVideo, -1,
"DirectDraw don't support colorkey");
}
if (ddcaps.dwCaps2 & DDCAPS2_CANRENDERWINDOWED)
{
// required for _directDraw->FlipToGDISurface();
WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
"DirectDraw support CANRENDERWINDOWED");
}
else
{
WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
"DirectDraw don't support CANRENDERWINDOWED");
}
// Determine if the hardware supports scaling during a blit
_canStretch = (ddcaps.dwCaps & DDCAPS_BLTSTRETCH) ? 1 : 0;
if (_canStretch)
{
WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
"DirectDraw blit can stretch");
}
else
{
WEBRTC_TRACE(kTraceWarning, kTraceVideo, -1,
"DirectDraw blit can't stretch");
}
_canMirrorLeftRight = (ddcaps.dwFXAlphaCaps & DDBLTFX_MIRRORLEFTRIGHT) ? 1
: 0;
if (_canMirrorLeftRight)
{
WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
"DirectDraw mirroring is supported");
}
else
{
WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
"DirectDraw mirroring is not supported");
}
// Determine if the hardware supports color conversion during a blit
_bCanBltFourcc = (ddcaps.dwCaps & DDCAPS_BLTFOURCC) ? 1 : 0;
if (_bCanBltFourcc)
_bCanBltFourcc = (ddcaps.dwCKeyCaps & DDCKEYCAPS_DESTBLT) ? 1 : 0;
if (_bCanBltFourcc)
{
WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
"DirectDraw can blit Fourcc");
DWORD i_codes;
ddrval = _directDraw->GetFourCCCodes(&i_codes, NULL);
if (i_codes > 0)
{
DWORD* pi_codes = new DWORD[i_codes];
ddrval = _directDraw->GetFourCCCodes(&i_codes, pi_codes);
for (unsigned int i = 0; i < i_codes && _blitVideoType
!= kI420; i++)
{
DWORD w = pi_codes[i];
switch (w)
{
case MAKEFOURCC('I', '4', '2', '0'):
// _blitVideoType = kI420;
// not enabled since its not tested
WEBRTC_TRACE(kTraceInfo, kTraceVideo,
-1, "DirectDraw support I420");
break;
case MAKEFOURCC('I', 'Y', 'U', 'V'): // same as YV12
// _blitVideoType = kIYUV;
// not enabled since its not tested
WEBRTC_TRACE(kTraceInfo, kTraceVideo,
-1, "DirectDraw support IYUV");
break;
case MAKEFOURCC('U', 'Y', 'N', 'V'): // same shit different name
WEBRTC_TRACE(kTraceInfo, kTraceVideo,
-1, "DirectDraw support UYNV");
// not enabled since its not tested
break;
case MAKEFOURCC('Y', '4', '2', '2'): // same shit different name
WEBRTC_TRACE(kTraceInfo, kTraceVideo,
-1, "DirectDraw support Y422");
// not enabled since its not tested
break;
case MAKEFOURCC('Y', 'U', 'N', 'V'): // same shit different name
WEBRTC_TRACE(kTraceInfo, kTraceVideo,
-1, "DirectDraw support YUNV");
// not enabled since its not tested
break;
case MAKEFOURCC('Y', 'V', '1', '2'):
_blitVideoType = kYV12;
WEBRTC_TRACE(kTraceInfo, kTraceVideo,
-1, "DirectDraw support YV12");
break;
case MAKEFOURCC('Y', 'U', 'Y', '2'):
if (_blitVideoType != kYV12)
{
_blitVideoType = kYUY2;
}
WEBRTC_TRACE(kTraceInfo, kTraceVideo,
-1, "DirectDraw support YUY2");
break;
case MAKEFOURCC('U', 'Y', 'V', 'Y'):
if (_blitVideoType != kYV12)
{
_blitVideoType = kUYVY;
}
WEBRTC_TRACE(kTraceInfo, kTraceVideo,
-1, "DirectDraw support UYVY");
break;
default:
WEBRTC_TRACE(kTraceInfo, kTraceVideo,
-1, "DirectDraw unknown blit type %x", w);
break;
}
}
delete[] pi_codes;
}
}
return 0;
}
int VideoRenderDirectDraw::Stop()
{
_confCritSect->Enter();
_blit = false;
_confCritSect->Leave();
return 0;
}
bool VideoRenderDirectDraw::IsPrimaryOrMixingSurfaceOnSystem()
{
return _isPrimaryOrMixingSurfaceOnSystem;
}
int VideoRenderDirectDraw::CreatePrimarySurface()
{
// Create the primary surface
DirectDrawSurfaceDesc ddsd;
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
HRESULT ddrval = DD_OK;
if (_directDraw == NULL)
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw object not created");
return -1;
//return VIDEO_DIRECT_DRAW_FAILURE;
}
if (_primarySurface)
{
_primarySurface->Release();
_primarySurface = NULL;
}
if (!_fullscreen)
{
// create a normal window
ddrval = _directDraw->SetCooperativeLevel(_hWnd, DDSCL_NORMAL);
if (FAILED(ddrval))
{
//******** Potential workaround for D#4608 *************** Ignore error.
WEBRTC_TRACE(kTraceWarning, kTraceVideo, -1,
"DirectDraw failed to set SetCooperativeLevel %x, ddrval");
}
// we cant size the primary surface based on _hwndRect
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_VIDEOMEMORY;
#ifndef NOGRAPHICSCARD_MEMORY
ddrval = _directDraw->CreateSurface(&ddsd, &_primarySurface, NULL);
if (FAILED(ddrval))
{
WEBRTC_TRACE(
kTraceError,
kTraceVideo,
-1,
"DirectDraw failed to CreateSurface _primarySurface using VideoMemory: 0x%x",
ddrval);
WEBRTC_TRACE(
kTraceError,
kTraceVideo,
-1,
"\t HWND: 0x%x, top: %d, left: %d, bottom: %d, right: %d, dwFlags: %d. Line : %d",
_hWnd, _hwndRect.top, _hwndRect.left,
_hwndRect.bottom, _hwndRect.right, ddsd.dwFlags,
__LINE__);
#endif
//allocate using System memory
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_SYSTEMMEMORY;
ddrval = _directDraw->CreateSurface(&ddsd, &_primarySurface, NULL);
if (FAILED(ddrval))
{
WEBRTC_TRACE(
kTraceError,
kTraceVideo,
-1,
"DirectDraw failed to CreateSurface _primarySurface using SystemMemory: 0x%x",
ddrval);
if (ddrval != 0x887600E1)
{
_directDraw->Release();
_directDraw = 0;
}
return -1;
//return VIDEO_DIRECT_DRAW_FAILURE;
}
_isPrimaryOrMixingSurfaceOnSystem = true;
WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
"DirectDraw _primarySurface on SystemMemory");
#ifndef NOGRAPHICSCARD_MEMORY
}
#endif
// Create a clipper to ensure that our drawing stays inside our window
LPDIRECTDRAWCLIPPER directDrawClipper;
ddrval = _directDraw->CreateClipper(0, &directDrawClipper, NULL );
if (ddrval != DD_OK)
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw failed to CreateClipper");
_primarySurface->Release();
_directDraw->Release();
_primarySurface = 0;
_directDraw = 0;
return -1;
//return VIDEO_DIRECT_DRAW_FAILURE;
}
// setting it to our hwnd gives the clipper the coordinates from our window
// when using cliplist we run into problem with transparent HWNDs (such as REX)
ddrval = directDrawClipper->SetHWnd(0, _hWnd);
if (ddrval != DD_OK)
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw failed to SetHWnd");
_primarySurface->Release();
_directDraw->Release();
_primarySurface = 0;
_directDraw = 0;
return -1;
//return VIDEO_DIRECT_DRAW_FAILURE;
}
// attach the clipper to the primary surface
ddrval = _primarySurface->SetClipper(directDrawClipper);
directDrawClipper->Release(); // no need to keep the clipper around
if (ddrval != DD_OK)
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw failed to SetClipper");
_primarySurface->Release();
_directDraw->Release();
_primarySurface = 0;
_directDraw = 0;
return -1;
//return VIDEO_DIRECT_DRAW_FAILURE;
}
}
else
{
/* The cooperative level determines how much control we have over the
* screen. This must at least be either DDSCL_EXCLUSIVE or DDSCL_NORMAL
*
* DDSCL_EXCLUSIVE allows us to change video modes, and requires
* the DDSCL_FULLSCREEN flag, which will cause the window to take over
* the fullscreen. This is the preferred DirectDraw mode because it allows
* us to have control of the whole screen without regard for GDI.
*
* DDSCL_NORMAL is used to allow the DirectDraw app to run windowed.
*/
// Note: debuging in fullscreen mode does not work, thanks MS...
::GetWindowRect(_hWnd, &_originalHwndRect);
// DDSCL_NOWINDOWCHANGES prevents DD to change the window but it give us trouble too, not using it
ddrval = _directDraw->SetCooperativeLevel(_hWnd, DDSCL_EXCLUSIVE
| DDSCL_FULLSCREEN | DDSCL_ALLOWREBOOT);
if (FAILED(ddrval))
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw failed to SetCooperativeLevel DDSCL_EXCLUSIVE");
WEBRTC_TRACE(
kTraceError,
kTraceVideo,
-1,
"\t HWND: 0x%x, top: %d, left: %d, bottom: %d, right: %d, dwFlags: %d. Line : %d",
_hWnd, _hwndRect.top, _hwndRect.left,
_hwndRect.bottom, _hwndRect.right, ddsd.dwFlags,
__LINE__);
_directDraw->Release();
_directDraw = 0;
return -1;
//return VIDEO_DIRECT_DRAW_FAILURE;
}
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP
| DDSCAPS_COMPLEX | DDSCAPS_VIDEOMEMORY;
ddsd.dwBackBufferCount = 1;
ddrval = _directDraw->CreateSurface(&ddsd, &_primarySurface, NULL);
if (FAILED(ddrval))
{
WEBRTC_TRACE(
kTraceError,
kTraceVideo,
-1,
"DirectDraw failed to CreateSurface _primarySurface, fullscreen mode: 0x%x",
ddrval);
WEBRTC_TRACE(
kTraceError,
kTraceVideo,
-1,
"\t HWND: 0x%x, top: %d, left: %d, bottom: %d, right: %d, dwFlags: %d. Line : %d",
_hWnd, _hwndRect.top, _hwndRect.left,
_hwndRect.bottom, _hwndRect.right, ddsd.dwFlags,
__LINE__);
_directDraw->Release();
_directDraw = 0;
return -1;
//return VIDEO_DIRECT_DRAW_FAILURE;
}
// Get a pointer to the back buffer
DirectDrawCaps ddsCaps;
ZeroMemory(&ddsCaps, sizeof(ddsCaps));
ddsCaps.dwCaps = DDSCAPS_BACKBUFFER | DDSCAPS_VIDEOMEMORY;
ddrval = _primarySurface->GetAttachedSurface(&ddsCaps, &_backSurface);
if (FAILED(ddrval))
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw failed to GetAttachedSurface, fullscreen mode ");
WEBRTC_TRACE(
kTraceError,
kTraceVideo,
-1,
"\t HWND: 0x%x, top: %d, left: %d, bottom: %d, right: %d, dwFlags: %d. Line : %d",
_hWnd, _hwndRect.top, _hwndRect.left,
_hwndRect.bottom, _hwndRect.right, ddsd.dwFlags,
__LINE__);
_primarySurface->Release();
_directDraw->Release();
_primarySurface = 0;
_directDraw = 0;
return -1;
//return VIDEO_DIRECT_DRAW_FAILURE;
}
// Get the screen size and save it as a rect
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
}
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
// get our prinmary surface description
ddrval = _primarySurface->GetSurfaceDesc(&ddsd);
if (!(SUCCEEDED(ddrval) && (ddsd.dwFlags & DDSD_WIDTH) && (ddsd.dwFlags
& DDSD_HEIGHT)))
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw failed to GetSurfaceDesc _primarySurface");
WEBRTC_TRACE(
kTraceError,
kTraceVideo,
-1,
"\t HWND: 0x%x, top: %d, left: %d, bottom: %d, right: %d, dwFlags: %d. Line : %d",
_hWnd, _hwndRect.top, _hwndRect.left, _hwndRect.bottom,
_hwndRect.right, ddsd.dwFlags, __LINE__);
_primarySurface->Release();
_directDraw->Release();
_primarySurface = 0;
_directDraw = 0;
return -1;
//return VIDEO_DIRECT_DRAW_FAILURE;
}
// first we need to figure out the size of the primary surface
// store screen size
::SetRect(&_screenRect, 0, 0, ddsd.dwWidth, ddsd.dwHeight);
// store RGB type
if (ddsd.ddpfPixelFormat.dwFlags & DDPF_RGB)
{
// RGB surface
switch (ddsd.ddpfPixelFormat.dwRGBBitCount)
{
case 16:
switch (ddsd.ddpfPixelFormat.dwGBitMask)
{
case 0x00e0:
_rgbVideoType = kARGB4444;
break;
case 0x03e0:
_rgbVideoType = kARGB1555;
break;
case 0x07e0:
_rgbVideoType = kRGB565;
break;
}
break;
case 24:
_rgbVideoType = kRGB24;
break;
case 32:
_rgbVideoType = kARGB;
break;
}
}
switch (_blitVideoType)
{
case kI420:
case kIYUV:
case kYUY2:
case kYV12:
case kUYVY:
_incomingVideoType = kI420;
break;
case kUnknown:
_blitVideoType = _rgbVideoType;
_incomingVideoType = kI420;
break;
default:
_blitVideoType = _rgbVideoType;
_incomingVideoType = kI420;
break;
}
WEBRTC_TRACE(
kTraceInfo,
kTraceVideo,
-1,
"DirectDraw created _primarySurface, _blitVideoType %d, _rgbvideoType %d",
_blitVideoType, _rgbVideoType);
return 0;
}
int VideoRenderDirectDraw::CreateMixingSurface()
{
if (_directDraw == NULL)
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw object not created");
return -1;
//return VIDEO_DIRECT_DRAW_FAILURE;
}
if (_fullscreen)
{
::CopyRect(&_hwndRect, &_screenRect);
}
else
{
// update our _hWnd size
::GetClientRect(_hWnd, &_hwndRect);
}
if (_mixingSurface)
{
_mixingSurface->Release();
_mixingSurface = NULL;
}
// create mixing surface
DirectDrawSurfaceDesc ddsd;
memset(&ddsd, 0, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
ddsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY;
ddsd.dwHeight = _hwndRect.bottom;
ddsd.dwWidth = _hwndRect.right;
/* char logStr[256];
_snprintf(logStr,256, "CreateMixingSurface H:%d W:%d \n",_hwndRect.bottom, _hwndRect.right);
OutputDebugString(logStr);
*/
#ifndef NOGRAPHICSCARD_MEMORY
HRESULT ddrval = _directDraw->CreateSurface(&ddsd, &_mixingSurface, NULL);
if (FAILED(ddrval))
{
WEBRTC_TRACE(
kTraceError,
kTraceVideo,
-1,
"DirectDraw failed to CreateSurface _mixingSurface using VideoMemory: 0x%x",
ddrval);
WEBRTC_TRACE(
kTraceError,
kTraceVideo,
-1,
"\t HWND: 0x%x, top: %d, left: %d, bottom: %d, right: %d, dwFlags: %d",
_hWnd, _hwndRect.top, _hwndRect.left, _hwndRect.bottom,
_hwndRect.right, ddsd.dwFlags);
#endif
ddsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY;
HRESULT ddrval = _directDraw->CreateSurface(&ddsd, &_mixingSurface,
NULL);
if (FAILED(ddrval))
{
WEBRTC_TRACE(
kTraceError,
kTraceVideo,
-1,
"DirectDraw failed to CreateSurface _mixingSurface on System Memory: 0x%x",
ddrval);
return -1;
//return VIDEO_DIRECT_DRAW_FAILURE;
}
_isPrimaryOrMixingSurfaceOnSystem = true;
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw CreateSurface _mixingSurface on SystemMemory");
#ifndef NOGRAPHICSCARD_MEMORY
}
#endif
_clearMixingSurface = true;
WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
"DirectDraw _mixingSurface created");
return 0;
}
VideoRenderCallback* VideoRenderDirectDraw::CreateChannel(WebRtc_UWord32 channel,
WebRtc_UWord32 zOrder,
float startWidth,
float startHeight,
float stopWidth,
float stopHeight)
{
if (!_canStretch)
{
if (startWidth != 0.0f || startHeight != 0.0f || stopWidth != 1.0f
|| stopHeight != 1.0f)
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw failed to CreateChannel HW don't support stretch");
return NULL;
}
}
DirectDrawChannel* ddobj =
new DirectDrawChannel(_directDraw, _blitVideoType,
_incomingVideoType, _rgbVideoType, this);
ddobj->SetStreamSettings(this, 0, startWidth, startHeight, stopWidth,
stopHeight);
// store channel
_directDrawChannels[channel & 0x0000ffff] = ddobj;
// store Z order
// default streamID is 0
_directDrawZorder.insert(ZorderPair(zOrder, channel & 0x0000ffff));
return ddobj;
}
int VideoRenderDirectDraw::AddDirectDrawChannel(int channel,
unsigned char streamID,
int zOrder,
DirectDrawChannel* ddObj)
{
// Only allow one stream per channel, demuxing is done outside of DirectDraw...
streamID = 0;
unsigned int streamChannel = (streamID << 16) + (channel & 0x0000ffff);
// store channel
_directDrawChannels[channel & 0x0000ffff] = ddObj;
_demuxing = true; // with this function it's always demux
// store Z order
_directDrawZorder.insert(ZorderPair(zOrder, streamChannel));
return 0;
}
DirectDrawChannel* VideoRenderDirectDraw::ShareDirectDrawChannel(
int channel)
{
CriticalSectionScoped cs(*_confCritSect);
DirectDrawChannel* obj = NULL;
std::map<int, DirectDrawChannel*>::iterator ddIt;
ddIt = _directDrawChannels.find(channel & 0x0000ffff);
if (ddIt != _directDrawChannels.end())
{
obj = ddIt->second;
obj->AddRef();
}
return obj;
}
WebRtc_Word32 VideoRenderDirectDraw::DeleteChannel(const WebRtc_UWord32 channel)
{
CriticalSectionScoped cs(*_confCritSect);
// Remove the old z order
//unsigned int streamChannel = (streamID << 16) + (channel & 0x0000ffff);
std::multimap<int, unsigned int>::iterator it;
it = _directDrawZorder.begin();
while (it != _directDrawZorder.end())
{
//if(streamChannel == it->second )
if ((channel & 0x0000ffff) == (it->second & 0x0000ffff))
{
it = _directDrawZorder.erase(it);
break;
}
it++;
}
std::map<int, DirectDrawChannel*>::iterator ddIt;
ddIt = _directDrawChannels.find(channel & 0x0000ffff);
if (ddIt != _directDrawChannels.end())
{
ddIt->second->Release();
_directDrawChannels.erase(ddIt);
_clearMixingSurface = true;
}
return 0;
}
WebRtc_Word32 VideoRenderDirectDraw::GetStreamSettings(const WebRtc_UWord32 channel,
const WebRtc_UWord16 streamId,
WebRtc_UWord32& zOrder,
float& startWidth,
float& startHeight,
float& stopWidth,
float& stopHeight)
{
CriticalSectionScoped cs(*_confCritSect);
std::map<int, DirectDrawChannel*>::iterator ddIt;
ddIt = _directDrawChannels.find(channel & 0x0000ffff);
if (ddIt == _directDrawChannels.end())
{
// This channel doesn't exist.
return -1;
}
DirectDrawChannel* ptrChannel = ddIt->second;
// Only support one stream per channel, is demuxing done outside if DD.
//if (ptrChannel->GetStreamSettings(this, streamId, startWidth, startHeight, stopWidth, stopHeight) == -1)
if (ptrChannel->GetStreamSettings(this, 0, startWidth, startHeight,
stopWidth, stopHeight) == -1)
{
// Error for this stream
return -1;
}
// Get the zOrder
std::multimap<int, unsigned int>::iterator it;
it = _directDrawZorder.begin();
while (it != _directDrawZorder.end())
{
if ((channel & 0x0000ffff) == (it->second & 0x0000ffff))
{
// We found our channel zOrder
zOrder = (unsigned int) (it->first);
break;
}
it++;
}
return 0;
}
int VideoRenderDirectDraw::GetChannels(std::list<int>& channelList)
{
CriticalSectionScoped cs(*_confCritSect);
std::map<int, DirectDrawChannel*>::iterator ddIt;
ddIt = _directDrawChannels.begin();
while (ddIt != _directDrawChannels.end())
{
int channel = ddIt->first;
if (channel == 0x0000ffff)
{
channel = -1;
}
channelList.push_back(channel);
ddIt++;
}
return 0;
}
bool VideoRenderDirectDraw::HasChannel(int channel)
{
CriticalSectionScoped cs(*_confCritSect);
std::map<int, DirectDrawChannel*>::iterator ddIt;
ddIt = _directDrawChannels.find(channel & 0x0000ffff);
if (ddIt != _directDrawChannels.end())
{
return true;
}
return false;
}
bool VideoRenderDirectDraw::HasChannels()
{
CriticalSectionScoped cs(*_confCritSect);
if (_directDrawChannels.begin() != _directDrawChannels.end())
{
return true;
}
return false;
}
bool VideoRenderDirectDraw::IsFullScreen()
{
return _fullscreen;
}
VideoType VideoRenderDirectDraw::GetPerferedVideoFormat()
{
return _incomingVideoType;
}
// this can be called rutime from another thread
DirectDrawChannel* VideoRenderDirectDraw::ConfigureDirectDrawChannel(int channel,
unsigned char streamID,
int zOrder,
float left,
float top,
float right,
float bottom)
{
// Only support one stream per channel, is demuxing done outside if DD.
streamID = 0;
CriticalSectionScoped cs(*_confCritSect);
if (!_canStretch)
{
if (left != 0.0f || top != 0.0f || right != 1.0f || bottom != 1.0f)
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw failed to ConfigureDirectDrawChannel HW don't support stretch");
return NULL;
}
}
std::map<int, DirectDrawChannel*>::iterator ddIt;
ddIt = _directDrawChannels.find(channel & 0x0000ffff);
DirectDrawChannel* ddobj = NULL;
if (ddIt != _directDrawChannels.end())
{
ddobj = ddIt->second;
}
if (ddobj == NULL)
{
WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, -1,
"DirectDraw failed to find channel");
return NULL;
}
unsigned int streamChannel = (streamID << 16) + (channel & 0x0000ffff);
// remove the old z order
std::multimap<int, unsigned int>::iterator it;
it = _directDrawZorder.begin();
while (it != _directDrawZorder.end())
{
if (streamChannel == it->second)
{
it = _directDrawZorder.erase(it);
break;
}
it++;
}
// if this channel already are in the zOrder map it's demux
it = _directDrawZorder.begin();
while (it != _directDrawZorder.end())
{
if (channel == (it->second & 0x0000ffff))
{
_demuxing = true;
break;
}
it++;
}
if (it == _directDrawZorder.end())
{
_demuxing = false;
}
_clearMixingSurface = true;
if (left == 0.0f && top == 0.0f && right == 0.0f && bottom == 0.0f)
{
// remove
_directDrawChannels.erase(ddIt);
ddobj->Release();
return NULL;
}
ddobj->SetStreamSettings(this, streamID, left, top, right, bottom);
_directDrawZorder.insert(ZorderPair(zOrder, streamChannel));
return ddobj;
}
WebRtc_Word32 VideoRenderDirectDraw::SetCropping(const WebRtc_UWord32 channel,
const WebRtc_UWord16 streamID,
float left, float top,
float right, float bottom)
{
CriticalSectionScoped cs(*_confCritSect);
if (!_canStretch)
{
if (left != 0.0f || top != 0.0f || right != 1.0f || bottom != 1.0f)
{
WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, -1,
"DirectDraw failed to SetCropping HW don't support stretch");
return -1;
//return VIDEO_DIRECT_DRAW_FAILURE;
}
}
std::map<int, DirectDrawChannel*>::iterator ddIt;
ddIt = _directDrawChannels.find(channel & 0x0000ffff);
if (ddIt != _directDrawChannels.end())
{
DirectDrawChannel* ddobj = ddIt->second;
if (ddobj)
{
// Only support one stream per channel, is demuxing done outside if DD.
ddobj->SetStreamCropSettings(this, 0, left, top, right, bottom);
//ddobj->SetStreamCropSettings(this, streamID, left, top, right, bottom);
}
}
return 0;
}
WebRtc_Word32 VideoRenderDirectDraw::ConfigureRenderer(const WebRtc_UWord32 channel,
const WebRtc_UWord16 streamId,
const unsigned int zOrder,
const float left,
const float top,
const float right,
const float bottom)
{
if (ConfigureDirectDrawChannel(channel, (unsigned char) streamId, zOrder,
left, top, right, bottom) == NULL)
{
if (left == 0.0f && top == 0.0f && right == 0.0f && bottom == 0.0f)
{
WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, -1,
"ConfigureRender, removed channel:%d streamId:%d",
channel, streamId);
}
else
{
WEBRTC_TRACE(
kTraceError,
kTraceVideoRenderer,
-1,
"DirectDraw failed to ConfigureRenderer for channel: %d",
channel);
return -1;
}
}
return 0;
}
// this can be called runtime from another thread
WebRtc_Word32 VideoRenderDirectDraw::SetText(const WebRtc_UWord8 textId,
const WebRtc_UWord8* text,
const WebRtc_Word32 textLength,
const WebRtc_UWord32 colorText,
const WebRtc_UWord32 colorBg,
const float left,
const float top,
const float right,
const float bottom)
{
DirectDrawTextSettings* textSetting = NULL;
CriticalSectionScoped cs(*_confCritSect);
_frameChanged = true;
std::map<unsigned char, DirectDrawTextSettings*>::iterator it;
it = _textSettings.find(textId);
if (it != _textSettings.end())
{
if (it->second)
{
textSetting = it->second;
}
}
_clearMixingSurface = true;
if (text == NULL || textLength == 0)
{
WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
"DirectDraw remove text textId:%d", textId);
if (textSetting)
{
delete textSetting;
_textSettings.erase(it);
}
return 0;
}
// sanity
if (left > 1.0f || left < 0.0f)
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw SetText invalid parameter");
return -1;
//return VIDEO_DIRECT_DRAW_INVALID_ARG;
}
if (top > 1.0f || top < 0.0f)
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw SetText invalid parameter");
return -1;
//return VIDEO_DIRECT_DRAW_INVALID_ARG;
}
if (right > 1.0f || right < 0.0f)
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw SetText invalid parameter");
return -1;
//return VIDEO_DIRECT_DRAW_INVALID_ARG;
}
if (bottom > 1.0f || bottom < 0.0f)
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw SetText invalid parameter");
return -1;
//return VIDEO_DIRECT_DRAW_INVALID_ARG;
}
if (textSetting == NULL)
{
textSetting = new DirectDrawTextSettings();
}
int retVal = textSetting->SetText((const char*) text, textLength,
(COLORREF) colorText, (COLORREF) colorBg,
left, top, right, bottom);
if (retVal != 0)
{
delete textSetting;
textSetting = NULL;
_textSettings.erase(textId);
return retVal;
}
if (textSetting)
{
_textSettings[textId] = textSetting;
}
return retVal;
}
// this can be called runtime from another thread
WebRtc_Word32 VideoRenderDirectDraw::SetBitmap(const void* bitMap,
const WebRtc_UWord8 pictureId,
const void* colorKey,
const float left,
const float top,
const float right,
const float bottom)
{
DirectDrawBitmapSettings* bitmapSetting = NULL;
CriticalSectionScoped cs(*_confCritSect);
_frameChanged = true;
std::map<unsigned char, DirectDrawBitmapSettings*>::iterator it;
it = _bitmapSettings.find(pictureId);
if (it != _bitmapSettings.end())
{
if (it->second)
{
bitmapSetting = it->second;
}
}
_clearMixingSurface = true;
if (bitMap == NULL)
{
WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
"DirectDraw remove bitmap pictureId:%d", pictureId);
if (bitmapSetting)
{
delete bitmapSetting;
_bitmapSettings.erase(it);
}
return 0;
}
// sanity
if (left > 1.0f || left < 0.0f)
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw SetBitmap invalid parameter");
return -1;
//return VIDEO_DIRECT_DRAW_INVALID_ARG;
}
if (top > 1.0f || top < 0.0f)
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw SetBitmap invalid parameter");
return -1;
//return VIDEO_DIRECT_DRAW_INVALID_ARG;
}
if (right > 1.0f || right < 0.0f)
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw SetBitmap invalid parameter");
return -1;
//return VIDEO_DIRECT_DRAW_INVALID_ARG;
}
if (bottom > 1.0f || bottom < 0.0f)
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw SetBitmap invalid parameter");
return -1;
//return VIDEO_DIRECT_DRAW_INVALID_ARG;
}
if (!_canStretch)
{
if (left != 0.0f || top != 0.0f || right != 1.0f || bottom != 1.0f)
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw failed to SetBitmap HW don't support stretch");
return -1;
//return VIDEO_DIRECT_DRAW_INVALID_ARG;
}
}
if (bitmapSetting == NULL)
{
bitmapSetting = new DirectDrawBitmapSettings();
}
bitmapSetting->_transparentBitMap = (HBITMAP) bitMap;
bitmapSetting->_transparentBitmapLeft = left;
bitmapSetting->_transparentBitmapRight = right;
bitmapSetting->_transparentBitmapTop = top;
bitmapSetting->_transparentBitmapBottom = bottom;
// colorKey == NULL equals no transparency
if (colorKey)
{
// first remove constness
DDCOLORKEY* ddColorKey =
static_cast<DDCOLORKEY*> (const_cast<void*> (colorKey));
if (!_supportTransparency)
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw failed to SetBitmap HW don't support transparency");
return -1;
//return VIDEO_DIRECT_DRAW_INVALID_ARG;
}
if (bitmapSetting->_transparentBitmapColorKey == NULL)
{
bitmapSetting->_transparentBitmapColorKey = new DDCOLORKEY();
}
if (ddColorKey)
{
bitmapSetting->_transparentBitmapColorKey->dwColorSpaceLowValue
= ddColorKey->dwColorSpaceLowValue;
bitmapSetting->_transparentBitmapColorKey->dwColorSpaceHighValue
= ddColorKey->dwColorSpaceHighValue;
}
}
int retval = bitmapSetting->SetBitmap(_trace, _directDraw);
if (retval != 0)
{
delete bitmapSetting;
bitmapSetting = NULL;
_bitmapSettings.erase(pictureId);
return retval;
}
if (bitmapSetting)
{
_bitmapSettings[pictureId] = bitmapSetting;
}
return retval;
}
// this can be called rutime from another thread
WebRtc_Word32 VideoRenderDirectDraw::SetTransparentBackground(
const bool enable)
{
CriticalSectionScoped cs(*_confCritSect);
if (_supportTransparency)
{
_transparentBackground = enable;
if (enable)
{
WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
"DirectDraw enabled TransparentBackground");
}
else
{
WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
"DirectDraw disabled TransparentBackground");
}
return 0;
}
WEBRTC_TRACE(
kTraceError,
kTraceVideo,
-1,
"DirectDraw failed to EnableTransparentBackground HW don't support transparency");
return -1;
//return VIDEO_DIRECT_DRAW_INVALID_ARG;
}
int VideoRenderDirectDraw::FillSurface(DirectDrawSurface *pDDSurface,
RECT* rect)
{
// sanity checks
if (NULL == pDDSurface)
{
return -1;
//return VIDEO_DIRECT_DRAW_FAILURE;
}
if (NULL == rect)
{
return -1;
//return VIDEO_DIRECT_DRAW_FAILURE;
}
// Repaint the whole specified surface
HRESULT ddrval;
DDBLTFX ddFX;
ZeroMemory(&ddFX, sizeof(ddFX));
ddFX.dwSize = sizeof(ddFX);
ddFX.dwFillColor = RGB(0, 0, 0);
// Draw color key on the video area of given surface
ddrval = pDDSurface->Blt(rect, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT,
&ddFX);
if (FAILED(ddrval))
{
WEBRTC_TRACE(kTraceError, kTraceVideo, -1,
"DirectDraw failed to fill surface");
return -1;
//return VIDEO_DIRECT_DRAW_FAILURE;
}
return 0;
}
// the real rendering thread
bool VideoRenderDirectDraw::RemoteRenderingThreadProc(void *obj)
{
return static_cast<VideoRenderDirectDraw*> (obj)->RemoteRenderingProcess();
}
bool VideoRenderDirectDraw::RemoteRenderingProcess()
{
bool hwndChanged = false;
int waitTime = 0;
_screenEvent->Wait(100);
_confCritSect->Enter();
if (_blit == false)
{
_confCritSect->Leave();
return true;
}
if (!::GetForegroundWindow())
{
//no window, i.e the user have clicked CTRL+ALT+DEL, return true and wait
_confCritSect->Leave();
return true;
}
// Skip to blit if last render to primare surface took too long time.
_processCount++;
if (_deliverInQuarterFrameRate)
{
if (_processCount % 4 != 0)
{
_confCritSect->Leave();
return true;
}
}
else if (_deliverInHalfFrameRate)
{
if (_processCount % 2 != 0)
{
_confCritSect->Leave();
return true;
}
}
// Calculate th erender process time
unsigned int startProcessTime = timeGetTime();
hwndChanged = HasHWNDChanged();
if (hwndChanged)
{
_clearMixingSurface = true;
}
std::map<int, DirectDrawChannel*>::iterator it;
it = _directDrawChannels.begin();
while (it != _directDrawChannels.end() && !_frameChanged)
{
if (it->second)
{
int channel = it->first;
_frameChanged = it->second->IsOffScreenSurfaceUpdated(this);
}
it++;