| /* |
| * 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(COCOA_RENDERING) |
| |
| #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" |
| #include "webrtc/modules/video_render/mac/video_render_nsopengl.h" |
| #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" |
| #include "webrtc/system_wrappers/interface/event_wrapper.h" |
| #include "webrtc/system_wrappers/interface/thread_wrapper.h" |
| #include "webrtc/system_wrappers/interface/trace.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), |
| _buffer( 0), |
| _bufferSize( 0), |
| _incomingBufferSize( 0), |
| _bufferIsUpdated( false), |
| _numberOfStreams( 0), |
| _pixelFormat( GL_RGBA), |
| _pixelDataType( GL_UNSIGNED_INT_8_8_8_8), |
| _texture( 0) |
| { |
| |
| } |
| |
| 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->LockAGLCntx(); |
| |
| _nsglContext = nsglContext; |
| [_nsglContext makeCurrentContext]; |
| |
| _owner->UnlockAGLCntx(); |
| return 0; |
| |
| } |
| |
| int32_t VideoChannelNSOpenGL::GetChannelProperties(float& left, float& top, |
| float& right, float& bottom) |
| { |
| |
| _owner->LockAGLCntx(); |
| |
| left = _startWidth; |
| top = _startHeight; |
| right = _stopWidth; |
| bottom = _stopHeight; |
| |
| _owner->UnlockAGLCntx(); |
| return 0; |
| } |
| |
| int32_t VideoChannelNSOpenGL::RenderFrame(const uint32_t /*streamId*/, |
| const VideoFrame& videoFrame) { |
| _owner->LockAGLCntx(); |
| |
| if(_width != videoFrame.width() || |
| _height != videoFrame.height()) { |
| if(FrameSizeChange(videoFrame.width(), videoFrame.height(), 1) == -1) { |
| _owner->UnlockAGLCntx(); |
| return -1; |
| } |
| } |
| int ret = DeliverFrame(videoFrame); |
| |
| _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; |
| } |
| |
| _incomingBufferSize = CalcBufferSize(kI420, _width, _height); |
| _bufferSize = CalcBufferSize(kARGB, _width, _height); |
| _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(const VideoFrame& videoFrame) { |
| _owner->LockAGLCntx(); |
| |
| if (_texture == 0) { |
| _owner->UnlockAGLCntx(); |
| return 0; |
| } |
| |
| if (CalcBufferSize(kI420, videoFrame.width(), videoFrame.height()) != |
| _incomingBufferSize) { |
| _owner->UnlockAGLCntx(); |
| return -1; |
| } |
| |
| // Using the VideoFrame for YV12: YV12 is YVU; I420 assumes |
| // YUV. |
| // TODO(mikhal) : Use appropriate functionality. |
| // TODO(wu): See if we are using glTexSubImage2D correctly. |
| int rgbRet = ConvertFromYV12(videoFrame, kBGRA, 0, _buffer); |
| if (rgbRet < 0) { |
| _owner->UnlockAGLCntx(); |
| return -1; |
| } |
| |
| [_nsglContext makeCurrentContext]; |
| |
| // Make sure this texture is the active one |
| glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _texture); |
| 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()), |
| _screenUpdateEvent(EventTimerWrapper::Create()), |
| _nsglContext( 0), |
| _nsglFullScreenContext( 0), |
| _fullScreenWindow( nil), |
| _windowRect( ), |
| _windowWidth( 0), |
| _windowHeight( 0), |
| _nsglChannels( ), |
| _zOrderToChannel( ), |
| _renderingIsPaused (FALSE), |
| _windowRefSuperView(NULL), |
| _windowRefSuperViewFrame(NSMakeRect(0,0,0,0)) |
| { |
| _screenUpdateThread = ThreadWrapper::CreateThread(ScreenUpdateThreadProc, |
| this, "ScreenUpdateNSOpenGL"); |
| } |
| |
| int VideoRenderNSOpenGL::ChangeWindow(CocoaRenderView* newWindowRef) |
| { |
| |
| LockAGLCntx(); |
| |
| _windowRef = newWindowRef; |
| |
| if(CreateMixingContext() == -1) |
| { |
| UnlockAGLCntx(); |
| return -1; |
| } |
| |
| int error = 0; |
| std::map<int, VideoChannelNSOpenGL*>::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 |
| */ |
| int32_t 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() || |
| FALSE == _screenUpdateEvent->StartTimer(true, 1000/MONITOR_FREQ)) |
| { |
| WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "Failed to restart screenUpdateThread or screenUpdateEvent"); |
| UnlockAGLCntx(); |
| return -1; |
| } |
| |
| _screenUpdateThread->SetPriority(kRealtimePriority); |
| |
| UnlockAGLCntx(); |
| return 0; |
| } |
| |
| |
| if (!_screenUpdateThread) |
| { |
| WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "failed start screenUpdateThread"); |
| UnlockAGLCntx(); |
| return -1; |
| } |
| |
| |
| UnlockAGLCntx(); |
| return 0; |
| } |
| int32_t 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(); |
| |
| |
| GLuint attribs[] = |
| { |
| NSOpenGLPFAColorSize, 24, |
| NSOpenGLPFAAlphaSize, 8, |
| NSOpenGLPFADepthSize, 16, |
| NSOpenGLPFAAccelerated, |
| 0 |
| }; |
| |
| NSOpenGLPixelFormat* fmt = [[[NSOpenGLPixelFormat alloc] initWithAttributes: |
| (NSOpenGLPixelFormatAttribute*) attribs] autorelease]; |
| |
| if(_windowRef) |
| { |
| [_windowRef initCocoaRenderView:fmt]; |
| } |
| else |
| { |
| UnlockAGLCntx(); |
| return -1; |
| } |
| |
| _nsglContext = [_windowRef nsOpenGLContext]; |
| [_nsglContext makeCurrentContext]; |
| |
| glClearColor(0.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| |
| DisplayBuffers(); |
| |
| UnlockAGLCntx(); |
| return 0; |
| } |
| |
| int VideoRenderNSOpenGL::setRenderTargetFullScreen() |
| { |
| LockAGLCntx(); |
| |
| |
| GLuint attribs[] = |
| { |
| NSOpenGLPFAColorSize, 24, |
| NSOpenGLPFAAlphaSize, 8, |
| NSOpenGLPFADepthSize, 16, |
| NSOpenGLPFAAccelerated, |
| 0 |
| }; |
| |
| NSOpenGLPixelFormat* fmt = [[[NSOpenGLPixelFormat alloc] initWithAttributes: |
| (NSOpenGLPixelFormatAttribute*) attribs] autorelease]; |
| |
| // Store original superview and frame for use when exiting full screens |
| _windowRefSuperViewFrame = [_windowRef frame]; |
| _windowRefSuperView = [_windowRef superview]; |
| |
| |
| // 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; |
| } |
| |
| _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() |
| { |
| |
| if(_fullScreen) |
| { |
| if(_fullScreenWindow) |
| { |
| // Detach CocoaRenderView from full screen view back to |
| // it's original parent. |
| [_windowRef removeFromSuperview]; |
| if(_windowRefSuperView) |
| { |
| [_windowRefSuperView addSubview:_windowRef]; |
| [_windowRef setFrame:_windowRefSuperViewFrame]; |
| } |
| |
| 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.release(); |
| |
| if (tmpPtr) |
| { |
| _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<int, VideoChannelNSOpenGL*>::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<int, int>::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(); |
| _screenUpdateThread->SetPriority(kRealtimePriority); |
| |
| // 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<int, int>(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<int, VideoChannelNSOpenGL*>::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; |
| } |
| |
| int32_t VideoRenderNSOpenGL::DeleteNSGLChannel(const uint32_t channel) |
| { |
| |
| CriticalSectionScoped cs(&_nsglContextCritSec); |
| |
| std::map<int, VideoChannelNSOpenGL*>::iterator it; |
| it = _nsglChannels.find(channel); |
| if (it != _nsglChannels.end()) |
| { |
| delete it->second; |
| _nsglChannels.erase(it); |
| } |
| else |
| { |
| return -1; |
| } |
| |
| std::multimap<int, int>::iterator zIt = _zOrderToChannel.begin(); |
| while( zIt != _zOrderToChannel.end()) |
| { |
| if (zIt->second == (int)channel) |
| { |
| _zOrderToChannel.erase(zIt); |
| break; |
| } |
| zIt++; |
| } |
| |
| return 0; |
| } |
| |
| int32_t VideoRenderNSOpenGL::GetChannelProperties(const uint16_t streamId, |
| uint32_t& 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<int, int>::reverse_iterator rIt = _zOrderToChannel.rbegin(); |
| rIt != _zOrderToChannel.rend(); |
| rIt++) |
| { |
| if(streamId == rIt->second) |
| { |
| channelFound = true; |
| |
| zOrder = rIt->second; |
| |
| std::map<int, VideoChannelNSOpenGL*>::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.release(); |
| WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, |
| "%s Stopping thread ", __FUNCTION__, tmpPtr); |
| |
| if (tmpPtr) |
| { |
| _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<int, VideoChannelNSOpenGL*>::iterator it = _nsglChannels.find(channel); |
| |
| if (it != _nsglChannels.end()) |
| { |
| return true; |
| } |
| return false; |
| } |
| |
| int VideoRenderNSOpenGL::GetChannels(std::list<int>& channelList) |
| { |
| |
| CriticalSectionScoped cs(&_nsglContextCritSec); |
| |
| std::map<int, VideoChannelNSOpenGL*>::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<int, VideoChannelNSOpenGL*>::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<int, int>::iterator it = _zOrderToChannel.begin(); |
| while(it != _zOrderToChannel.end()) |
| { |
| if (it->second == channel) |
| { |
| if (it->first != zOrder) |
| { |
| _zOrderToChannel.erase(it); |
| _zOrderToChannel.insert(std::pair<int, int>(zOrder, channel)); |
| } |
| break; |
| } |
| it++; |
| } |
| return aglChannel; |
| } |
| |
| return NULL; |
| } |
| |
| /* |
| * |
| * Rendering process |
| * |
| */ |
| |
| bool VideoRenderNSOpenGL::ScreenUpdateThreadProc(void* obj) |
| { |
| return static_cast<VideoRenderNSOpenGL*>(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<int, VideoChannelNSOpenGL*>::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<int, int>::reverse_iterator rIt = _zOrderToChannel.rbegin(); |
| rIt != _zOrderToChannel.rend(); |
| rIt++) |
| { |
| int channelId = rIt->second; |
| std::map<int, VideoChannelNSOpenGL*>::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; |
| } |
| } |
| |
| int32_t VideoRenderNSOpenGL::SetText(const uint8_t /*textId*/, |
| const uint8_t* /*text*/, |
| const int32_t /*textLength*/, |
| const uint32_t /*textColorRef*/, |
| const uint32_t /*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 |