1/* 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11#include "webrtc/engine_configurations.h" 12#if defined(COCOA_RENDERING) 13 14#include "webrtc/base/platform_thread.h" 15#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h" 16#include "webrtc/modules/video_render/mac/video_render_nsopengl.h" 17#include "webrtc/system_wrappers/include/critical_section_wrapper.h" 18#include "webrtc/system_wrappers/include/event_wrapper.h" 19#include "webrtc/system_wrappers/include/trace.h" 20 21namespace webrtc { 22 23VideoChannelNSOpenGL::VideoChannelNSOpenGL(NSOpenGLContext *nsglContext, int iId, VideoRenderNSOpenGL* owner) : 24_nsglContext( nsglContext), 25_id( iId), 26_owner( owner), 27_width( 0), 28_height( 0), 29_startWidth( 0.0f), 30_startHeight( 0.0f), 31_stopWidth( 0.0f), 32_stopHeight( 0.0f), 33_stretchedWidth( 0), 34_stretchedHeight( 0), 35_oldStretchedHeight( 0), 36_oldStretchedWidth( 0), 37_buffer( 0), 38_bufferSize( 0), 39_incomingBufferSize( 0), 40_bufferIsUpdated( false), 41_numberOfStreams( 0), 42_pixelFormat( GL_RGBA), 43_pixelDataType( GL_UNSIGNED_INT_8_8_8_8), 44_texture( 0) 45{ 46 47} 48 49VideoChannelNSOpenGL::~VideoChannelNSOpenGL() 50{ 51 if (_buffer) 52 { 53 delete [] _buffer; 54 _buffer = NULL; 55 } 56 57 if (_texture != 0) 58 { 59 [_nsglContext makeCurrentContext]; 60 glDeleteTextures(1, (const GLuint*) &_texture); 61 _texture = 0; 62 } 63} 64 65int VideoChannelNSOpenGL::ChangeContext(NSOpenGLContext *nsglContext) 66{ 67 _owner->LockAGLCntx(); 68 69 _nsglContext = nsglContext; 70 [_nsglContext makeCurrentContext]; 71 72 _owner->UnlockAGLCntx(); 73 return 0; 74 75} 76 77int32_t VideoChannelNSOpenGL::GetChannelProperties(float& left, float& top, 78 float& right, float& bottom) 79{ 80 81 _owner->LockAGLCntx(); 82 83 left = _startWidth; 84 top = _startHeight; 85 right = _stopWidth; 86 bottom = _stopHeight; 87 88 _owner->UnlockAGLCntx(); 89 return 0; 90} 91 92int32_t VideoChannelNSOpenGL::RenderFrame(const uint32_t /*streamId*/, 93 const VideoFrame& videoFrame) { 94 _owner->LockAGLCntx(); 95 96 if(_width != videoFrame.width() || 97 _height != videoFrame.height()) { 98 if(FrameSizeChange(videoFrame.width(), videoFrame.height(), 1) == -1) { 99 _owner->UnlockAGLCntx(); 100 return -1; 101 } 102 } 103 int ret = DeliverFrame(videoFrame); 104 105 _owner->UnlockAGLCntx(); 106 return ret; 107} 108 109int VideoChannelNSOpenGL::UpdateSize(int width, int height) 110{ 111 _owner->LockAGLCntx(); 112 _width = width; 113 _height = height; 114 _owner->UnlockAGLCntx(); 115 return 0; 116} 117 118int VideoChannelNSOpenGL::UpdateStretchSize(int stretchHeight, int stretchWidth) 119{ 120 121 _owner->LockAGLCntx(); 122 _stretchedHeight = stretchHeight; 123 _stretchedWidth = stretchWidth; 124 _owner->UnlockAGLCntx(); 125 return 0; 126} 127 128int VideoChannelNSOpenGL::FrameSizeChange(int width, int height, int numberOfStreams) 129{ 130 // We got a new frame size from VideoAPI, prepare the buffer 131 132 _owner->LockAGLCntx(); 133 134 if (width == _width && _height == height) 135 { 136 // We already have a correct buffer size 137 _numberOfStreams = numberOfStreams; 138 _owner->UnlockAGLCntx(); 139 return 0; 140 } 141 142 _width = width; 143 _height = height; 144 145 // Delete the old buffer, create a new one with correct size. 146 if (_buffer) 147 { 148 delete [] _buffer; 149 _bufferSize = 0; 150 } 151 152 _incomingBufferSize = CalcBufferSize(kI420, _width, _height); 153 _bufferSize = CalcBufferSize(kARGB, _width, _height); 154 _buffer = new unsigned char [_bufferSize]; 155 memset(_buffer, 0, _bufferSize * sizeof(unsigned char)); 156 157 [_nsglContext makeCurrentContext]; 158 159 if(glIsTexture(_texture)) 160 { 161 glDeleteTextures(1, (const GLuint*) &_texture); 162 _texture = 0; 163 } 164 165 // Create a new texture 166 glGenTextures(1, (GLuint *) &_texture); 167 168 GLenum glErr = glGetError(); 169 170 if (glErr != GL_NO_ERROR) 171 { 172 173 } 174 175 glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _texture); 176 177 GLint texSize; 178 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texSize); 179 180 if (texSize < _width || texSize < _height) 181 { 182 _owner->UnlockAGLCntx(); 183 return -1; 184 } 185 186 // Set up th texture type and size 187 glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, // target 188 0, // level 189 GL_RGBA, // internal format 190 _width, // width 191 _height, // height 192 0, // border 0/1 = off/on 193 _pixelFormat, // format, GL_RGBA 194 _pixelDataType, // data type, GL_UNSIGNED_INT_8_8_8_8 195 _buffer); // pixel data 196 197 glErr = glGetError(); 198 if (glErr != GL_NO_ERROR) 199 { 200 _owner->UnlockAGLCntx(); 201 return -1; 202 } 203 204 _owner->UnlockAGLCntx(); 205 return 0; 206} 207 208int VideoChannelNSOpenGL::DeliverFrame(const VideoFrame& videoFrame) { 209 _owner->LockAGLCntx(); 210 211 if (_texture == 0) { 212 _owner->UnlockAGLCntx(); 213 return 0; 214 } 215 216 if (CalcBufferSize(kI420, videoFrame.width(), videoFrame.height()) != 217 _incomingBufferSize) { 218 _owner->UnlockAGLCntx(); 219 return -1; 220 } 221 222 // Using the VideoFrame for YV12: YV12 is YVU; I420 assumes 223 // YUV. 224 // TODO(mikhal) : Use appropriate functionality. 225 // TODO(wu): See if we are using glTexSubImage2D correctly. 226 int rgbRet = ConvertFromYV12(videoFrame, kBGRA, 0, _buffer); 227 if (rgbRet < 0) { 228 _owner->UnlockAGLCntx(); 229 return -1; 230 } 231 232 [_nsglContext makeCurrentContext]; 233 234 // Make sure this texture is the active one 235 glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _texture); 236 GLenum glErr = glGetError(); 237 if (glErr != GL_NO_ERROR) { 238 WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, 239 "ERROR %d while calling glBindTexture", glErr); 240 _owner->UnlockAGLCntx(); 241 return -1; 242 } 243 244 glTexSubImage2D(GL_TEXTURE_RECTANGLE_EXT, 245 0, // Level, not use 246 0, // start point x, (low left of pic) 247 0, // start point y, 248 _width, // width 249 _height, // height 250 _pixelFormat, // pictue format for _buffer 251 _pixelDataType, // data type of _buffer 252 (const GLvoid*) _buffer); // the pixel data 253 254 glErr = glGetError(); 255 if (glErr != GL_NO_ERROR) { 256 WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, 257 "ERROR %d while calling glTexSubImage2d", glErr); 258 _owner->UnlockAGLCntx(); 259 return -1; 260 } 261 262 _bufferIsUpdated = true; 263 264 _owner->UnlockAGLCntx(); 265 return 0; 266} 267 268int VideoChannelNSOpenGL::RenderOffScreenBuffer() 269{ 270 271 _owner->LockAGLCntx(); 272 273 if (_texture == 0) 274 { 275 _owner->UnlockAGLCntx(); 276 return 0; 277 } 278 279 // if(_fullscreen) 280 // { 281 // NSRect mainDisplayRect = [[NSScreen mainScreen] frame]; 282 // _width = mainDisplayRect.size.width; 283 // _height = mainDisplayRect.size.height; 284 // glViewport(0, 0, mainDisplayRect.size.width, mainDisplayRect.size.height); 285 // float newX = mainDisplayRect.size.width/_width; 286 // float newY = mainDisplayRect.size.height/_height; 287 288 // convert from 0.0 <= size <= 1.0 to 289 // open gl world -1.0 < size < 1.0 290 GLfloat xStart = 2.0f * _startWidth - 1.0f; 291 GLfloat xStop = 2.0f * _stopWidth - 1.0f; 292 GLfloat yStart = 1.0f - 2.0f * _stopHeight; 293 GLfloat yStop = 1.0f - 2.0f * _startHeight; 294 295 [_nsglContext makeCurrentContext]; 296 297 glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _texture); 298 _oldStretchedHeight = _stretchedHeight; 299 _oldStretchedWidth = _stretchedWidth; 300 301 glLoadIdentity(); 302 glEnable(GL_TEXTURE_RECTANGLE_EXT); 303 glBegin(GL_POLYGON); 304 { 305 glTexCoord2f(0.0, 0.0); glVertex2f(xStart, yStop); 306 glTexCoord2f(_width, 0.0); glVertex2f(xStop, yStop); 307 glTexCoord2f(_width, _height); glVertex2f(xStop, yStart); 308 glTexCoord2f(0.0, _height); glVertex2f(xStart, yStart); 309 } 310 glEnd(); 311 312 glDisable(GL_TEXTURE_RECTANGLE_EXT); 313 314 _bufferIsUpdated = false; 315 316 _owner->UnlockAGLCntx(); 317 return 0; 318} 319 320int VideoChannelNSOpenGL::IsUpdated(bool& isUpdated) 321{ 322 _owner->LockAGLCntx(); 323 324 isUpdated = _bufferIsUpdated; 325 326 _owner->UnlockAGLCntx(); 327 return 0; 328} 329 330int VideoChannelNSOpenGL::SetStreamSettings(int /*streamId*/, float startWidth, float startHeight, float stopWidth, float stopHeight) 331{ 332 _owner->LockAGLCntx(); 333 334 _startWidth = startWidth; 335 _stopWidth = stopWidth; 336 _startHeight = startHeight; 337 _stopHeight = stopHeight; 338 339 int oldWidth = _width; 340 int oldHeight = _height; 341 int oldNumberOfStreams = _numberOfStreams; 342 343 _width = 0; 344 _height = 0; 345 346 int retVal = FrameSizeChange(oldWidth, oldHeight, oldNumberOfStreams); 347 348 _owner->UnlockAGLCntx(); 349 return retVal; 350} 351 352int VideoChannelNSOpenGL::SetStreamCropSettings(int /*streamId*/, float /*startWidth*/, float /*startHeight*/, float /*stopWidth*/, float /*stopHeight*/) 353{ 354 return -1; 355} 356 357/* 358 * 359 * VideoRenderNSOpenGL 360 * 361 */ 362 363VideoRenderNSOpenGL::VideoRenderNSOpenGL(CocoaRenderView *windowRef, bool fullScreen, int iId) : 364_windowRef( (CocoaRenderView*)windowRef), 365_fullScreen( fullScreen), 366_id( iId), 367_nsglContextCritSec( *CriticalSectionWrapper::CreateCriticalSection()), 368_screenUpdateEvent(EventTimerWrapper::Create()), 369_nsglContext( 0), 370_nsglFullScreenContext( 0), 371_fullScreenWindow( nil), 372_windowRect( ), 373_windowWidth( 0), 374_windowHeight( 0), 375_nsglChannels( ), 376_zOrderToChannel( ), 377_renderingIsPaused (FALSE), 378_windowRefSuperView(NULL), 379_windowRefSuperViewFrame(NSMakeRect(0,0,0,0)) 380{ 381 _screenUpdateThread.reset(new rtc::PlatformThread( 382 ScreenUpdateThreadProc, this, "ScreenUpdateNSOpenGL")); 383} 384 385int VideoRenderNSOpenGL::ChangeWindow(CocoaRenderView* newWindowRef) 386{ 387 388 LockAGLCntx(); 389 390 _windowRef = newWindowRef; 391 392 if(CreateMixingContext() == -1) 393 { 394 UnlockAGLCntx(); 395 return -1; 396 } 397 398 int error = 0; 399 std::map<int, VideoChannelNSOpenGL*>::iterator it = _nsglChannels.begin(); 400 while (it!= _nsglChannels.end()) 401 { 402 error |= (it->second)->ChangeContext(_nsglContext); 403 it++; 404 } 405 if(error != 0) 406 { 407 UnlockAGLCntx(); 408 return -1; 409 } 410 411 UnlockAGLCntx(); 412 return 0; 413} 414 415/* Check if the thread and event already exist. 416 * If so then they will simply be restarted 417 * If not then create them and continue 418 */ 419int32_t VideoRenderNSOpenGL::StartRender() 420{ 421 422 LockAGLCntx(); 423 424 const unsigned int MONITOR_FREQ = 60; 425 if(TRUE == _renderingIsPaused) 426 { 427 WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "Restarting screenUpdateThread"); 428 429 // we already have the thread. Most likely StopRender() was called and they were paused 430 _screenUpdateThread->Start(); 431 if (FALSE == 432 _screenUpdateEvent->StartTimer(true, 1000 / MONITOR_FREQ)) { 433 WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "Failed to restart screenUpdateThread or screenUpdateEvent"); 434 UnlockAGLCntx(); 435 return -1; 436 } 437 438 _screenUpdateThread->SetPriority(rtc::kRealtimePriority); 439 440 UnlockAGLCntx(); 441 return 0; 442 } 443 444 445 if (!_screenUpdateThread) 446 { 447 WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "failed start screenUpdateThread"); 448 UnlockAGLCntx(); 449 return -1; 450 } 451 452 453 UnlockAGLCntx(); 454 return 0; 455} 456int32_t VideoRenderNSOpenGL::StopRender() 457{ 458 459 LockAGLCntx(); 460 461 /* The code below is functional 462 * but it pauses for several seconds 463 */ 464 465 // pause the update thread and the event timer 466 if(!_screenUpdateThread || !_screenUpdateEvent) 467 { 468 _renderingIsPaused = TRUE; 469 470 UnlockAGLCntx(); 471 return 0; 472 } 473 474 _screenUpdateThread->Stop(); 475 if (FALSE == _screenUpdateEvent->StopTimer()) { 476 _renderingIsPaused = FALSE; 477 478 UnlockAGLCntx(); 479 return -1; 480 } 481 482 _renderingIsPaused = TRUE; 483 484 UnlockAGLCntx(); 485 return 0; 486} 487 488int VideoRenderNSOpenGL::configureNSOpenGLView() 489{ 490 return 0; 491 492} 493 494int VideoRenderNSOpenGL::configureNSOpenGLEngine() 495{ 496 497 LockAGLCntx(); 498 499 // Disable not needed functionality to increase performance 500 glDisable(GL_DITHER); 501 glDisable(GL_ALPHA_TEST); 502 glDisable(GL_STENCIL_TEST); 503 glDisable(GL_FOG); 504 glDisable(GL_TEXTURE_2D); 505 glPixelZoom(1.0, 1.0); 506 glDisable(GL_BLEND); 507 glDisable(GL_DEPTH_TEST); 508 glDepthMask(GL_FALSE); 509 glDisable(GL_CULL_FACE); 510 511 // Set texture parameters 512 glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_PRIORITY, 1.0); 513 glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 514 glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 515 glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 516 glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 517 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); 518 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 519 glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_SHARED_APPLE); 520 521 if (GetWindowRect(_windowRect) == -1) 522 { 523 UnlockAGLCntx(); 524 return true; 525 } 526 527 if (_windowWidth != (_windowRect.right - _windowRect.left) 528 || _windowHeight != (_windowRect.bottom - _windowRect.top)) 529 { 530 _windowWidth = _windowRect.right - _windowRect.left; 531 _windowHeight = _windowRect.bottom - _windowRect.top; 532 } 533 glViewport(0, 0, _windowWidth, _windowHeight); 534 535 // Synchronize buffer swaps with vertical refresh rate 536 GLint swapInt = 1; 537 [_nsglContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; 538 539 UnlockAGLCntx(); 540 return 0; 541} 542 543int VideoRenderNSOpenGL::setRenderTargetWindow() 544{ 545 LockAGLCntx(); 546 547 548 GLuint attribs[] = 549 { 550 NSOpenGLPFAColorSize, 24, 551 NSOpenGLPFAAlphaSize, 8, 552 NSOpenGLPFADepthSize, 16, 553 NSOpenGLPFAAccelerated, 554 0 555 }; 556 557 NSOpenGLPixelFormat* fmt = [[[NSOpenGLPixelFormat alloc] initWithAttributes: 558 (NSOpenGLPixelFormatAttribute*) attribs] autorelease]; 559 560 if(_windowRef) 561 { 562 [_windowRef initCocoaRenderView:fmt]; 563 } 564 else 565 { 566 UnlockAGLCntx(); 567 return -1; 568 } 569 570 _nsglContext = [_windowRef nsOpenGLContext]; 571 [_nsglContext makeCurrentContext]; 572 573 glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 574 glClear(GL_COLOR_BUFFER_BIT); 575 576 577 DisplayBuffers(); 578 579 UnlockAGLCntx(); 580 return 0; 581} 582 583int VideoRenderNSOpenGL::setRenderTargetFullScreen() 584{ 585 LockAGLCntx(); 586 587 588 GLuint attribs[] = 589 { 590 NSOpenGLPFAColorSize, 24, 591 NSOpenGLPFAAlphaSize, 8, 592 NSOpenGLPFADepthSize, 16, 593 NSOpenGLPFAAccelerated, 594 0 595 }; 596 597 NSOpenGLPixelFormat* fmt = [[[NSOpenGLPixelFormat alloc] initWithAttributes: 598 (NSOpenGLPixelFormatAttribute*) attribs] autorelease]; 599 600 // Store original superview and frame for use when exiting full screens 601 _windowRefSuperViewFrame = [_windowRef frame]; 602 _windowRefSuperView = [_windowRef superview]; 603 604 605 // create new fullscreen window 606 NSRect screenRect = [[NSScreen mainScreen]frame]; 607 [_windowRef setFrame:screenRect]; 608 [_windowRef setBounds:screenRect]; 609 610 611 _fullScreenWindow = [[CocoaFullScreenWindow alloc]init]; 612 [_fullScreenWindow grabFullScreen]; 613 [[[_fullScreenWindow window] contentView] addSubview:_windowRef]; 614 615 if(_windowRef) 616 { 617 [_windowRef initCocoaRenderViewFullScreen:fmt]; 618 } 619 else 620 { 621 UnlockAGLCntx(); 622 return -1; 623 } 624 625 _nsglContext = [_windowRef nsOpenGLContext]; 626 [_nsglContext makeCurrentContext]; 627 628 glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 629 glClear(GL_COLOR_BUFFER_BIT); 630 631 DisplayBuffers(); 632 633 UnlockAGLCntx(); 634 return 0; 635} 636 637VideoRenderNSOpenGL::~VideoRenderNSOpenGL() 638{ 639 640 if(_fullScreen) 641 { 642 if(_fullScreenWindow) 643 { 644 // Detach CocoaRenderView from full screen view back to 645 // it's original parent. 646 [_windowRef removeFromSuperview]; 647 if(_windowRefSuperView) 648 { 649 [_windowRefSuperView addSubview:_windowRef]; 650 [_windowRef setFrame:_windowRefSuperViewFrame]; 651 } 652 653 WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, 0, "%s:%d Attempting to release fullscreen window", __FUNCTION__, __LINE__); 654 [_fullScreenWindow releaseFullScreen]; 655 656 } 657 } 658 659 // Signal event to exit thread, then delete it 660 rtc::PlatformThread* tmpPtr = _screenUpdateThread.release(); 661 662 if (tmpPtr) 663 { 664 _screenUpdateEvent->Set(); 665 _screenUpdateEvent->StopTimer(); 666 667 tmpPtr->Stop(); 668 delete tmpPtr; 669 delete _screenUpdateEvent; 670 _screenUpdateEvent = NULL; 671 } 672 673 if (_nsglContext != 0) 674 { 675 [_nsglContext makeCurrentContext]; 676 _nsglContext = nil; 677 } 678 679 // Delete all channels 680 std::map<int, VideoChannelNSOpenGL*>::iterator it = _nsglChannels.begin(); 681 while (it!= _nsglChannels.end()) 682 { 683 delete it->second; 684 _nsglChannels.erase(it); 685 it = _nsglChannels.begin(); 686 } 687 _nsglChannels.clear(); 688 689 // Clean the zOrder map 690 std::multimap<int, int>::iterator zIt = _zOrderToChannel.begin(); 691 while(zIt != _zOrderToChannel.end()) 692 { 693 _zOrderToChannel.erase(zIt); 694 zIt = _zOrderToChannel.begin(); 695 } 696 _zOrderToChannel.clear(); 697 698} 699 700/* static */ 701int VideoRenderNSOpenGL::GetOpenGLVersion(int& /*nsglMajor*/, int& /*nsglMinor*/) 702{ 703 return -1; 704} 705 706int VideoRenderNSOpenGL::Init() 707{ 708 709 LockAGLCntx(); 710 if (!_screenUpdateThread) 711 { 712 UnlockAGLCntx(); 713 return -1; 714 } 715 716 _screenUpdateThread->Start(); 717 _screenUpdateThread->SetPriority(rtc::kRealtimePriority); 718 719 // Start the event triggering the render process 720 unsigned int monitorFreq = 60; 721 _screenUpdateEvent->StartTimer(true, 1000/monitorFreq); 722 723 if (CreateMixingContext() == -1) 724 { 725 UnlockAGLCntx(); 726 return -1; 727 } 728 729 UnlockAGLCntx(); 730 return 0; 731} 732 733VideoChannelNSOpenGL* VideoRenderNSOpenGL::CreateNSGLChannel(int channel, int zOrder, float startWidth, float startHeight, float stopWidth, float stopHeight) 734{ 735 CriticalSectionScoped cs(&_nsglContextCritSec); 736 737 if (HasChannel(channel)) 738 { 739 return NULL; 740 } 741 742 if (_zOrderToChannel.find(zOrder) != _zOrderToChannel.end()) 743 { 744 745 } 746 747 VideoChannelNSOpenGL* newAGLChannel = new VideoChannelNSOpenGL(_nsglContext, _id, this); 748 if (newAGLChannel->SetStreamSettings(0, startWidth, startHeight, stopWidth, stopHeight) == -1) 749 { 750 if (newAGLChannel) 751 { 752 delete newAGLChannel; 753 newAGLChannel = NULL; 754 } 755 756 return NULL; 757 } 758 759 _nsglChannels[channel] = newAGLChannel; 760 _zOrderToChannel.insert(std::pair<int, int>(zOrder, channel)); 761 762 WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s successfully created NSGL channel number %d", __FUNCTION__, channel); 763 764 return newAGLChannel; 765} 766 767int VideoRenderNSOpenGL::DeleteAllNSGLChannels() 768{ 769 770 CriticalSectionScoped cs(&_nsglContextCritSec); 771 772 std::map<int, VideoChannelNSOpenGL*>::iterator it; 773 it = _nsglChannels.begin(); 774 775 while (it != _nsglChannels.end()) 776 { 777 VideoChannelNSOpenGL* channel = it->second; 778 WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s Deleting channel %d", __FUNCTION__, channel); 779 delete channel; 780 it++; 781 } 782 _nsglChannels.clear(); 783 return 0; 784} 785 786int32_t VideoRenderNSOpenGL::DeleteNSGLChannel(const uint32_t channel) 787{ 788 789 CriticalSectionScoped cs(&_nsglContextCritSec); 790 791 std::map<int, VideoChannelNSOpenGL*>::iterator it; 792 it = _nsglChannels.find(channel); 793 if (it != _nsglChannels.end()) 794 { 795 delete it->second; 796 _nsglChannels.erase(it); 797 } 798 else 799 { 800 return -1; 801 } 802 803 std::multimap<int, int>::iterator zIt = _zOrderToChannel.begin(); 804 while( zIt != _zOrderToChannel.end()) 805 { 806 if (zIt->second == (int)channel) 807 { 808 _zOrderToChannel.erase(zIt); 809 break; 810 } 811 zIt++; 812 } 813 814 return 0; 815} 816 817int32_t VideoRenderNSOpenGL::GetChannelProperties(const uint16_t streamId, 818 uint32_t& zOrder, 819 float& left, 820 float& top, 821 float& right, 822 float& bottom) 823{ 824 825 CriticalSectionScoped cs(&_nsglContextCritSec); 826 827 bool channelFound = false; 828 829 // Loop through all channels until we find a match. 830 // From that, get zorder. 831 // From that, get T, L, R, B 832 for (std::multimap<int, int>::reverse_iterator rIt = _zOrderToChannel.rbegin(); 833 rIt != _zOrderToChannel.rend(); 834 rIt++) 835 { 836 if(streamId == rIt->second) 837 { 838 channelFound = true; 839 840 zOrder = rIt->second; 841 842 std::map<int, VideoChannelNSOpenGL*>::iterator rIt = _nsglChannels.find(streamId); 843 VideoChannelNSOpenGL* tempChannel = rIt->second; 844 845 if(-1 == tempChannel->GetChannelProperties(left, top, right, bottom) ) 846 { 847 return -1; 848 } 849 break; 850 } 851 } 852 853 if(false == channelFound) 854 { 855 856 return -1; 857 } 858 859 return 0; 860} 861 862int VideoRenderNSOpenGL::StopThread() 863{ 864 865 rtc::PlatformThread* tmpPtr = _screenUpdateThread.release(); 866 WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, 867 "%s Stopping thread ", __FUNCTION__, tmpPtr); 868 869 if (tmpPtr) 870 { 871 _screenUpdateEvent->Set(); 872 tmpPtr->Stop(); 873 delete tmpPtr; 874 } 875 876 delete _screenUpdateEvent; 877 _screenUpdateEvent = NULL; 878 879 return 0; 880} 881 882bool VideoRenderNSOpenGL::IsFullScreen() 883{ 884 885 CriticalSectionScoped cs(&_nsglContextCritSec); 886 return _fullScreen; 887} 888 889bool VideoRenderNSOpenGL::HasChannels() 890{ 891 CriticalSectionScoped cs(&_nsglContextCritSec); 892 893 if (_nsglChannels.begin() != _nsglChannels.end()) 894 { 895 return true; 896 } 897 return false; 898} 899 900bool VideoRenderNSOpenGL::HasChannel(int channel) 901{ 902 903 CriticalSectionScoped cs(&_nsglContextCritSec); 904 905 std::map<int, VideoChannelNSOpenGL*>::iterator it = _nsglChannels.find(channel); 906 907 if (it != _nsglChannels.end()) 908 { 909 return true; 910 } 911 return false; 912} 913 914int VideoRenderNSOpenGL::GetChannels(std::list<int>& channelList) 915{ 916 917 CriticalSectionScoped cs(&_nsglContextCritSec); 918 919 std::map<int, VideoChannelNSOpenGL*>::iterator it = _nsglChannels.begin(); 920 921 while (it != _nsglChannels.end()) 922 { 923 channelList.push_back(it->first); 924 it++; 925 } 926 927 return 0; 928} 929 930VideoChannelNSOpenGL* VideoRenderNSOpenGL::ConfigureNSGLChannel(int channel, int zOrder, float startWidth, float startHeight, float stopWidth, float stopHeight) 931{ 932 933 CriticalSectionScoped cs(&_nsglContextCritSec); 934 935 std::map<int, VideoChannelNSOpenGL*>::iterator it = _nsglChannels.find(channel); 936 937 if (it != _nsglChannels.end()) 938 { 939 VideoChannelNSOpenGL* aglChannel = it->second; 940 if (aglChannel->SetStreamSettings(0, startWidth, startHeight, stopWidth, stopHeight) == -1) 941 { 942 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", 943 __FUNCTION__, channel, zOrder, startWidth, startHeight, stopWidth, stopHeight); 944 return NULL; 945 } 946 WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s Configuring channel %d. channel=%d zOrder=%d startWidth=%d startHeight=%d stopWidth=%d stopHeight=%d", 947 __FUNCTION__, channel, zOrder, startWidth, startHeight, stopWidth, stopHeight); 948 949 std::multimap<int, int>::iterator it = _zOrderToChannel.begin(); 950 while(it != _zOrderToChannel.end()) 951 { 952 if (it->second == channel) 953 { 954 if (it->first != zOrder) 955 { 956 _zOrderToChannel.erase(it); 957 _zOrderToChannel.insert(std::pair<int, int>(zOrder, channel)); 958 } 959 break; 960 } 961 it++; 962 } 963 return aglChannel; 964 } 965 966 return NULL; 967} 968 969/* 970 * 971 * Rendering process 972 * 973 */ 974 975bool VideoRenderNSOpenGL::ScreenUpdateThreadProc(void* obj) 976{ 977 return static_cast<VideoRenderNSOpenGL*>(obj)->ScreenUpdateProcess(); 978} 979 980bool VideoRenderNSOpenGL::ScreenUpdateProcess() 981{ 982 983 _screenUpdateEvent->Wait(10); 984 LockAGLCntx(); 985 986 if (!_screenUpdateThread) 987 { 988 WEBRTC_TRACE(kTraceWarning, kTraceVideoRenderer, _id, "%s no screen update thread", __FUNCTION__); 989 UnlockAGLCntx(); 990 return false; 991 } 992 993 [_nsglContext makeCurrentContext]; 994 995 if (GetWindowRect(_windowRect) == -1) 996 { 997 UnlockAGLCntx(); 998 return true; 999 } 1000 1001 if (_windowWidth != (_windowRect.right - _windowRect.left) 1002 || _windowHeight != (_windowRect.bottom - _windowRect.top)) 1003 { 1004 _windowWidth = _windowRect.right - _windowRect.left; 1005 _windowHeight = _windowRect.bottom - _windowRect.top; 1006 glViewport(0, 0, _windowWidth, _windowHeight); 1007 } 1008 1009 // Check if there are any updated buffers 1010 bool updated = false; 1011 std::map<int, VideoChannelNSOpenGL*>::iterator it = _nsglChannels.begin(); 1012 while (it != _nsglChannels.end()) 1013 { 1014 1015 VideoChannelNSOpenGL* aglChannel = it->second; 1016 aglChannel->UpdateStretchSize(_windowHeight, _windowWidth); 1017 aglChannel->IsUpdated(updated); 1018 if (updated) 1019 { 1020 break; 1021 } 1022 it++; 1023 } 1024 1025 if (updated) 1026 { 1027 1028 // At least on buffers is updated, we need to repaint the texture 1029 if (RenderOffScreenBuffers() != -1) 1030 { 1031 UnlockAGLCntx(); 1032 return true; 1033 } 1034 } 1035 // } 1036 UnlockAGLCntx(); 1037 return true; 1038} 1039 1040/* 1041 * 1042 * Functions for creating mixing buffers and screen settings 1043 * 1044 */ 1045 1046int VideoRenderNSOpenGL::CreateMixingContext() 1047{ 1048 1049 CriticalSectionScoped cs(&_nsglContextCritSec); 1050 1051 if(_fullScreen) 1052 { 1053 if(-1 == setRenderTargetFullScreen()) 1054 { 1055 return -1; 1056 } 1057 } 1058 else 1059 { 1060 1061 if(-1 == setRenderTargetWindow()) 1062 { 1063 return -1; 1064 } 1065 } 1066 1067 configureNSOpenGLEngine(); 1068 1069 DisplayBuffers(); 1070 1071 GLenum glErr = glGetError(); 1072 if (glErr) 1073 { 1074 } 1075 1076 return 0; 1077} 1078 1079/* 1080 * 1081 * Rendering functions 1082 * 1083 */ 1084 1085int VideoRenderNSOpenGL::RenderOffScreenBuffers() 1086{ 1087 LockAGLCntx(); 1088 1089 // Get the current window size, it might have changed since last render. 1090 if (GetWindowRect(_windowRect) == -1) 1091 { 1092 UnlockAGLCntx(); 1093 return -1; 1094 } 1095 1096 [_nsglContext makeCurrentContext]; 1097 glClear(GL_COLOR_BUFFER_BIT); 1098 1099 // Loop through all channels starting highest zOrder ending with lowest. 1100 for (std::multimap<int, int>::reverse_iterator rIt = _zOrderToChannel.rbegin(); 1101 rIt != _zOrderToChannel.rend(); 1102 rIt++) 1103 { 1104 int channelId = rIt->second; 1105 std::map<int, VideoChannelNSOpenGL*>::iterator it = _nsglChannels.find(channelId); 1106 1107 VideoChannelNSOpenGL* aglChannel = it->second; 1108 1109 aglChannel->RenderOffScreenBuffer(); 1110 } 1111 1112 DisplayBuffers(); 1113 1114 UnlockAGLCntx(); 1115 return 0; 1116} 1117 1118/* 1119 * 1120 * Help functions 1121 * 1122 * All help functions assumes external protections 1123 * 1124 */ 1125 1126int VideoRenderNSOpenGL::DisplayBuffers() 1127{ 1128 1129 LockAGLCntx(); 1130 1131 glFinish(); 1132 [_nsglContext flushBuffer]; 1133 1134 WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s glFinish and [_nsglContext flushBuffer]", __FUNCTION__); 1135 1136 UnlockAGLCntx(); 1137 return 0; 1138} 1139 1140int VideoRenderNSOpenGL::GetWindowRect(Rect& rect) 1141{ 1142 1143 CriticalSectionScoped cs(&_nsglContextCritSec); 1144 1145 if (_windowRef) 1146 { 1147 if(_fullScreen) 1148 { 1149 NSRect mainDisplayRect = [[NSScreen mainScreen] frame]; 1150 rect.bottom = 0; 1151 rect.left = 0; 1152 rect.right = mainDisplayRect.size.width; 1153 rect.top = mainDisplayRect.size.height; 1154 } 1155 else 1156 { 1157 rect.top = [_windowRef frame].origin.y; 1158 rect.left = [_windowRef frame].origin.x; 1159 rect.bottom = [_windowRef frame].origin.y + [_windowRef frame].size.height; 1160 rect.right = [_windowRef frame].origin.x + [_windowRef frame].size.width; 1161 } 1162 1163 return 0; 1164 } 1165 else 1166 { 1167 return -1; 1168 } 1169} 1170 1171int32_t VideoRenderNSOpenGL::SetText(const uint8_t /*textId*/, 1172 const uint8_t* /*text*/, 1173 const int32_t /*textLength*/, 1174 const uint32_t /*textColorRef*/, 1175 const uint32_t /*backgroundColorRef*/, 1176 const float /*left*/, 1177 const float /*top*/, 1178 const float /*right*/, 1179 const float /*bottom*/) 1180{ 1181 1182 return 0; 1183 1184} 1185 1186void VideoRenderNSOpenGL::LockAGLCntx() 1187{ 1188 _nsglContextCritSec.Enter(); 1189} 1190void VideoRenderNSOpenGL::UnlockAGLCntx() 1191{ 1192 _nsglContextCritSec.Leave(); 1193} 1194 1195/* 1196 1197 bool VideoRenderNSOpenGL::SetFullScreen(bool fullscreen) 1198 { 1199 NSRect mainDisplayRect, viewRect; 1200 1201 // Create a screen-sized window on the display you want to take over 1202 // Note, mainDisplayRect has a non-zero origin if the key window is on a secondary display 1203 mainDisplayRect = [[NSScreen mainScreen] frame]; 1204 fullScreenWindow = [[NSWindow alloc] initWithContentRect:mainDisplayRect styleMask:NSBorderlessWindowMask 1205 backing:NSBackingStoreBuffered defer:YES]; 1206 1207 // Set the window level to be above the menu bar 1208 [fullScreenWindow setLevel:NSMainMenuWindowLevel+1]; 1209 1210 // Perform any other window configuration you desire 1211 [fullScreenWindow setOpaque:YES]; 1212 [fullScreenWindow setHidesOnDeactivate:YES]; 1213 1214 // Create a view with a double-buffered OpenGL context and attach it to the window 1215 // By specifying the non-fullscreen context as the shareContext, we automatically inherit the OpenGL objects (textures, etc) it has defined 1216 viewRect = NSMakeRect(0.0, 0.0, mainDisplayRect.size.width, mainDisplayRect.size.height); 1217 fullScreenView = [[MyOpenGLView alloc] initWithFrame:viewRect shareContext:[openGLView openGLContext]]; 1218 [fullScreenWindow setContentView:fullScreenView]; 1219 1220 // Show the window 1221 [fullScreenWindow makeKeyAndOrderFront:self]; 1222 1223 // Set the scene with the full-screen viewport and viewing transformation 1224 [scene setViewportRect:viewRect]; 1225 1226 // Assign the view's MainController to self 1227 [fullScreenView setMainController:self]; 1228 1229 if (!isAnimating) { 1230 // Mark the view as needing drawing to initalize its contents 1231 [fullScreenView setNeedsDisplay:YES]; 1232 } 1233 else { 1234 // Start playing the animation 1235 [fullScreenView startAnimation]; 1236 } 1237 1238 } 1239 1240 1241 1242 */ 1243 1244 1245} // namespace webrtc 1246 1247#endif // COCOA_RENDERING 1248