Index: webrtc/modules/video_render/mac/video_render_nsopengl.mm |
diff --git a/webrtc/modules/video_render/mac/video_render_nsopengl.mm b/webrtc/modules/video_render/mac/video_render_nsopengl.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b7683a96af4895b078de5d9b4323e15f4da3d37b |
--- /dev/null |
+++ b/webrtc/modules/video_render/mac/video_render_nsopengl.mm |
@@ -0,0 +1,1247 @@ |
+/* |
+ * 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/base/platform_thread.h" |
+#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" |
+#include "webrtc/modules/video_render/mac/video_render_nsopengl.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 { |
+ |
+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.reset(new rtc::PlatformThread( |
+ 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 |
+ _screenUpdateThread->Start(); |
+ if (FALSE == |
+ _screenUpdateEvent->StartTimer(true, 1000 / MONITOR_FREQ)) { |
+ WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "Failed to restart screenUpdateThread or screenUpdateEvent"); |
+ UnlockAGLCntx(); |
+ return -1; |
+ } |
+ |
+ _screenUpdateThread->SetPriority(rtc::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; |
+ } |
+ |
+ _screenUpdateThread->Stop(); |
+ if (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 |
+ rtc::PlatformThread* tmpPtr = _screenUpdateThread.release(); |
+ |
+ if (tmpPtr) |
+ { |
+ _screenUpdateEvent->Set(); |
+ _screenUpdateEvent->StopTimer(); |
+ |
+ 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(rtc::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() |
+{ |
+ |
+ rtc::PlatformThread* tmpPtr = _screenUpdateThread.release(); |
+ WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, |
+ "%s Stopping thread ", __FUNCTION__, tmpPtr); |
+ |
+ if (tmpPtr) |
+ { |
+ _screenUpdateEvent->Set(); |
+ 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 |