1/* 2 * Copyright 2017 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#import "RTCMTLNSVideoView.h" 12 13#import <Metal/Metal.h> 14#import <MetalKit/MetalKit.h> 15 16#import "base/RTCVideoFrame.h" 17 18#import "RTCMTLI420Renderer.h" 19 20@interface RTC_OBJC_TYPE (RTCMTLNSVideoView) 21()<MTKViewDelegate> @property(nonatomic) id<RTCMTLRenderer> renderer; 22@property(nonatomic, strong) MTKView *metalView; 23@property(atomic, strong) RTC_OBJC_TYPE(RTCVideoFrame) * videoFrame; 24@end 25 26@implementation RTC_OBJC_TYPE (RTCMTLNSVideoView) { 27 id<RTCMTLRenderer> _renderer; 28} 29 30@synthesize delegate = _delegate; 31@synthesize renderer = _renderer; 32@synthesize metalView = _metalView; 33@synthesize videoFrame = _videoFrame; 34 35- (instancetype)initWithFrame:(CGRect)frameRect { 36 self = [super initWithFrame:frameRect]; 37 if (self) { 38 [self configure]; 39 } 40 return self; 41} 42 43- (instancetype)initWithCoder:(NSCoder *)aCoder { 44 self = [super initWithCoder:aCoder]; 45 if (self) { 46 [self configure]; 47 } 48 return self; 49} 50 51#pragma mark - Private 52 53+ (BOOL)isMetalAvailable { 54 return [MTLCopyAllDevices() count] > 0; 55} 56 57- (void)configure { 58 if ([[self class] isMetalAvailable]) { 59 _metalView = [[MTKView alloc] initWithFrame:self.bounds]; 60 [self addSubview:_metalView]; 61 _metalView.layerContentsPlacement = NSViewLayerContentsPlacementScaleProportionallyToFit; 62 _metalView.translatesAutoresizingMaskIntoConstraints = NO; 63 _metalView.framebufferOnly = YES; 64 _metalView.delegate = self; 65 66 _renderer = [[RTCMTLI420Renderer alloc] init]; 67 if (![(RTCMTLI420Renderer *)_renderer addRenderingDestination:_metalView]) { 68 _renderer = nil; 69 }; 70 } 71} 72 73- (void)updateConstraints { 74 NSDictionary *views = NSDictionaryOfVariableBindings(_metalView); 75 76 NSArray *constraintsHorizontal = 77 [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[_metalView]-0-|" 78 options:0 79 metrics:nil 80 views:views]; 81 [self addConstraints:constraintsHorizontal]; 82 83 NSArray *constraintsVertical = 84 [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[_metalView]-0-|" 85 options:0 86 metrics:nil 87 views:views]; 88 [self addConstraints:constraintsVertical]; 89 [super updateConstraints]; 90} 91 92#pragma mark - MTKViewDelegate methods 93- (void)drawInMTKView:(nonnull MTKView *)view { 94 if (self.videoFrame == nil) { 95 return; 96 } 97 if (view == self.metalView) { 98 [_renderer drawFrame:self.videoFrame]; 99 } 100} 101 102- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size { 103} 104 105#pragma mark - RTC_OBJC_TYPE(RTCVideoRenderer) 106 107- (void)setSize:(CGSize)size { 108 _metalView.drawableSize = size; 109 dispatch_async(dispatch_get_main_queue(), ^{ 110 [self.delegate videoView:self didChangeVideoSize:size]; 111 }); 112 [_metalView draw]; 113} 114 115- (void)renderFrame:(nullable RTC_OBJC_TYPE(RTCVideoFrame) *)frame { 116 if (frame == nil) { 117 return; 118 } 119 self.videoFrame = [frame newI420VideoFrame]; 120} 121 122@end 123