blob: 745709581e123a9bb110b3d1aac4919d823f41f6 [file] [log] [blame]
/*
* 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 = PlatformThread::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
PlatformThread* 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()
{
PlatformThread* 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