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