/* * 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 "engine_configurations.h" #if defined(COCOA_RENDERING) #include "video_render_nsopengl.h" #include "critical_section_wrapper.h" #include "event_wrapper.h" #include "trace.h" #include "thread_wrapper.h" #include "vplib.h" namespace webrtc { VideoChannelNSOpenGL::VideoChannelNSOpenGL(NSOpenGLContext *nsglContext, int iId, VideoRenderNSOpenGL* owner) : _nsglContext( nsglContext), _id( iId), _owner( owner), _width( 0), _height( 0), _startWidth( 0.0f), _startHeight( 0.0f), _stopWidth( 0.0f), _stopHeight( 0.0f), _stretchedWidth( 0), _stretchedHeight( 0), _oldStretchedHeight( 0), _oldStretchedWidth( 0), _xOldWidth( 0), _yOldHeight( 0), _buffer( 0), _bufferSize( 0), _incommingBufferSize( 0), _bufferIsUpdated( false), _numberOfStreams( 0), _pixelFormat( GL_RGBA), _pixelDataType( GL_UNSIGNED_INT_8_8_8_8), _texture( 0), _bVideoSizeStartedChanging(false) { } VideoChannelNSOpenGL::~VideoChannelNSOpenGL() { if (_buffer) { delete [] _buffer; _buffer = NULL; } if (_texture != 0) { [_nsglContext makeCurrentContext]; glDeleteTextures(1, (const GLuint*) &_texture); _texture = 0; } } int VideoChannelNSOpenGL::ChangeContext(NSOpenGLContext *nsglContext) { _owner->UnlockAGLCntx(); _nsglContext = nsglContext; [_nsglContext makeCurrentContext]; _owner->UnlockAGLCntx(); return 0; } WebRtc_Word32 VideoChannelNSOpenGL::GetChannelProperties(float& left, float& top, float& right, float& bottom) { _owner->LockAGLCntx(); left = _startWidth; top = _startHeight; right = _stopWidth; bottom = _stopHeight; _owner->UnlockAGLCntx(); return 0; } WebRtc_Word32 VideoChannelNSOpenGL::RenderFrame(const WebRtc_UWord32 /*streamId*/, VideoFrame& videoFrame) { _owner->LockAGLCntx(); if(_width != (int)videoFrame.Width() || _height != (int)videoFrame.Height()) { if(FrameSizeChange(videoFrame.Width(), videoFrame.Height(), 1) == -1) { _owner->UnlockAGLCntx(); return -1; } } int ret = DeliverFrame(videoFrame.Buffer(), videoFrame.Length(), videoFrame.TimeStamp()); _owner->UnlockAGLCntx(); return ret; } int VideoChannelNSOpenGL::UpdateSize(int width, int height) { _owner->LockAGLCntx(); _width = width; _height = height; _owner->UnlockAGLCntx(); return 0; } int VideoChannelNSOpenGL::UpdateStretchSize(int stretchHeight, int stretchWidth) { _owner->LockAGLCntx(); _stretchedHeight = stretchHeight; _stretchedWidth = stretchWidth; _owner->UnlockAGLCntx(); return 0; } int VideoChannelNSOpenGL::FrameSizeChange(int width, int height, int numberOfStreams) { // We got 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; } _incommingBufferSize = CalcBufferSize(kI420, _width, _height); _bufferSize = CalcBufferSize(kARGB, _width, _height);//_width * _height * bytesPerPixel; _buffer = new unsigned char [_bufferSize]; memset(_buffer, 0, _bufferSize * sizeof(unsigned char)); [_nsglContext makeCurrentContext]; if(glIsTexture(_texture)) { glDeleteTextures(1, (const GLuint*) &_texture); _texture = 0; } // Create a new texture glGenTextures(1, (GLuint *) &_texture); GLenum glErr = glGetError(); if (glErr != GL_NO_ERROR) { } glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _texture); GLint texSize; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize); if (texSize < _width || texSize < _height) { _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_RGBA _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; } int VideoChannelNSOpenGL::DeliverFrame(unsigned char* buffer, int bufferSize, unsigned int /*timeStamp90kHz*/) { _owner->LockAGLCntx(); if (_texture == 0) { _owner->UnlockAGLCntx(); return 0; } if (bufferSize != _incommingBufferSize) { _owner->UnlockAGLCntx(); return -1; } int rgbLength = ConvertFromI420(kRGBAMac, buffer, _width, _height, _buffer); if (rgbLength == -1) { _owner->UnlockAGLCntx(); return -1; } [_nsglContext makeCurrentContext]; WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, "bufferSize=%d _width=%d _height=%d", bufferSize, _width, _height); glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _texture); // Make sure this texture is the active one GLenum glErr = glGetError(); if (glErr != GL_NO_ERROR) { WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "ERROR %d while calling glBindTexture", glErr); _owner->UnlockAGLCntx(); return -1; } 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 glErr = glGetError(); if (glErr != GL_NO_ERROR) { WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "ERROR %d while calling glTexSubImage2d", glErr); _owner->UnlockAGLCntx(); return -1; } _bufferIsUpdated = true; _owner->UnlockAGLCntx(); return 0; } int VideoChannelNSOpenGL::RenderOffScreenBuffer() { _owner->LockAGLCntx(); if (_texture == 0) { _owner->UnlockAGLCntx(); return 0; } // if(_fullscreen) // { NSRect mainDisplayRect = [[NSScreen mainScreen] frame]; // _width = mainDisplayRect.size.width; // _height = mainDisplayRect.size.height; // glViewport(0, 0, mainDisplayRect.size.width, mainDisplayRect.size.height); // float newX = mainDisplayRect.size.width/_width; // float newY = mainDisplayRect.size.height/_height; // convert from 0.0 <= size <= 1.0 to // open gl world -1.0 < size < 1.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; [_nsglContext makeCurrentContext]; glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _texture); _oldStretchedHeight = _stretchedHeight; _oldStretchedWidth = _stretchedWidth; 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 VideoChannelNSOpenGL::IsUpdated(bool& isUpdated) { _owner->LockAGLCntx(); isUpdated = _bufferIsUpdated; _owner->UnlockAGLCntx(); return 0; } int VideoChannelNSOpenGL::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 VideoChannelNSOpenGL::SetStreamCropSettings(int /*streamId*/, float /*startWidth*/, float /*startHeight*/, float /*stopWidth*/, float /*stopHeight*/) { return -1; } /* * * VideoRenderNSOpenGL * */ VideoRenderNSOpenGL::VideoRenderNSOpenGL(CocoaRenderView *windowRef, bool fullScreen, int iId) : _windowRef( (CocoaRenderView*)windowRef), _fullScreen( fullScreen), _id( iId), _nsglContextCritSec( *CriticalSectionWrapper::CreateCriticalSection()), _screenUpdateThread( 0), _screenUpdateEvent( 0), _nsglContext( 0), _nsglFullScreenContext( 0), _fullScreenWindow( nil), _windowRect( ), _windowWidth( 0), _windowHeight( 0), _nsglChannels( ), _zOrderToChannel( ), _threadID (0), _renderingIsPaused (FALSE) { _screenUpdateThread = ThreadWrapper::CreateThread(ScreenUpdateThreadProc, this, kRealtimePriority); _screenUpdateEvent = EventWrapper::Create(); } int VideoRenderNSOpenGL::ChangeWindow(CocoaRenderView* newWindowRef) { LockAGLCntx(); _windowRef = newWindowRef; if(CreateMixingContext() == -1) { UnlockAGLCntx(); return -1; } int error = 0; std::map::iterator it = _nsglChannels.begin(); while (it!= _nsglChannels.end()) { error |= (it->second)->ChangeContext(_nsglContext); it++; } if(error != 0) { UnlockAGLCntx(); return -1; } UnlockAGLCntx(); return 0; } /* Check if the thread and event already exist. * If so then they will simply be restarted * If not then create them and continue */ WebRtc_Word32 VideoRenderNSOpenGL::StartRender() { LockAGLCntx(); const unsigned int MONITOR_FREQ = 60; if(TRUE == _renderingIsPaused) { WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "Restarting screenUpdateThread"); // we already have the thread. Most likely StopRender() was called and they were paused if(FALSE == _screenUpdateThread->Start(_threadID) || FALSE == _screenUpdateEvent->StartTimer(true, 1000/MONITOR_FREQ)) { WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "Failed to restart screenUpdateThread or screenUpdateEvent"); UnlockAGLCntx(); return -1; } UnlockAGLCntx(); return 0; } WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "Starting screenUpdateThread and screenUpdateEvent"); _screenUpdateThread = ThreadWrapper::CreateThread(ScreenUpdateThreadProc, this, kRealtimePriority); _screenUpdateEvent = EventWrapper::Create(); if (!_screenUpdateThread) { WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "failed start screenUpdateThread"); UnlockAGLCntx(); return -1; } _screenUpdateThread->Start(_threadID); _screenUpdateEvent->StartTimer(true, 1000/MONITOR_FREQ); UnlockAGLCntx(); return 0; } WebRtc_Word32 VideoRenderNSOpenGL::StopRender() { LockAGLCntx(); /* The code below is functional * but it pauses for several seconds */ // pause the update thread and the event timer if(!_screenUpdateThread || !_screenUpdateEvent) { _renderingIsPaused = TRUE; UnlockAGLCntx(); return 0; } if(FALSE == _screenUpdateThread->Stop() || FALSE == _screenUpdateEvent->StopTimer()) { _renderingIsPaused = FALSE; UnlockAGLCntx(); return -1; } _renderingIsPaused = TRUE; UnlockAGLCntx(); return 0; } int VideoRenderNSOpenGL::configureNSOpenGLView() { return 0; } int VideoRenderNSOpenGL::configureNSOpenGLEngine() { LockAGLCntx(); // 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); // 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); 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); if (GetWindowRect(_windowRect) == -1) { UnlockAGLCntx(); return true; } if (_windowWidth != (_windowRect.right - _windowRect.left) || _windowHeight != (_windowRect.bottom - _windowRect.top)) { _windowWidth = _windowRect.right - _windowRect.left; _windowHeight = _windowRect.bottom - _windowRect.top; } glViewport(0, 0, _windowWidth, _windowHeight); // Synchronize buffer swaps with vertical refresh rate GLint swapInt = 1; [_nsglContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; UnlockAGLCntx(); return 0; } int VideoRenderNSOpenGL::setRenderTargetWindow() { LockAGLCntx(); WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, "%s:%d", __FUNCTION__, __LINE__); GLuint attribs[] = { NSOpenGLPFAColorSize, 24, NSOpenGLPFAAlphaSize, 8, NSOpenGLPFADepthSize, 16, NSOpenGLPFAAccelerated, 0 }; NSOpenGLPixelFormat* fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes: (NSOpenGLPixelFormatAttribute*) attribs]; if(_windowRef) { [_windowRef initCocoaRenderView:fmt]; } else { UnlockAGLCntx(); return -1; } [fmt release]; _nsglContext = [_windowRef nsOpenGLContext]; [_nsglContext makeCurrentContext]; glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, "setRenderTargetWindow glClearColor 0.5"); DisplayBuffers(); UnlockAGLCntx(); return 0; } int VideoRenderNSOpenGL::setRenderTargetFullScreen() { LockAGLCntx(); WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, _id, "%s:%d", __FUNCTION__, __LINE__); GLuint attribs[] = { NSOpenGLPFAColorSize, 24, NSOpenGLPFAAlphaSize, 8, NSOpenGLPFADepthSize, 16, NSOpenGLPFAAccelerated, 0 }; NSOpenGLPixelFormat* fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes: (NSOpenGLPixelFormatAttribute*) attribs]; // create new fullscreen window NSRect screenRect = [[NSScreen mainScreen]frame]; [_windowRef setFrame:screenRect]; [_windowRef setBounds:screenRect]; _fullScreenWindow = [[CocoaFullScreenWindow alloc]init]; [_fullScreenWindow grabFullScreen]; [[[_fullScreenWindow window] contentView] addSubview:_windowRef]; if(_windowRef) { [_windowRef initCocoaRenderViewFullScreen:fmt]; } else { UnlockAGLCntx(); return -1; } [fmt release]; _nsglContext = [_windowRef nsOpenGLContext]; [_nsglContext makeCurrentContext]; glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); DisplayBuffers(); UnlockAGLCntx(); return 0; } VideoRenderNSOpenGL::~VideoRenderNSOpenGL() { WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer, 0, "%s:%d", __FUNCTION__, __LINE__); if(_fullScreen) { if(_fullScreenWindow) { WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, 0, "%s:%d Attempting to release fullscreen window", __FUNCTION__, __LINE__); [_fullScreenWindow releaseFullScreen]; } } // Signal event to exit thread, then delete it ThreadWrapper* tmpPtr = _screenUpdateThread; _screenUpdateThread = NULL; if (tmpPtr) { tmpPtr->SetNotAlive(); _screenUpdateEvent->Set(); _screenUpdateEvent->StopTimer(); if (tmpPtr->Stop()) { delete tmpPtr; } delete _screenUpdateEvent; _screenUpdateEvent = NULL; } if (_nsglContext != 0) { [_nsglContext makeCurrentContext]; _nsglContext = nil; } // Delete all channels std::map::iterator it = _nsglChannels.begin(); while (it!= _nsglChannels.end()) { delete it->second; _nsglChannels.erase(it); it = _nsglChannels.begin(); } _nsglChannels.clear(); // Clean the zOrder map std::multimap::iterator zIt = _zOrderToChannel.begin(); while(zIt != _zOrderToChannel.end()) { _zOrderToChannel.erase(zIt); zIt = _zOrderToChannel.begin(); } _zOrderToChannel.clear(); } /* static */ int VideoRenderNSOpenGL::GetOpenGLVersion(int& /*nsglMajor*/, int& /*nsglMinor*/) { return -1; } int VideoRenderNSOpenGL::Init() { LockAGLCntx(); if (!_screenUpdateThread) { UnlockAGLCntx(); return -1; } _screenUpdateThread->Start(_threadID); // Start the event triggering the render process unsigned int monitorFreq = 60; _screenUpdateEvent->StartTimer(true, 1000/monitorFreq); if (CreateMixingContext() == -1) { UnlockAGLCntx(); return -1; } UnlockAGLCntx(); return 0; } VideoChannelNSOpenGL* VideoRenderNSOpenGL::CreateNSGLChannel(int channel, int zOrder, float startWidth, float startHeight, float stopWidth, float stopHeight) { CriticalSectionScoped cs(_nsglContextCritSec); if (HasChannel(channel)) { return NULL; } if (_zOrderToChannel.find(zOrder) != _zOrderToChannel.end()) { } VideoChannelNSOpenGL* newAGLChannel = new VideoChannelNSOpenGL(_nsglContext, _id, this); if (newAGLChannel->SetStreamSettings(0, startWidth, startHeight, stopWidth, stopHeight) == -1) { if (newAGLChannel) { delete newAGLChannel; newAGLChannel = NULL; } return NULL; } _nsglChannels[channel] = newAGLChannel; _zOrderToChannel.insert(std::pair(zOrder, channel)); WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s successfully created NSGL channel number %d", __FUNCTION__, channel); return newAGLChannel; } int VideoRenderNSOpenGL::DeleteAllNSGLChannels() { CriticalSectionScoped cs(_nsglContextCritSec); std::map::iterator it; it = _nsglChannels.begin(); while (it != _nsglChannels.end()) { VideoChannelNSOpenGL* channel = it->second; WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s Deleting channel %d", __FUNCTION__, channel); delete channel; it++; } _nsglChannels.clear(); return 0; } WebRtc_Word32 VideoRenderNSOpenGL::DeleteNSGLChannel(const WebRtc_UWord32 channel) { CriticalSectionScoped cs(_nsglContextCritSec); std::map::iterator it; it = _nsglChannels.find(channel); if (it != _nsglChannels.end()) { delete it->second; _nsglChannels.erase(it); } else { return -1; } std::multimap::iterator zIt = _zOrderToChannel.begin(); while( zIt != _zOrderToChannel.end()) { if (zIt->second == (int)channel) { _zOrderToChannel.erase(zIt); break; } zIt++; } return 0; } WebRtc_Word32 VideoRenderNSOpenGL::GetChannelProperties(const WebRtc_UWord16 streamId, WebRtc_UWord32& zOrder, float& left, float& top, float& right, float& bottom) { CriticalSectionScoped cs(_nsglContextCritSec); bool channelFound = false; // Loop through all channels until we find a match. // From that, get zorder. // From that, get T, L, R, B for (std::multimap::reverse_iterator rIt = _zOrderToChannel.rbegin(); rIt != _zOrderToChannel.rend(); rIt++) { if(streamId == rIt->second) { channelFound = true; zOrder = rIt->second; std::map::iterator rIt = _nsglChannels.find(streamId); VideoChannelNSOpenGL* tempChannel = rIt->second; if(-1 == tempChannel->GetChannelProperties(left, top, right, bottom) ) { return -1; } break; } } if(false == channelFound) { return -1; } return 0; } int VideoRenderNSOpenGL::StopThread() { ThreadWrapper* tmpPtr = _screenUpdateThread; WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s Stopping thread ", __FUNCTION__, _screenUpdateThread); _screenUpdateThread = NULL; if (tmpPtr) { tmpPtr->SetNotAlive(); _screenUpdateEvent->Set(); if (tmpPtr->Stop()) { delete tmpPtr; } } delete _screenUpdateEvent; _screenUpdateEvent = NULL; return 0; } bool VideoRenderNSOpenGL::IsFullScreen() { CriticalSectionScoped cs(_nsglContextCritSec); return _fullScreen; } bool VideoRenderNSOpenGL::HasChannels() { CriticalSectionScoped cs(_nsglContextCritSec); if (_nsglChannels.begin() != _nsglChannels.end()) { return true; } return false; } bool VideoRenderNSOpenGL::HasChannel(int channel) { CriticalSectionScoped cs(_nsglContextCritSec); std::map::iterator it = _nsglChannels.find(channel); if (it != _nsglChannels.end()) { return true; } return false; } int VideoRenderNSOpenGL::GetChannels(std::list& channelList) { CriticalSectionScoped cs(_nsglContextCritSec); std::map::iterator it = _nsglChannels.begin(); while (it != _nsglChannels.end()) { channelList.push_back(it->first); it++; } return 0; } VideoChannelNSOpenGL* VideoRenderNSOpenGL::ConfigureNSGLChannel(int channel, int zOrder, float startWidth, float startHeight, float stopWidth, float stopHeight) { CriticalSectionScoped cs(_nsglContextCritSec); std::map::iterator it = _nsglChannels.find(channel); if (it != _nsglChannels.end()) { VideoChannelNSOpenGL* aglChannel = it->second; if (aglChannel->SetStreamSettings(0, startWidth, startHeight, stopWidth, stopHeight) == -1) { WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s failed to set stream settings: channel %d. channel=%d zOrder=%d startWidth=%d startHeight=%d stopWidth=%d stopHeight=%d", __FUNCTION__, channel, zOrder, startWidth, startHeight, stopWidth, stopHeight); return NULL; } WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s Configuring channel %d. channel=%d zOrder=%d startWidth=%d startHeight=%d stopWidth=%d stopHeight=%d", __FUNCTION__, channel, zOrder, startWidth, startHeight, stopWidth, stopHeight); 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; } /* * * Rendering process * */ bool VideoRenderNSOpenGL::ScreenUpdateThreadProc(void* obj) { return static_cast(obj)->ScreenUpdateProcess(); } bool VideoRenderNSOpenGL::ScreenUpdateProcess() { _screenUpdateEvent->Wait(10); LockAGLCntx(); if (!_screenUpdateThread) { WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, "%s no screen update thread", __FUNCTION__); UnlockAGLCntx(); return false; } [_nsglContext makeCurrentContext]; if (GetWindowRect(_windowRect) == -1) { UnlockAGLCntx(); return true; } if (_windowWidth != (_windowRect.right - _windowRect.left) || _windowHeight != (_windowRect.bottom - _windowRect.top)) { _windowWidth = _windowRect.right - _windowRect.left; _windowHeight = _windowRect.bottom - _windowRect.top; glViewport(0, 0, _windowWidth, _windowHeight); } // Check if there are any updated buffers bool updated = false; std::map::iterator it = _nsglChannels.begin(); while (it != _nsglChannels.end()) { VideoChannelNSOpenGL* 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) { UnlockAGLCntx(); return true; } } // } UnlockAGLCntx(); return true; } /* * * Functions for creating mixing buffers and screen settings * */ int VideoRenderNSOpenGL::CreateMixingContext() { CriticalSectionScoped cs(_nsglContextCritSec); if(_fullScreen) { if(-1 == setRenderTargetFullScreen()) { return -1; } } else { if(-1 == setRenderTargetWindow()) { return -1; } } configureNSOpenGLEngine(); DisplayBuffers(); GLenum glErr = glGetError(); if (glErr) { } return 0; } /* * * Rendering functions * */ int VideoRenderNSOpenGL::RenderOffScreenBuffers() { LockAGLCntx(); // Get the current window size, it might have changed since last render. if (GetWindowRect(_windowRect) == -1) { UnlockAGLCntx(); return -1; } [_nsglContext makeCurrentContext]; 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 = _nsglChannels.find(channelId); VideoChannelNSOpenGL* aglChannel = it->second; aglChannel->RenderOffScreenBuffer(); } DisplayBuffers(); UnlockAGLCntx(); return 0; } /* * * Help functions * * All help functions assumes external protections * */ int VideoRenderNSOpenGL::DisplayBuffers() { LockAGLCntx(); glFinish(); [_nsglContext flushBuffer]; WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s glFinish and [_nsglContext flushBuffer]", __FUNCTION__); UnlockAGLCntx(); return 0; } int VideoRenderNSOpenGL::GetWindowRect(Rect& rect) { CriticalSectionScoped cs(_nsglContextCritSec); if (_windowRef) { if(_fullScreen) { NSRect mainDisplayRect = [[NSScreen mainScreen] frame]; rect.bottom = 0; rect.left = 0; rect.right = mainDisplayRect.size.width; rect.top = mainDisplayRect.size.height; } else { rect.top = [_windowRef frame].origin.y; rect.left = [_windowRef frame].origin.x; rect.bottom = [_windowRef frame].origin.y + [_windowRef frame].size.height; rect.right = [_windowRef frame].origin.x + [_windowRef frame].size.width; } return 0; } else { return -1; } } WebRtc_Word32 VideoRenderNSOpenGL::ChangeUniqueID(WebRtc_Word32 id) { CriticalSectionScoped cs(_nsglContextCritSec); _id = id; return 0; } WebRtc_Word32 VideoRenderNSOpenGL::SetText(const WebRtc_UWord8 /*textId*/, const WebRtc_UWord8* /*text*/, const WebRtc_Word32 /*textLength*/, const WebRtc_UWord32 /*textColorRef*/, const WebRtc_UWord32 /*backgroundColorRef*/, const float /*left*/, const float /*top*/, const float /*right*/, const float /*bottom*/) { return 0; } void VideoRenderNSOpenGL::LockAGLCntx() { _nsglContextCritSec.Enter(); } void VideoRenderNSOpenGL::UnlockAGLCntx() { _nsglContextCritSec.Leave(); } /* bool VideoRenderNSOpenGL::SetFullScreen(bool fullscreen) { NSRect mainDisplayRect, viewRect; // Create a screen-sized window on the display you want to take over // Note, mainDisplayRect has a non-zero origin if the key window is on a secondary display mainDisplayRect = [[NSScreen mainScreen] frame]; fullScreenWindow = [[NSWindow alloc] initWithContentRect:mainDisplayRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES]; // Set the window level to be above the menu bar [fullScreenWindow setLevel:NSMainMenuWindowLevel+1]; // Perform any other window configuration you desire [fullScreenWindow setOpaque:YES]; [fullScreenWindow setHidesOnDeactivate:YES]; // Create a view with a double-buffered OpenGL context and attach it to the window // By specifying the non-fullscreen context as the shareContext, we automatically inherit the OpenGL objects (textures, etc) it has defined viewRect = NSMakeRect(0.0, 0.0, mainDisplayRect.size.width, mainDisplayRect.size.height); fullScreenView = [[MyOpenGLView alloc] initWithFrame:viewRect shareContext:[openGLView openGLContext]]; [fullScreenWindow setContentView:fullScreenView]; // Show the window [fullScreenWindow makeKeyAndOrderFront:self]; // Set the scene with the full-screen viewport and viewing transformation [scene setViewportRect:viewRect]; // Assign the view's MainController to self [fullScreenView setMainController:self]; if (!isAnimating) { // Mark the view as needing drawing to initalize its contents [fullScreenView setNeedsDisplay:YES]; } else { // Start playing the animation [fullScreenView startAnimation]; } } */ } //namespace webrtc #endif // COCOA_RENDERING