diff options
Diffstat (limited to 'chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCNSGLVideoView.m')
-rw-r--r-- | chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCNSGLVideoView.m | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCNSGLVideoView.m b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCNSGLVideoView.m new file mode 100644 index 00000000000..39f3678bfad --- /dev/null +++ b/chromium/third_party/libjingle/source/talk/app/webrtc/objc/RTCNSGLVideoView.m @@ -0,0 +1,187 @@ +/* + * libjingle + * Copyright 2014, Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "RTCNSGLVideoView.h" + +#import <CoreVideo/CVDisplayLink.h> +#import <OpenGL/gl3.h> +#import "RTCOpenGLVideoRenderer.h" +#import "RTCVideoRenderer.h" + +@interface RTCNSGLVideoView () <RTCVideoRendererDelegate> +// |i420Frame| is set when we receive a frame from a worker thread and is read +// from the display link callback so atomicity is required. +@property(atomic, strong) RTCI420Frame* i420Frame; +@property(atomic, strong) RTCOpenGLVideoRenderer* glRenderer; +- (void)drawFrame; +@end + +static CVReturn OnDisplayLinkFired(CVDisplayLinkRef displayLink, + const CVTimeStamp* now, + const CVTimeStamp* outputTime, + CVOptionFlags flagsIn, + CVOptionFlags* flagsOut, + void* displayLinkContext) { + RTCNSGLVideoView* view = (__bridge RTCNSGLVideoView*)displayLinkContext; + [view drawFrame]; + return kCVReturnSuccess; +} + +@implementation RTCNSGLVideoView { + CVDisplayLinkRef _displayLink; + RTCVideoRenderer* _videoRenderer; +} + +- (instancetype)initWithFrame:(NSRect)frame + pixelFormat:(NSOpenGLPixelFormat*)format { + if (self = [super initWithFrame:frame pixelFormat:format]) { + _videoRenderer = [[RTCVideoRenderer alloc] initWithDelegate:self]; + } + return self; +} + +- (void)dealloc { + [self teardownDisplayLink]; +} + +- (void)drawRect:(NSRect)rect { + [self drawFrame]; +} + +- (void)reshape { + [super reshape]; + NSRect frame = [self frame]; + CGLLockContext([[self openGLContext] CGLContextObj]); + glViewport(0, 0, frame.size.width, frame.size.height); + CGLUnlockContext([[self openGLContext] CGLContextObj]); +} + +- (void)lockFocus { + NSOpenGLContext* context = [self openGLContext]; + [super lockFocus]; + if ([context view] != self) { + [context setView:self]; + } + [context makeCurrentContext]; +} + +- (void)prepareOpenGL { + [super prepareOpenGL]; + if (!self.glRenderer) { + self.glRenderer = + [[RTCOpenGLVideoRenderer alloc] initWithContext:[self openGLContext]]; + } + [self.glRenderer setupGL]; + [self setupDisplayLink]; +} + +- (void)clearGLContext { + [self.glRenderer teardownGL]; + self.glRenderer = nil; + [super clearGLContext]; +} + +- (void)setVideoTrack:(RTCVideoTrack*)videoTrack { + if (_videoTrack == videoTrack) { + return; + } + if (_videoTrack) { + [_videoTrack removeRenderer:_videoRenderer]; + CVDisplayLinkStop(_displayLink); + } + _videoTrack = videoTrack; + if (_videoTrack) { + [_videoTrack addRenderer:_videoRenderer]; + CVDisplayLinkStart(_displayLink); + } +} + +#pragma mark - RTCVideoRendererDelegate + +// These methods are called when the video track has frame information to +// provide. This occurs on non-main thread. +- (void)renderer:(RTCVideoRenderer*)renderer + didSetSize:(CGSize)size { + dispatch_async(dispatch_get_main_queue(), ^{ + [self.delegate videoView:self didChangeVideoSize:size]; + }); +} + +- (void)renderer:(RTCVideoRenderer*)renderer + didReceiveFrame:(RTCI420Frame*)frame { + self.i420Frame = frame; +} + +#pragma mark - Private + +- (void)drawFrame { + RTCI420Frame* i420Frame = self.i420Frame; + if (i420Frame && self.glRenderer.lastDrawnFrame != i420Frame) { + // This method may be called from CVDisplayLink callback which isn't on the + // main thread so we have to lock the GL context before drawing. + CGLLockContext([[self openGLContext] CGLContextObj]); + [self.glRenderer drawFrame:i420Frame]; + CGLUnlockContext([[self openGLContext] CGLContextObj]); + } +} + +- (void)setupDisplayLink { + if (_displayLink) { + return; + } + // Synchronize buffer swaps with vertical refresh rate. + GLint swapInt = 1; + [[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]; + + // Create display link. + CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink); + CVDisplayLinkSetOutputCallback(_displayLink, + &OnDisplayLinkFired, + (__bridge void*)self); + // Set the display link for the current renderer. + CGLContextObj cglContext = [[self openGLContext] CGLContextObj]; + CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj]; + CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext( + _displayLink, cglContext, cglPixelFormat); + if (_videoTrack) { + CVDisplayLinkStart(_displayLink); + } +} + +- (void)teardownDisplayLink { + if (!_displayLink) { + return; + } + CVDisplayLinkRelease(_displayLink); + _displayLink = NULL; +} + +@end |