/* * Copyright (c) 2012 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 "webrtc/engine_configurations.h" #if defined(CARBON_RENDERING) #include "webrtc/modules/video_render/mac/video_render_agl.h" // includes #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" #include "webrtc/system_wrappers/include/critical_section_wrapper.h" #include "webrtc/system_wrappers/include/event_wrapper.h" #include "webrtc/system_wrappers/include/trace.h" namespace webrtc { /* * * VideoChannelAGL * */ #pragma mark VideoChannelAGL constructor VideoChannelAGL::VideoChannelAGL(AGLContext& aglContext, int iId, VideoRenderAGL* owner) : _aglContext( aglContext), _id( iId), _owner( owner), _width( 0), _height( 0), _stretchedWidth( 0), _stretchedHeight( 0), _startWidth( 0.0f), _startHeight( 0.0f), _stopWidth( 0.0f), _stopHeight( 0.0f), _xOldWidth( 0), _yOldHeight( 0), _oldStretchedHeight(0), _oldStretchedWidth( 0), _buffer( 0), _bufferSize( 0), _incomingBufferSize(0), _bufferIsUpdated( false), _sizeInitialized( false), _numberOfStreams( 0), _bVideoSizeStartedChanging(false), _pixelFormat( GL_RGBA), _pixelDataType( GL_UNSIGNED_INT_8_8_8_8), _texture( 0) { //WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d Constructor", __FUNCTION__, __LINE__); } VideoChannelAGL::~VideoChannelAGL() { //WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d Destructor", __FUNCTION__, __LINE__); if (_buffer) { delete [] _buffer; _buffer = NULL; } aglSetCurrentContext(_aglContext); if (_texture != 0) { glDeleteTextures(1, (const GLuint*) &_texture); _texture = 0; } } int32_t VideoChannelAGL::RenderFrame(const uint32_t streamId, VideoFrame& videoFrame) { _owner->LockAGLCntx(); if (_width != videoFrame.width() || _height != videoFrame.height()) { if (FrameSizeChange(videoFrame.width(), videoFrame.height(), 1) == -1) { WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s:%d FrameSize Change returned an error", __FUNCTION__, __LINE__); _owner->UnlockAGLCntx(); return -1; } } _owner->UnlockAGLCntx(); return DeliverFrame(videoFrame); } int VideoChannelAGL::UpdateSize(int /*width*/, int /*height*/) { _owner->LockAGLCntx(); _owner->UnlockAGLCntx(); return 0; } int VideoChannelAGL::UpdateStretchSize(int stretchHeight, int stretchWidth) { _owner->LockAGLCntx(); _stretchedHeight = stretchHeight; _stretchedWidth = stretchWidth; _owner->UnlockAGLCntx(); return 0; } int VideoChannelAGL::FrameSizeChange(int width, int height, int numberOfStreams) { // We'll get a new frame size from VideoAPI, prepare the buffer _owner->LockAGLCntx(); if (width == _width && _height == height) { // We already have a correct buffer size _numberOfStreams = numberOfStreams; _owner->UnlockAGLCntx(); return 0; } _width = width; _height = height; // Delete the old buffer, create a new one with correct size. if (_buffer) { delete [] _buffer; _bufferSize = 0; } _incomingBufferSize = CalcBufferSize(kI420, _width, _height); _bufferSize = CalcBufferSize(kARGB, _width, _height);//_width * _height * bytesPerPixel; _buffer = new unsigned char [_bufferSize]; memset(_buffer, 0, _bufferSize * sizeof(unsigned char)); if (aglSetCurrentContext(_aglContext) == false) { _owner->UnlockAGLCntx(); return -1; } // Delete a possible old texture if (_texture != 0) { glDeleteTextures(1, (const GLuint*) &_texture); _texture = 0; } // Create a new texture glGenTextures(1, (GLuint *) &_texture); GLenum glErr = glGetError(); if (glErr != GL_NO_ERROR) { } // Do the setup for both textures // Note: we setup two textures even if we're not running full screen glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _texture); // Set texture parameters glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_PRIORITY, 1.0); glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_NEAREST); //glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_SHARED_APPLE); // Maximum width/height for a texture GLint texSize; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize); if (texSize < _width || texSize < _height) { // Image too big for memory _owner->UnlockAGLCntx(); return -1; } // Set up th texture type and size glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, // target 0, // level GL_RGBA, // internal format _width, // width _height, // height 0, // border 0/1 = off/on _pixelFormat, // format, GL_BGRA _pixelDataType, // data type, GL_UNSIGNED_INT_8_8_8_8 _buffer); // pixel data glErr = glGetError(); if (glErr != GL_NO_ERROR) { _owner->UnlockAGLCntx(); return -1; } _owner->UnlockAGLCntx(); return 0; } // Called from video engine when a new frame should be rendered. int VideoChannelAGL::DeliverFrame(const VideoFrame& videoFrame) { _owner->LockAGLCntx(); if (_texture == 0) { _owner->UnlockAGLCntx(); return 0; } if (CalcBufferSize(kI420, videoFrame.width(), videoFrame.height()) != _incomingBufferSize) { _owner->UnlockAGLCntx(); return -1; } // Setting stride = width. int rgbret = ConvertFromYV12(videoFrame, kBGRA, 0, _buffer); if (rgbret < 0) { _owner->UnlockAGLCntx(); return -1; } aglSetCurrentContext(_aglContext); // Put the new frame into the graphic card texture. // Make sure this texture is the active one glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _texture); GLenum glErr = glGetError(); if (glErr != GL_NO_ERROR) { _owner->UnlockAGLCntx(); return -1; } // Copy buffer to texture glTexSubImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, // Level, not use 0, // start point x, (low left of pic) 0, // start point y, _width, // width _height, // height _pixelFormat, // pictue format for _buffer _pixelDataType, // data type of _buffer (const GLvoid*) _buffer); // the pixel data if (glGetError() != GL_NO_ERROR) { _owner->UnlockAGLCntx(); return -1; } _bufferIsUpdated = true; _owner->UnlockAGLCntx(); return 0; } int VideoChannelAGL::RenderOffScreenBuffer() { _owner->LockAGLCntx(); if (_texture == 0) { _owner->UnlockAGLCntx(); return 0; } GLfloat xStart = 2.0f * _startWidth - 1.0f; GLfloat xStop = 2.0f * _stopWidth - 1.0f; GLfloat yStart = 1.0f - 2.0f * _stopHeight; GLfloat yStop = 1.0f - 2.0f * _startHeight; aglSetCurrentContext(_aglContext); glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _texture); if(_stretchedWidth != _oldStretchedWidth || _stretchedHeight != _oldStretchedHeight) { glViewport(0, 0, _stretchedWidth, _stretchedHeight); } _oldStretchedHeight = _stretchedHeight; _oldStretchedWidth = _stretchedWidth; // Now really put the texture into the framebuffer glLoadIdentity(); glEnable(GL_TEXTURE_RECTANGLE_EXT); glBegin(GL_POLYGON); { glTexCoord2f(0.0, 0.0); glVertex2f(xStart, yStop); glTexCoord2f(_width, 0.0); glVertex2f(xStop, yStop); glTexCoord2f(_width, _height); glVertex2f(xStop, yStart); glTexCoord2f(0.0, _height); glVertex2f(xStart, yStart); } glEnd(); glDisable(GL_TEXTURE_RECTANGLE_EXT); _bufferIsUpdated = false; _owner->UnlockAGLCntx(); return 0; } int VideoChannelAGL::IsUpdated(bool& isUpdated) { _owner->LockAGLCntx(); isUpdated = _bufferIsUpdated; _owner->UnlockAGLCntx(); return 0; } int VideoChannelAGL::SetStreamSettings(int /*streamId*/, float startWidth, float startHeight, float stopWidth, float stopHeight) { _owner->LockAGLCntx(); _startWidth = startWidth; _stopWidth = stopWidth; _startHeight = startHeight; _stopHeight = stopHeight; int oldWidth = _width; int oldHeight = _height; int oldNumberOfStreams = _numberOfStreams; _width = 0; _height = 0; int retVal = FrameSizeChange(oldWidth, oldHeight, oldNumberOfStreams); _owner->UnlockAGLCntx(); return retVal; } int VideoChannelAGL::SetStreamCropSettings(int /*streamId*/, float /*startWidth*/, float /*startHeight*/, float /*stopWidth*/, float /*stopHeight*/) { return -1; } #pragma mark VideoRenderAGL WindowRef constructor VideoRenderAGL::VideoRenderAGL(WindowRef windowRef, bool fullscreen, int iId) : _hiviewRef( 0), _windowRef( windowRef), _fullScreen( fullscreen), _id( iId), _renderCritSec(*CriticalSectionWrapper::CreateCriticalSection()), _screenUpdateEvent( 0), _isHIViewRef( false), _aglContext( 0), _windowWidth( 0), _windowHeight( 0), _lastWindowWidth( -1), _lastWindowHeight( -1), _lastHiViewWidth( -1), _lastHiViewHeight( -1), _currentParentWindowHeight( 0), _currentParentWindowWidth( 0), _currentParentWindowBounds( ), _windowHasResized( false), _lastParentWindowBounds( ), _currentHIViewBounds( ), _lastHIViewBounds( ), _windowRect( ), _aglChannels( ), _zOrderToChannel( ), _hiviewEventHandlerRef( NULL), _windowEventHandlerRef( NULL), _currentViewBounds( ), _lastViewBounds( ), _renderingIsPaused( false), { //WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s"); _screenUpdateThread = ThreadWrapper::CreateThread( ScreenUpdateThreadProc, this, "ScreenUpdate"); _screenUpdateEvent = EventWrapper::Create(); if(!IsValidWindowPtr(_windowRef)) { //WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s:%d Invalid WindowRef:0x%x", __FUNCTION__, __LINE__, _windowRef); } else { //WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s:%d WindowRef 0x%x is valid", __FUNCTION__, __LINE__, _windowRef); } GetWindowRect(_windowRect); _lastViewBounds.origin.x = 0; _lastViewBounds.origin.y = 0; _lastViewBounds.size.width = 0; _lastViewBounds.size.height = 0; } // this is a static function. It has been registered (in class constructor) to be called on various window redrawing or resizing. // Since it is a static method, I have passed in "this" as the userData (one and only allowed) parameter, then calling member methods on it. #pragma mark WindowRef Event Handler pascal OSStatus VideoRenderAGL::sHandleWindowResized (EventHandlerCallRef /*nextHandler*/, EventRef theEvent, void* userData) { WindowRef windowRef = NULL; int eventType = GetEventKind(theEvent); // see https://dcs.sourcerepo.com/dcs/tox_view/trunk/tox/libraries/i686-win32/include/quicktime/CarbonEvents.h for a list of codes GetEventParameter (theEvent, kEventParamDirectObject, typeWindowRef, NULL, sizeof (WindowRef), NULL, &windowRef); VideoRenderAGL* obj = (VideoRenderAGL*)(userData); bool updateUI = true; if(kEventWindowBoundsChanged == eventType) { } else if(kEventWindowBoundsChanging == eventType) { } else if(kEventWindowZoomed == eventType) { } else if(kEventWindowExpanding == eventType) { } else if(kEventWindowExpanded == eventType) { } else if(kEventWindowClickResizeRgn == eventType) { } else if(kEventWindowClickDragRgn == eventType) { } else { updateUI = false; } if(true == updateUI) { obj->ParentWindowResized(windowRef); obj->UpdateClipping(); obj->RenderOffScreenBuffers(); } return noErr; } #pragma mark VideoRenderAGL HIViewRef constructor VideoRenderAGL::VideoRenderAGL(HIViewRef windowRef, bool fullscreen, int iId) : _hiviewRef( windowRef), _windowRef( 0), _fullScreen( fullscreen), _id( iId), _renderCritSec(*CriticalSectionWrapper::CreateCriticalSection()), _screenUpdateEvent( 0), _isHIViewRef( false), _aglContext( 0), _windowWidth( 0), _windowHeight( 0), _lastWindowWidth( -1), _lastWindowHeight( -1), _lastHiViewWidth( -1), _lastHiViewHeight( -1), _currentParentWindowHeight( 0), _currentParentWindowWidth( 0), _currentParentWindowBounds( ), _windowHasResized( false), _lastParentWindowBounds( ), _currentHIViewBounds( ), _lastHIViewBounds( ), _windowRect( ), _aglChannels( ), _zOrderToChannel( ), _hiviewEventHandlerRef( NULL), _windowEventHandlerRef( NULL), _currentViewBounds( ), _lastViewBounds( ), _renderingIsPaused( false), { //WEBRTC_TRACE(kTraceDebug, "%s:%d Constructor", __FUNCTION__, __LINE__); // _renderCritSec = CriticalSectionWrapper::CreateCriticalSection(); _screenUpdateThread = ThreadWrapper::CreateThread( ScreenUpdateThreadProc, this, "ScreenUpdateThread"); _screenUpdateEvent = EventWrapper::Create(); GetWindowRect(_windowRect); _lastViewBounds.origin.x = 0; _lastViewBounds.origin.y = 0; _lastViewBounds.size.width = 0; _lastViewBounds.size.height = 0; #ifdef NEW_HIVIEW_PARENT_EVENT_HANDLER // This gets the parent window of the HIViewRef that's passed in and installs a WindowRef event handler on it // The event handler looks for window resize events and adjusts the offset of the controls. //WEBRTC_TRACE(kTraceDebug, "%s:%d Installing Eventhandler for hiviewRef's parent window", __FUNCTION__, __LINE__); static const EventTypeSpec windowEventTypes[] = { kEventClassWindow, kEventWindowBoundsChanged, kEventClassWindow, kEventWindowBoundsChanging, kEventClassWindow, kEventWindowZoomed, kEventClassWindow, kEventWindowExpanded, kEventClassWindow, kEventWindowClickResizeRgn, kEventClassWindow, kEventWindowClickDragRgn }; WindowRef parentWindow = HIViewGetWindow(windowRef); InstallWindowEventHandler (parentWindow, NewEventHandlerUPP (sHandleWindowResized), GetEventTypeCount(windowEventTypes), windowEventTypes, (void *) this, // this is an arbitrary parameter that will be passed on to your event handler when it is called later &_windowEventHandlerRef); #endif #ifdef NEW_HIVIEW_EVENT_HANDLER //WEBRTC_TRACE(kTraceDebug, "%s:%d Installing Eventhandler for hiviewRef", __FUNCTION__, __LINE__); static const EventTypeSpec hiviewEventTypes[] = { kEventClassControl, kEventControlBoundsChanged, kEventClassControl, kEventControlDraw // kEventControlDragLeave // kEventControlDragReceive // kEventControlGetFocusPart // kEventControlApplyBackground // kEventControlDraw // kEventControlHit }; HIViewInstallEventHandler(_hiviewRef, NewEventHandlerUPP(sHandleHiViewResized), GetEventTypeCount(hiviewEventTypes), hiviewEventTypes, (void *) this, &_hiviewEventHandlerRef); #endif } // this is a static function. It has been registered (in constructor) to be called on various window redrawing or resizing. // Since it is a static method, I have passed in "this" as the userData (one and only allowed) parameter, then calling member methods on it. #pragma mark HIViewRef Event Handler pascal OSStatus VideoRenderAGL::sHandleHiViewResized (EventHandlerCallRef nextHandler, EventRef theEvent, void* userData) { //static int callbackCounter = 1; HIViewRef hiviewRef = NULL; // see https://dcs.sourcerepo.com/dcs/tox_view/trunk/tox/libraries/i686-win32/include/quicktime/CarbonEvents.h for a list of codes int eventType = GetEventKind(theEvent); OSStatus status = noErr; status = GetEventParameter (theEvent, kEventParamDirectObject, typeControlRef, NULL, sizeof (ControlRef), NULL, &hiviewRef); VideoRenderAGL* obj = (VideoRenderAGL*)(userData); WindowRef parentWindow = HIViewGetWindow(hiviewRef); bool updateUI = true; if(kEventControlBoundsChanged == eventType) { } else if(kEventControlDraw == eventType) { } else { updateUI = false; } if(true == updateUI) { obj->ParentWindowResized(parentWindow); obj->UpdateClipping(); obj->RenderOffScreenBuffers(); } return status; } VideoRenderAGL::~VideoRenderAGL() { //WEBRTC_TRACE(kTraceDebug, "%s:%d Destructor", __FUNCTION__, __LINE__); #ifdef USE_EVENT_HANDLERS // remove event handlers OSStatus status; if(_isHIViewRef) { status = RemoveEventHandler(_hiviewEventHandlerRef); } else { status = RemoveEventHandler(_windowEventHandlerRef); } if(noErr != status) { if(_isHIViewRef) { //WEBRTC_TRACE(kTraceDebug, "%s:%d Failed to remove hiview event handler: %d", __FUNCTION__, __LINE__, (int)_hiviewEventHandlerRef); } else { //WEBRTC_TRACE(kTraceDebug, "%s:%d Failed to remove window event handler %d", __FUNCTION__, __LINE__, (int)_windowEventHandlerRef); } } #endif OSStatus status; #ifdef NEW_HIVIEW_PARENT_EVENT_HANDLER if(_windowEventHandlerRef) { status = RemoveEventHandler(_windowEventHandlerRef); if(status != noErr) { //WEBRTC_TRACE(kTraceDebug, "%s:%d failed to remove window event handler %d", __FUNCTION__, __LINE__, (int)_windowEventHandlerRef); } } #endif #ifdef NEW_HIVIEW_EVENT_HANDLER if(_hiviewEventHandlerRef) { status = RemoveEventHandler(_hiviewEventHandlerRef); if(status != noErr) { //WEBRTC_TRACE(kTraceDebug, "%s:%d Failed to remove hiview event handler: %d", __FUNCTION__, __LINE__, (int)_hiviewEventHandlerRef); } } #endif // Signal event to exit thread, then delete it ThreadWrapper* tmpPtr = _screenUpdateThread.release(); if (tmpPtr) { _screenUpdateEvent->Set(); _screenUpdateEvent->StopTimer(); tmpPtr->Stop(); delete tmpPtr; delete _screenUpdateEvent; _screenUpdateEvent = NULL; } if (_aglContext != 0) { aglSetCurrentContext(_aglContext); aglDestroyContext(_aglContext); _aglContext = 0; } // Delete all channels std::map::iterator it = _aglChannels.begin(); while (it!= _aglChannels.end()) { delete it->second; _aglChannels.erase(it); it = _aglChannels.begin(); } _aglChannels.clear(); // Clean the zOrder map std::multimap::iterator zIt = _zOrderToChannel.begin(); while(zIt != _zOrderToChannel.end()) { _zOrderToChannel.erase(zIt); zIt = _zOrderToChannel.begin(); } _zOrderToChannel.clear(); //delete _renderCritSec; } int VideoRenderAGL::GetOpenGLVersion(int& aglMajor, int& aglMinor) { aglGetVersion((GLint *) &aglMajor, (GLint *) &aglMinor); return 0; } int VideoRenderAGL::Init() { LockAGLCntx(); // Start rendering thread... if (!_screenUpdateThread) { UnlockAGLCntx(); //WEBRTC_TRACE(kTraceError, "%s:%d Thread not created", __FUNCTION__, __LINE__); return -1; } _screenUpdateThread->Start(); _screenUpdateThread->SetPriority(kRealtimePriority); // Start the event triggering the render process unsigned int monitorFreq = 60; _screenUpdateEvent->StartTimer(true, 1000/monitorFreq); // Create mixing textures if (CreateMixingContext() == -1) { //WEBRTC_TRACE(kTraceError, "%s:%d Could not create a mixing context", __FUNCTION__, __LINE__); UnlockAGLCntx(); return -1; } UnlockAGLCntx(); return 0; } VideoChannelAGL* VideoRenderAGL::CreateAGLChannel(int channel, int zOrder, float startWidth, float startHeight, float stopWidth, float stopHeight) { LockAGLCntx(); //WEBRTC_TRACE(kTraceInfo, "%s:%d Creating AGL channel: %d", __FUNCTION__, __LINE__, channel); if (HasChannel(channel)) { //WEBRTC_TRACE(kTraceError, "%s:%d Channel already exists", __FUNCTION__, __LINE__); UnlockAGLCntx();k return NULL; } if (_zOrderToChannel.find(zOrder) != _zOrderToChannel.end()) { // There are already one channel using this zOrder // TODO: Allow multiple channels with same zOrder } VideoChannelAGL* newAGLChannel = new VideoChannelAGL(_aglContext, _id, this); if (newAGLChannel->SetStreamSettings(0, startWidth, startHeight, stopWidth, stopHeight) == -1) { if (newAGLChannel) { delete newAGLChannel; newAGLChannel = NULL; } //WEBRTC_LOG(kTraceError, "Could not create AGL channel"); //WEBRTC_TRACE(kTraceError, "%s:%d Could not create AGL channel", __FUNCTION__, __LINE__); UnlockAGLCntx(); return NULL; } k _aglChannels[channel] = newAGLChannel; _zOrderToChannel.insert(std::pair(zOrder, channel)); UnlockAGLCntx(); return newAGLChannel; } int VideoRenderAGL::DeleteAllAGLChannels() { CriticalSectionScoped cs(&_renderCritSec); //WEBRTC_TRACE(kTraceInfo, "%s:%d Deleting all AGL channels", __FUNCTION__, __LINE__); //int i = 0 ; std::map::iterator it; it = _aglChannels.begin(); while (it != _aglChannels.end()) { VideoChannelAGL* channel = it->second; if (channel) delete channel; _aglChannels.erase(it); it = _aglChannels.begin(); } _aglChannels.clear(); return 0; } int VideoRenderAGL::DeleteAGLChannel(int channel) { CriticalSectionScoped cs(&_renderCritSec); //WEBRTC_TRACE(kTraceDebug, "%s:%d Deleting AGL channel %d", __FUNCTION__, __LINE__, channel); std::map::iterator it; it = _aglChannels.find(channel); if (it != _aglChannels.end()) { delete it->second; _aglChannels.erase(it); } else { //WEBRTC_TRACE(kTraceWarning, "%s:%d Channel not found", __FUNCTION__, __LINE__); return -1; } std::multimap::iterator zIt = _zOrderToChannel.begin(); while( zIt != _zOrderToChannel.end()) { if (zIt->second == channel) { _zOrderToChannel.erase(zIt); break; } zIt++;// = _zOrderToChannel.begin(); } return 0; } int VideoRenderAGL::StopThread() { CriticalSectionScoped cs(&_renderCritSec); ThreadWrapper* tmpPtr = _screenUpdateThread.release(); if (tmpPtr) { _screenUpdateEvent->Set(); _renderCritSec.Leave(); tmpPtr->Stop(); delete tmpPtr; _renderCritSec.Enter(); } delete _screenUpdateEvent; _screenUpdateEvent = NULL; return 0; } bool VideoRenderAGL::IsFullScreen() { CriticalSectionScoped cs(&_renderCritSec); return _fullScreen; } bool VideoRenderAGL::HasChannels() { CriticalSectionScoped cs(&_renderCritSec); if (_aglChannels.begin() != _aglChannels.end()) { return true; } return false; } bool VideoRenderAGL::HasChannel(int channel) { CriticalSectionScoped cs(&_renderCritSec); std::map::iterator it = _aglChannels.find(channel); if (it != _aglChannels.end()) { return true; } return false; } int VideoRenderAGL::GetChannels(std::list& channelList) { CriticalSectionScoped cs(&_renderCritSec); std::map::iterator it = _aglChannels.begin(); while (it != _aglChannels.end()) { channelList.push_back(it->first); it++; } return 0; } VideoChannelAGL* VideoRenderAGL::ConfigureAGLChannel(int channel, int zOrder, float startWidth, float startHeight, float stopWidth, float stopHeight) { CriticalSectionScoped cs(&_renderCritSec); std::map::iterator it = _aglChannels.find(channel); if (it != _aglChannels.end()) { VideoChannelAGL* aglChannel = it->second; if (aglChannel->SetStreamSettings(0, startWidth, startHeight, stopWidth, stopHeight) == -1) { return NULL; } std::multimap::iterator it = _zOrderToChannel.begin(); while(it != _zOrderToChannel.end()) { if (it->second == channel) { if (it->first != zOrder) { _zOrderToChannel.erase(it); _zOrderToChannel.insert(std::pair(zOrder, channel)); } break; } it++; } return aglChannel; } return NULL; } bool VideoRenderAGL::ScreenUpdateThreadProc(void* obj) { return static_cast(obj)->ScreenUpdateProcess(); } bool VideoRenderAGL::ScreenUpdateProcess() { _screenUpdateEvent->Wait(100); LockAGLCntx(); if (!_screenUpdateThread) { UnlockAGLCntx(); return false; } if (aglSetCurrentContext(_aglContext) == GL_FALSE) { UnlockAGLCntx(); return true; } if (GetWindowRect(_windowRect) == -1) { UnlockAGLCntx(); return true; } if (_windowWidth != (_windowRect.right - _windowRect.left) || _windowHeight != (_windowRect.bottom - _windowRect.top)) { // We have a new window size, update the context. if (aglUpdateContext(_aglContext) == GL_FALSE) { UnlockAGLCntx(); return true; } _windowWidth = _windowRect.right - _windowRect.left; _windowHeight = _windowRect.bottom - _windowRect.top; } // this section will poll to see if the window size has changed // this is causing problem w/invalid windowRef // this code has been modified and exists now in the window event handler #ifndef NEW_HIVIEW_PARENT_EVENT_HANDLER if (_isHIViewRef) { if(FALSE == HIViewIsValid(_hiviewRef)) { //WEBRTC_TRACE(kTraceDebug, "%s:%d Invalid windowRef", __FUNCTION__, __LINE__); UnlockAGLCntx(); return true; } WindowRef window = HIViewGetWindow(_hiviewRef); if(FALSE == IsValidWindowPtr(window)) { //WEBRTC_TRACE(kTraceDebug, "%s:%d Invalide hiviewRef", __FUNCTION__, __LINE__); UnlockAGLCntx(); return true; } if (window == NULL) { //WEBRTC_TRACE(kTraceDebug, "%s:%d WindowRef = NULL", __FUNCTION__, __LINE__); UnlockAGLCntx(); return true; } if(FALSE == MacIsWindowVisible(window)) { //WEBRTC_TRACE(kTraceDebug, "%s:%d MacIsWindowVisible == FALSE. Returning early", __FUNCTION__, __LINE__); UnlockAGLCntx(); return true; } HIRect viewBounds; // Placement and size for HIView int windowWidth = 0; // Parent window width int windowHeight = 0; // Parent window height // NOTE: Calling GetWindowBounds with kWindowStructureRgn will crash intermittentaly if the OS decides it needs to push it into the back for a moment. // To counter this, we get the titlebar height on class construction and then add it to the content region here. Content regions seems not to crash Rect contentBounds = { 0, 0, 0, 0}; // The bounds for the parent window #if defined(USE_CONTENT_RGN) GetWindowBounds(window, kWindowContentRgn, &contentBounds); #elif defined(USE_STRUCT_RGN) GetWindowBounds(window, kWindowStructureRgn, &contentBounds); #endif Rect globalBounds = { 0, 0, 0, 0}; // The bounds for the parent window globalBounds.top = contentBounds.top; globalBounds.right = contentBounds.right; globalBounds.bottom = contentBounds.bottom; globalBounds.left = contentBounds.left; windowHeight = globalBounds.bottom - globalBounds.top; windowWidth = globalBounds.right - globalBounds.left; // Get the size of the HIViewRef HIViewGetBounds(_hiviewRef, &viewBounds); HIViewConvertRect(&viewBounds, _hiviewRef, NULL); // Check if this is the first call.. if (_lastWindowHeight == -1 && _lastWindowWidth == -1) { _lastWindowWidth = windowWidth; _lastWindowHeight = windowHeight; _lastViewBounds.origin.x = viewBounds.origin.x; _lastViewBounds.origin.y = viewBounds.origin.y; _lastViewBounds.size.width = viewBounds.size.width; _lastViewBounds.size.height = viewBounds.size.height; } sfasdfasdf bool resized = false; // Check if parent window size has changed if (windowHeight != _lastWindowHeight || windowWidth != _lastWindowWidth) { resized = true; } // Check if the HIView has new size or is moved in the parent window if (_lastViewBounds.origin.x != viewBounds.origin.x || _lastViewBounds.origin.y != viewBounds.origin.y || _lastViewBounds.size.width != viewBounds.size.width || _lastViewBounds.size.height != viewBounds.size.height) { // The HiView is resized or has moved. resized = true; } if (resized) { //WEBRTC_TRACE(kTraceDebug, "%s:%d Window has resized", __FUNCTION__, __LINE__); // Calculate offset between the windows // {x, y, widht, height}, x,y = lower left corner const GLint offs[4] = { (int)(0.5f + viewBounds.origin.x), (int)(0.5f + windowHeight - (viewBounds.origin.y + viewBounds.size.height)), viewBounds.size.width, viewBounds.size.height}; //WEBRTC_TRACE(kTraceDebug, "%s:%d contentBounds t:%d r:%d b:%d l:%d", __FUNCTION__, __LINE__, contentBounds.top, contentBounds.right, contentBounds.bottom, contentBounds.left); //WEBRTC_TRACE(kTraceDebug, "%s:%d windowHeight=%d", __FUNCTION__, __LINE__, windowHeight); //WEBRTC_TRACE(kTraceDebug, "%s:%d offs[4] = %d, %d, %d, %d", __FUNCTION__, __LINE__, offs[0], offs[1], offs[2], offs[3]); aglSetDrawable (_aglContext, GetWindowPort(window)); aglSetInteger(_aglContext, AGL_BUFFER_RECT, offs); aglEnable(_aglContext, AGL_BUFFER_RECT); // We need to change the viewport too if the HIView size has changed glViewport(0.0f, 0.0f, (GLsizei) viewBounds.size.width, (GLsizei) viewBounds.size.height); } _lastWindowWidth = windowWidth; _lastWindowHeight = windowHeight; _lastViewBounds.origin.x = viewBounds.origin.x; _lastViewBounds.origin.y = viewBounds.origin.y; _lastViewBounds.size.width = viewBounds.size.width; _lastViewBounds.size.height = viewBounds.size.height; } #endif if (_fullScreen) { // TODO // We use double buffers, must always update //RenderOffScreenBuffersToBackBuffer(); } else { // Check if there are any updated buffers bool updated = false; // TODO: check if window size is updated! // TODO Improvement: Walk through the zOrder Map to only render the ones in need of update std::map::iterator it = _aglChannels.begin(); while (it != _aglChannels.end()) { VideoChannelAGL* aglChannel = it->second; aglChannel->UpdateStretchSize(_windowHeight, _windowWidth); aglChannel->IsUpdated(updated); if (updated) { break; } it++; } if (updated) { // At least on buffers is updated, we need to repaint the texture if (RenderOffScreenBuffers() != -1) { // MF //SwapAndDisplayBuffers(); } else { // Error updating the mixing texture, don't swap. } } } UnlockAGLCntx(); //WEBRTC_LOG(kTraceDebug, "Leaving ScreenUpdateProcess()"); return true; } void VideoRenderAGL::ParentWindowResized(WindowRef window) { //WEBRTC_LOG(kTraceDebug, "%s HIViewRef:%d owner window has resized", __FUNCTION__, (int)_hiviewRef); LockAGLCntx(); k // set flag _windowHasResized = false; if(FALSE == HIViewIsValid(_hiviewRef)) { //WEBRTC_LOG(kTraceDebug, "invalid windowRef"); UnlockAGLCntx(); return; } if(FALSE == IsValidWindowPtr(window)) { //WEBRTC_LOG(kTraceError, "invalid windowRef"); UnlockAGLCntx(); return; } if (window == NULL) { //WEBRTC_LOG(kTraceError, "windowRef = NULL"); UnlockAGLCntx(); return; } if(FALSE == MacIsWindowVisible(window)) { //WEBRTC_LOG(kTraceDebug, "MacIsWindowVisible = FALSE. Returning early."); UnlockAGLCntx(); return; } Rect contentBounds = { 0, 0, 0, 0}; #if defined(USE_CONTENT_RGN) GetWindowBounds(window, kWindowContentRgn, &contentBounds); #elif defined(USE_STRUCT_RGN) GetWindowBounds(window, kWindowStructureRgn, &contentBounds); #endif //WEBRTC_LOG(kTraceDebug, "%s contentBounds t:%d r:%d b:%d l:%d", __FUNCTION__, contentBounds.top, contentBounds.right, contentBounds.bottom, contentBounds.left); // update global vars _currentParentWindowBounds.top = contentBounds.top; _currentParentWindowBounds.left = contentBounds.left; _currentParentWindowBounds.bottom = contentBounds.bottom; _currentParentWindowBounds.right = contentBounds.right; _currentParentWindowWidth = _currentParentWindowBounds.right - _currentParentWindowBounds.left; _currentParentWindowHeight = _currentParentWindowBounds.bottom - _currentParentWindowBounds.top; _windowHasResized = true; // ********* update AGL offsets HIRect viewBounds; HIViewGetBounds(_hiviewRef, &viewBounds); HIViewConvertRect(&viewBounds, _hiviewRef, NULL); const GLint offs[4] = { (int)(0.5f + viewBounds.origin.x), (int)(0.5f + _currentParentWindowHeight - (viewBounds.origin.y + viewBounds.size.height)), viewBounds.size.width, viewBounds.size.height}; //WEBRTC_LOG(kTraceDebug, "%s _currentParentWindowHeight=%d", __FUNCTION__, _currentParentWindowHeight); //WEBRTC_LOG(kTraceDebug, "%s offs[4] = %d, %d, %d, %d", __FUNCTION__, offs[0], offs[1], offs[2], offs[3]); aglSetCurrentContext(_aglContext); aglSetDrawable (_aglContext, GetWindowPort(window)); aglSetInteger(_aglContext, AGL_BUFFER_RECT, offs); aglEnable(_aglContext, AGL_BUFFER_RECT); // We need to change the viewport too if the HIView size has changed glViewport(0.0f, 0.0f, (GLsizei) viewBounds.size.width, (GLsizei) viewBounds.size.height); UnlockAGLCntx(); return; } int VideoRenderAGL::CreateMixingContext() { LockAGLCntx(); //WEBRTC_LOG(kTraceDebug, "Entering CreateMixingContext()"); // Use both AGL_ACCELERATED and AGL_NO_RECOVERY to make sure // a hardware renderer is used and not a software renderer. GLint attributes[] = { AGL_DOUBLEBUFFER, AGL_WINDOW, AGL_RGBA, AGL_NO_RECOVERY, AGL_ACCELERATED, AGL_RED_SIZE, 8, AGL_GREEN_SIZE, 8, AGL_BLUE_SIZE, 8, AGL_ALPHA_SIZE, 8, AGL_DEPTH_SIZE, 24, AGL_NONE, }; AGLPixelFormat aglPixelFormat; // ***** Set up the OpenGL Context ***** // Get a pixel format for the attributes above aglPixelFormat = aglChoosePixelFormat(NULL, 0, attributes); if (NULL == aglPixelFormat) { //WEBRTC_LOG(kTraceError, "Could not create pixel format"); UnlockAGLCntx(); return -1; } // Create an AGL context _aglContext = aglCreateContext(aglPixelFormat, NULL); if (_aglContext == NULL) { //WEBRTC_LOG(kTraceError, "Could no create AGL context"); UnlockAGLCntx(); return -1; } // Release the pixel format memory aglDestroyPixelFormat(aglPixelFormat); // Set the current AGL context for the rest of the settings if (aglSetCurrentContext(_aglContext) == false) { //WEBRTC_LOG(kTraceError, "Could not set current context: %d", aglGetError()); UnlockAGLCntx(); return -1; } if (_isHIViewRef) { //--------------------------- // BEGIN: new test code #if 0 // Don't use this one! // There seems to be an OS X bug that can't handle // movements and resizing of the parent window // and or the HIView if (aglSetHIViewRef(_aglContext,_hiviewRef) == false) { //WEBRTC_LOG(kTraceError, "Could not set WindowRef: %d", aglGetError()); UnlockAGLCntx(); return -1; } #else // Get the parent window for this control WindowRef window = GetControlOwner(_hiviewRef); Rect globalBounds = { 0,0,0,0}; // The bounds for the parent window HIRect viewBounds; // Placemnt in the parent window and size. int windowHeight = 0; // Rect titleBounds = {0,0,0,0}; // GetWindowBounds(window, kWindowTitleBarRgn, &titleBounds); // _titleBarHeight = titleBounds.top - titleBounds.bottom; // if(0 == _titleBarHeight) // { // //WEBRTC_LOG(kTraceError, "Titlebar height = 0"); // //return -1; // } // Get the bounds for the parent window #if defined(USE_CONTENT_RGN) GetWindowBounds(window, kWindowContentRgn, &globalBounds); #elif defined(USE_STRUCT_RGN) GetWindowBounds(window, kWindowStructureRgn, &globalBounds); #endif windowHeight = globalBounds.bottom - globalBounds.top; // Get the bounds for the HIView HIViewGetBounds(_hiviewRef, &viewBounds); HIViewConvertRect(&viewBounds, _hiviewRef, NULL); const GLint offs[4] = { (int)(0.5f + viewBounds.origin.x), (int)(0.5f + windowHeight - (viewBounds.origin.y + viewBounds.size.height)), viewBounds.size.width, viewBounds.size.height}; //WEBRTC_LOG(kTraceDebug, "%s offs[4] = %d, %d, %d, %d", __FUNCTION__, offs[0], offs[1], offs[2], offs[3]); aglSetDrawable (_aglContext, GetWindowPort(window)); aglSetInteger(_aglContext, AGL_BUFFER_RECT, offs); aglEnable(_aglContext, AGL_BUFFER_RECT); GLint surfaceOrder = 1; // 1: above window, -1 below. //OSStatus status = aglSetInteger(_aglContext, AGL_SURFACE_ORDER, &surfaceOrder); aglSetInteger(_aglContext, AGL_SURFACE_ORDER, &surfaceOrder); glViewport(0.0f, 0.0f, (GLsizei) viewBounds.size.width, (GLsizei) viewBounds.size.height); #endif } else { if(GL_FALSE == aglSetDrawable (_aglContext, GetWindowPort(_windowRef))) { //WEBRTC_LOG(kTraceError, "Could not set WindowRef: %d", aglGetError()); UnlockAGLCntx(); return -1; } } _windowWidth = _windowRect.right - _windowRect.left; _windowHeight = _windowRect.bottom - _windowRect.top; // opaque surface int surfaceOpacity = 1; if (aglSetInteger(_aglContext, AGL_SURFACE_OPACITY, (const GLint *) &surfaceOpacity) == false) { //WEBRTC_LOG(kTraceError, "Could not set surface opacity: %d", aglGetError()); UnlockAGLCntx(); return -1; } // 1 -> sync to screen rat, slow... //int swapInterval = 0; // 0 don't sync with vertical trace int swapInterval = 0; // 1 sync with vertical trace if (aglSetInteger(_aglContext, AGL_SWAP_INTERVAL, (const GLint *) &swapInterval) == false) { //WEBRTC_LOG(kTraceError, "Could not set swap interval: %d", aglGetError()); UnlockAGLCntx(); return -1; } // Update the rect with the current size if (GetWindowRect(_windowRect) == -1) { //WEBRTC_LOG(kTraceError, "Could not get window size"); UnlockAGLCntx(); return -1; } // Disable not needed functionality to increase performance glDisable(GL_DITHER); glDisable(GL_ALPHA_TEST); glDisable(GL_STENCIL_TEST); glDisable(GL_FOG); glDisable(GL_TEXTURE_2D); glPixelZoom(1.0, 1.0); glDisable(GL_BLEND); glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); glDisable(GL_CULL_FACE); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); GLenum glErr = glGetError(); if (glErr) { } UpdateClipping(); //WEBRTC_LOG(kTraceDebug, "Leaving CreateMixingContext()"); UnlockAGLCntx(); return 0; } int VideoRenderAGL::RenderOffScreenBuffers() { LockAGLCntx(); // Get the current window size, it might have changed since last render. if (GetWindowRect(_windowRect) == -1) { //WEBRTC_LOG(kTraceError, "Could not get window rect"); UnlockAGLCntx(); return -1; } if (aglSetCurrentContext(_aglContext) == false) { //WEBRTC_LOG(kTraceError, "Could not set current context for rendering"); UnlockAGLCntx(); return -1; } // HERE - onl if updated! glClear(GL_COLOR_BUFFER_BIT); // Loop through all channels starting highest zOrder ending with lowest. for (std::multimap::reverse_iterator rIt = _zOrderToChannel.rbegin(); rIt != _zOrderToChannel.rend(); rIt++) { int channelId = rIt->second; std::map::iterator it = _aglChannels.find(channelId); VideoChannelAGL* aglChannel = it->second; aglChannel->RenderOffScreenBuffer(); } SwapAndDisplayBuffers(); UnlockAGLCntx(); return 0; } int VideoRenderAGL::SwapAndDisplayBuffers() { LockAGLCntx(); if (_fullScreen) { // TODO: // Swap front and back buffers, rendering taking care of in the same call //aglSwapBuffers(_aglContext); // Update buffer index to the idx for the next rendering! //_textureIdx = (_textureIdx + 1) & 1; } else { // Single buffer rendering, only update context. glFlush(); aglSwapBuffers(_aglContext); HIViewSetNeedsDisplay(_hiviewRef, true); } UnlockAGLCntx(); return 0; } int VideoRenderAGL::GetWindowRect(Rect& rect) { LockAGLCntx(); if (_isHIViewRef) { if (_hiviewRef) { HIRect HIViewRect1; if(FALSE == HIViewIsValid(_hiviewRef)) { rect.top = 0; rect.left = 0; rect.right = 0; rect.bottom = 0; //WEBRTC_LOG(kTraceError,"GetWindowRect() HIViewIsValid() returned false"); UnlockAGLCntx(); } HIViewGetBounds(_hiviewRef,&HIViewRect1); HIRectConvert(&HIViewRect1, 1, NULL, 2, NULL); if(HIViewRect1.origin.x < 0) { rect.top = 0; //WEBRTC_LOG(kTraceDebug, "GetWindowRect() rect.top = 0"); } else { rect.top = HIViewRect1.origin.x; } if(HIViewRect1.origin.y < 0) { rect.left = 0; //WEBRTC_LOG(kTraceDebug, "GetWindowRect() rect.left = 0"); } else { rect.left = HIViewRect1.origin.y; } if(HIViewRect1.size.width < 0) { rect.right = 0; //WEBRTC_LOG(kTraceDebug, "GetWindowRect() rect.right = 0"); } else { rect.right = HIViewRect1.size.width; } if(HIViewRect1.size.height < 0) { rect.bottom = 0; //WEBRTC_LOG(kTraceDebug, "GetWindowRect() rect.bottom = 0"); } else { rect.bottom = HIViewRect1.size.height; } ////WEBRTC_LOG(kTraceDebug,"GetWindowRect() HIViewRef: rect.top = %d, rect.left = %d, rect.right = %d, rect.bottom =%d in GetWindowRect", rect.top,rect.left,rect.right,rect.bottom); UnlockAGLCntx(); } else { //WEBRTC_LOG(kTraceError, "invalid HIViewRef"); UnlockAGLCntx(); } } else { if (_windowRef) { GetWindowBounds(_windowRef, kWindowContentRgn, &rect); UnlockAGLCntx(); } else { //WEBRTC_LOG(kTraceError, "No WindowRef"); UnlockAGLCntx(); } } } int VideoRenderAGL::UpdateClipping() { //WEBRTC_LOG(kTraceDebug, "Entering UpdateClipping()"); LockAGLCntx(); if(_isHIViewRef) { if(FALSE == HIViewIsValid(_hiviewRef)) { //WEBRTC_LOG(kTraceError, "UpdateClipping() _isHIViewRef is invalid. Returning -1"); UnlockAGLCntx(); return -1; } RgnHandle visibleRgn = NewRgn(); SetEmptyRgn (visibleRgn); if(-1 == CalculateVisibleRegion((ControlRef)_hiviewRef, visibleRgn, true)) { } if(GL_FALSE == aglSetCurrentContext(_aglContext)) { GLenum glErr = aglGetError(); //WEBRTC_LOG(kTraceError, "aglSetCurrentContext returned FALSE with error code %d at line %d", glErr, __LINE__); } if(GL_FALSE == aglEnable(_aglContext, AGL_CLIP_REGION)) { GLenum glErr = aglGetError(); //WEBRTC_LOG(kTraceError, "aglEnable returned FALSE with error code %d at line %d\n", glErr, __LINE__); } if(GL_FALSE == aglSetInteger(_aglContext, AGL_CLIP_REGION, (const GLint*)visibleRgn)) { GLenum glErr = aglGetError(); //WEBRTC_LOG(kTraceError, "aglSetInteger returned FALSE with error code %d at line %d\n", glErr, __LINE__); } DisposeRgn(visibleRgn); } else { //WEBRTC_LOG(kTraceDebug, "Not using a hiviewref!\n"); } //WEBRTC_LOG(kTraceDebug, "Leaving UpdateClipping()"); UnlockAGLCntx(); return true; } int VideoRenderAGL::CalculateVisibleRegion(ControlRef control, RgnHandle &visibleRgn, bool clipChildren) { // LockAGLCntx(); //WEBRTC_LOG(kTraceDebug, "Entering CalculateVisibleRegion()"); OSStatus osStatus = 0; OSErr osErr = 0; RgnHandle tempRgn = NewRgn(); if (IsControlVisible(control)) { RgnHandle childRgn = NewRgn(); WindowRef window = GetControlOwner(control); ControlRef rootControl; GetRootControl(window, &rootControl); // 'wvnc' ControlRef masterControl; osStatus = GetSuperControl(rootControl, &masterControl); // //WEBRTC_LOG(kTraceDebug, "IBM GetSuperControl=%d", osStatus); if (masterControl != NULL) { CheckValidRegion(visibleRgn); // init visibleRgn with region of 'wvnc' osStatus = GetControlRegion(rootControl, kControlStructureMetaPart, visibleRgn); // //WEBRTC_LOG(kTraceDebug, "IBM GetControlRegion=%d : %d", osStatus, __LINE__); //GetSuperControl(rootControl, &rootControl); ControlRef tempControl = control, lastControl = 0; while (tempControl != masterControl) // current control != master { CheckValidRegion(tempRgn); // //WEBRTC_LOG(kTraceDebug, "IBM tempControl=%d masterControl=%d", tempControl, masterControl); ControlRef subControl; osStatus = GetControlRegion(tempControl, kControlStructureMetaPart, tempRgn); // intersect the region of the current control with visibleRgn // //WEBRTC_LOG(kTraceDebug, "IBM GetControlRegion=%d : %d", osStatus, __LINE__); CheckValidRegion(tempRgn); osErr = HIViewConvertRegion(tempRgn, tempControl, rootControl); // //WEBRTC_LOG(kTraceDebug, "IBM HIViewConvertRegion=%d : %d", osErr, __LINE__); CheckValidRegion(tempRgn); SectRgn(tempRgn, visibleRgn, visibleRgn); CheckValidRegion(tempRgn); CheckValidRegion(visibleRgn); if (EmptyRgn(visibleRgn)) // if the region is empty, bail break; if (clipChildren || tempControl != control) // clip children if true, cut out the tempControl if it's not one passed to this function { UInt16 numChildren; osStatus = CountSubControls(tempControl, &numChildren); // count the subcontrols // //WEBRTC_LOG(kTraceDebug, "IBM CountSubControls=%d : %d", osStatus, __LINE__); // //WEBRTC_LOG(kTraceDebug, "IBM numChildren=%d", numChildren); for (int i = 0; i < numChildren; i++) { osErr = GetIndexedSubControl(tempControl, numChildren - i, &subControl); // retrieve the subcontrol in order by zorder // //WEBRTC_LOG(kTraceDebug, "IBM GetIndexedSubControls=%d : %d", osErr, __LINE__); if ( subControl == lastControl ) // break because of zorder { // //WEBRTC_LOG(kTraceDebug, "IBM breaking because of zorder %d", __LINE__); break; } if (!IsControlVisible(subControl)) // dont' clip invisible controls { // //WEBRTC_LOG(kTraceDebug, "IBM continue. Control is not visible %d", __LINE__); continue; } if(!subControl) continue; osStatus = GetControlRegion(subControl, kControlStructureMetaPart, tempRgn); //get the region of the current control and union to childrg // //WEBRTC_LOG(kTraceDebug, "IBM GetControlRegion=%d %d", osStatus, __LINE__); CheckValidRegion(tempRgn); if(osStatus != 0) { // //WEBRTC_LOG(kTraceDebug, "IBM ERROR! osStatus=%d. Continuing. %d", osStatus, __LINE__); continue; } if(!tempRgn) { // //WEBRTC_LOG(kTraceDebug, "IBM ERROR! !tempRgn %d", osStatus, __LINE__); continue; } osStatus = HIViewConvertRegion(tempRgn, subControl, rootControl); CheckValidRegion(tempRgn); // //WEBRTC_LOG(kTraceDebug, "IBM HIViewConvertRegion=%d %d", osStatus, __LINE__); if(osStatus != 0) { // //WEBRTC_LOG(kTraceDebug, "IBM ERROR! osStatus=%d. Continuing. %d", osStatus, __LINE__); continue; } if(!rootControl) { // //WEBRTC_LOG(kTraceDebug, "IBM ERROR! !rootControl %d", osStatus, __LINE__); continue; } UnionRgn(tempRgn, childRgn, childRgn); CheckValidRegion(tempRgn); CheckValidRegion(childRgn); CheckValidRegion(visibleRgn); if(!childRgn) { // //WEBRTC_LOG(kTraceDebug, "IBM ERROR! !childRgn %d", osStatus, __LINE__); continue; } } // next child control } lastControl = tempControl; GetSuperControl(tempControl, &subControl); tempControl = subControl; } DiffRgn(visibleRgn, childRgn, visibleRgn); CheckValidRegion(visibleRgn); CheckValidRegion(childRgn); DisposeRgn(childRgn); } else { CopyRgn(tempRgn, visibleRgn); CheckValidRegion(tempRgn); CheckValidRegion(visibleRgn); } DisposeRgn(tempRgn); } //WEBRTC_LOG(kTraceDebug, "Leaving CalculateVisibleRegion()"); //_aglCritPtr->Leave(); return 0; } bool VideoRenderAGL::CheckValidRegion(RgnHandle rHandle) { Handle hndSize = (Handle)rHandle; long size = GetHandleSize(hndSize); if(0 == size) { OSErr memErr = MemError(); if(noErr != memErr) { // //WEBRTC_LOG(kTraceError, "IBM ERROR Could not get size of handle. MemError() returned %d", memErr); } else { // //WEBRTC_LOG(kTraceError, "IBM ERROR Could not get size of handle yet MemError() returned noErr"); } } else { // //WEBRTC_LOG(kTraceDebug, "IBM handleSize = %d", size); } if(false == IsValidRgnHandle(rHandle)) { // //WEBRTC_LOG(kTraceError, "IBM ERROR Invalid Region found : $%d", rHandle); assert(false); } int err = QDError(); switch(err) { case 0: break; case -147: //WEBRTC_LOG(kTraceError, "ERROR region too big"); assert(false); break; case -149: //WEBRTC_LOG(kTraceError, "ERROR not enough stack"); assert(false); break; default: //WEBRTC_LOG(kTraceError, "ERROR Unknown QDError %d", err); assert(false); break; } return true; } int VideoRenderAGL::ChangeWindow(void* newWindowRef) { LockAGLCntx(); UnlockAGLCntx(); return -1; } int32_t VideoRenderAGL::StartRender() { LockAGLCntx(); const unsigned int MONITOR_FREQ = 60; if(TRUE == _renderingIsPaused) { //WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d Rendering is paused. Restarting now", __FUNCTION__, __LINE__); // we already have the thread. Most likely StopRender() was called and they were paused if(FALSE == _screenUpdateThread->Start()) { //WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s:%d Failed to start screenUpdateThread", __FUNCTION__, __LINE__); UnlockAGLCntx(); return -1; } _screenUpdateThread->SetPriority(kRealtimePriority); if(FALSE == _screenUpdateEvent->StartTimer(true, 1000/MONITOR_FREQ)) { //WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s:%d Failed to start screenUpdateEvent", __FUNCTION__, __LINE__); UnlockAGLCntx(); return -1; } return 0; } _screenUpdateThread = ThreadWrapper::CreateThread(ScreenUpdateThreadProc, this, "ScreenUpdate"); _screenUpdateEvent = EventWrapper::Create(); if (!_screenUpdateThread) { //WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s:%d Failed to start screenUpdateThread", __FUNCTION__, __LINE__); UnlockAGLCntx(); return -1; } _screenUpdateThread->Start(); _screenUpdateThread->SetPriority(kRealtimePriority); _screenUpdateEvent->StartTimer(true, 1000/MONITOR_FREQ); //WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d Started screenUpdateThread", __FUNCTION__, __LINE__); UnlockAGLCntx(); return 0; } int32_t VideoRenderAGL::StopRender() { LockAGLCntx(); if(!_screenUpdateThread || !_screenUpdateEvent) { _renderingIsPaused = TRUE; UnlockAGLCntx(); return 0; } if(FALSE == _screenUpdateThread->Stop() || FALSE == _screenUpdateEvent->StopTimer()) { _renderingIsPaused = FALSE; //WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d Could not stop either: screenUpdateThread or screenUpdateEvent", __FUNCTION__, __LINE__); UnlockAGLCntx(); return -1; } _renderingIsPaused = TRUE; //WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d Stopped screenUpdateThread", __FUNCTION__, __LINE__); UnlockAGLCntx(); return 0; } int32_t VideoRenderAGL::DeleteAGLChannel(const uint32_t streamID) { LockAGLCntx(); std::map::iterator it; it = _aglChannels.begin(); while (it != _aglChannels.end()) { VideoChannelAGL* channel = it->second; //WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s:%d Deleting channel %d", __FUNCTION__, __LINE__, streamID); delete channel; it++; } _aglChannels.clear(); UnlockAGLCntx(); return 0; } int32_t VideoRenderAGL::GetChannelProperties(const uint16_t streamId, uint32_t& zOrder, float& left, float& top, float& right, float& bottom) { LockAGLCntx(); UnlockAGLCntx(); return -1; } void VideoRenderAGL::LockAGLCntx() { _renderCritSec.Enter(); } void VideoRenderAGL::UnlockAGLCntx() { _renderCritSec.Leave(); } } // namespace webrtc #endif // CARBON_RENDERING