Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / libjingle / source / talk / app / webrtc / objc / RTCNSGLVideoView.m
1 /*
2  * libjingle
3  * Copyright 2014, Google Inc.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  *  1. Redistributions of source code must retain the above copyright notice,
9  *     this list of conditions and the following disclaimer.
10  *  2. Redistributions in binary form must reproduce the above copyright notice,
11  *     this list of conditions and the following disclaimer in the documentation
12  *     and/or other materials provided with the distribution.
13  *  3. The name of the author may not be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #if !defined(__has_feature) || !__has_feature(objc_arc)
29 #error "This file requires ARC support."
30 #endif
31
32 #import "RTCNSGLVideoView.h"
33
34 #import <CoreVideo/CVDisplayLink.h>
35 #import <OpenGL/gl3.h>
36 #import "RTCOpenGLVideoRenderer.h"
37 #import "RTCVideoRenderer.h"
38
39 @interface RTCNSGLVideoView () <RTCVideoRendererDelegate>
40 // |i420Frame| is set when we receive a frame from a worker thread and is read
41 // from the display link callback so atomicity is required.
42 @property(atomic, strong) RTCI420Frame* i420Frame;
43 @property(atomic, strong) RTCOpenGLVideoRenderer* glRenderer;
44 - (void)drawFrame;
45 @end
46
47 static CVReturn OnDisplayLinkFired(CVDisplayLinkRef displayLink,
48                                    const CVTimeStamp* now,
49                                    const CVTimeStamp* outputTime,
50                                    CVOptionFlags flagsIn,
51                                    CVOptionFlags* flagsOut,
52                                    void* displayLinkContext) {
53   RTCNSGLVideoView* view = (__bridge RTCNSGLVideoView*)displayLinkContext;
54   [view drawFrame];
55   return kCVReturnSuccess;
56 }
57
58 @implementation RTCNSGLVideoView {
59   CVDisplayLinkRef _displayLink;
60   RTCVideoRenderer* _videoRenderer;
61 }
62
63 - (instancetype)initWithFrame:(NSRect)frame
64                   pixelFormat:(NSOpenGLPixelFormat*)format {
65   if (self = [super initWithFrame:frame pixelFormat:format]) {
66     _videoRenderer = [[RTCVideoRenderer alloc] initWithDelegate:self];
67   }
68   return self;
69 }
70
71 - (void)dealloc {
72   [self teardownDisplayLink];
73 }
74
75 - (void)drawRect:(NSRect)rect {
76   [self drawFrame];
77 }
78
79 - (void)reshape {
80   [super reshape];
81   NSRect frame = [self frame];
82   CGLLockContext([[self openGLContext] CGLContextObj]);
83   glViewport(0, 0, frame.size.width, frame.size.height);
84   CGLUnlockContext([[self openGLContext] CGLContextObj]);
85 }
86
87 - (void)lockFocus {
88   NSOpenGLContext* context = [self openGLContext];
89   [super lockFocus];
90   if ([context view] != self) {
91     [context setView:self];
92   }
93   [context makeCurrentContext];
94 }
95
96 - (void)prepareOpenGL {
97   [super prepareOpenGL];
98   if (!self.glRenderer) {
99     self.glRenderer =
100         [[RTCOpenGLVideoRenderer alloc] initWithContext:[self openGLContext]];
101   }
102   [self.glRenderer setupGL];
103   [self setupDisplayLink];
104 }
105
106 - (void)clearGLContext {
107   [self.glRenderer teardownGL];
108   self.glRenderer = nil;
109   [super clearGLContext];
110 }
111
112 - (void)setVideoTrack:(RTCVideoTrack*)videoTrack {
113   if (_videoTrack == videoTrack) {
114     return;
115   }
116   if (_videoTrack) {
117     [_videoTrack removeRenderer:_videoRenderer];
118     CVDisplayLinkStop(_displayLink);
119     // Clear contents.
120     self.i420Frame = nil;
121     [self drawFrame];
122   }
123   _videoTrack = videoTrack;
124   if (_videoTrack) {
125     [_videoTrack addRenderer:_videoRenderer];
126     CVDisplayLinkStart(_displayLink);
127   }
128 }
129
130 #pragma mark - RTCVideoRendererDelegate
131
132 // These methods are called when the video track has frame information to
133 // provide. This occurs on non-main thread.
134 - (void)renderer:(RTCVideoRenderer*)renderer
135       didSetSize:(CGSize)size {
136   dispatch_async(dispatch_get_main_queue(), ^{
137     [self.delegate videoView:self didChangeVideoSize:size];
138   });
139 }
140
141 - (void)renderer:(RTCVideoRenderer*)renderer
142     didReceiveFrame:(RTCI420Frame*)frame {
143   self.i420Frame = frame;
144 }
145
146 #pragma mark - Private
147
148 - (void)drawFrame {
149   RTCI420Frame* i420Frame = self.i420Frame;
150   if (self.glRenderer.lastDrawnFrame != i420Frame) {
151     // This method may be called from CVDisplayLink callback which isn't on the
152     // main thread so we have to lock the GL context before drawing.
153     CGLLockContext([[self openGLContext] CGLContextObj]);
154     [self.glRenderer drawFrame:i420Frame];
155     CGLUnlockContext([[self openGLContext] CGLContextObj]);
156   }
157 }
158
159 - (void)setupDisplayLink {
160   if (_displayLink) {
161     return;
162   }
163   // Synchronize buffer swaps with vertical refresh rate.
164   GLint swapInt = 1;
165   [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
166
167   // Create display link.
168   CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
169   CVDisplayLinkSetOutputCallback(_displayLink,
170                                  &OnDisplayLinkFired,
171                                  (__bridge void*)self);
172   // Set the display link for the current renderer.
173   CGLContextObj cglContext = [[self openGLContext] CGLContextObj];
174   CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
175   CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(
176       _displayLink, cglContext, cglPixelFormat);
177   if (_videoTrack) {
178     CVDisplayLinkStart(_displayLink);
179   }
180 }
181
182 - (void)teardownDisplayLink {
183   if (!_displayLink) {
184     return;
185   }
186   CVDisplayLinkRelease(_displayLink);
187   _displayLink = NULL;
188 }
189
190 @end