blob: 96778c40954254871019cb8ee32c79101e6b67ca [file] [log] [blame]
denicija070d5e32017-02-26 19:44:131/*
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 "WebRTC/RTCMTLVideoView.h"
12
13#import <Metal/Metal.h>
14#import <MetalKit/MetalKit.h>
15
16#import "WebRTC/RTCLogging.h"
17#import "WebRTC/RTCVideoFrame.h"
18
denicijad2088152017-04-28 09:14:5419#import "RTCMTLI420Renderer.h"
denicija070d5e32017-02-26 19:44:1320#import "RTCMTLNV12Renderer.h"
21
kthelgason954d9b92017-03-09 11:36:5822// To avoid unreconized symbol linker errors, we're taking advantage of the objc runtime.
23// Linking errors occur when compiling for architectures that don't support Metal.
24#define MTKViewClass NSClassFromString(@"MTKView")
25#define RTCMTLNV12RendererClass NSClassFromString(@"RTCMTLNV12Renderer")
denicijad2088152017-04-28 09:14:5426#define RTCMTLI420RendererClass NSClassFromString(@"RTCMTLI420Renderer")
kthelgason954d9b92017-03-09 11:36:5827
denicija070d5e32017-02-26 19:44:1328@interface RTCMTLVideoView () <MTKViewDelegate>
denicijad2088152017-04-28 09:14:5429@property(nonatomic, strong) RTCMTLI420Renderer *rendererI420;
30@property(nonatomic, strong) RTCMTLNV12Renderer *rendererNV12;
denicija070d5e32017-02-26 19:44:1331@property(nonatomic, strong) MTKView *metalView;
32@property(atomic, strong) RTCVideoFrame *videoFrame;
33@end
34
kthelgason954d9b92017-03-09 11:36:5835@implementation RTCMTLVideoView
denicija070d5e32017-02-26 19:44:1336
denicijad2088152017-04-28 09:14:5437@synthesize rendererI420 = _rendererI420;
38@synthesize rendererNV12 = _rendererNV12;
denicija070d5e32017-02-26 19:44:1339@synthesize metalView = _metalView;
40@synthesize videoFrame = _videoFrame;
41
42- (instancetype)initWithFrame:(CGRect)frameRect {
43 self = [super initWithFrame:frameRect];
44 if (self) {
45 [self configure];
46 }
47 return self;
48}
49
50- (instancetype)initWithCoder:(NSCoder *)aCoder {
51 self = [super initWithCoder:aCoder];
52 if (self) {
53 [self configure];
54 }
55 return self;
56}
57
58#pragma mark - Private
59
60+ (BOOL)isMetalAvailable {
kthelgasona2fb30c2017-03-09 11:34:2761#if defined(RTC_SUPPORTS_METAL)
denicija070d5e32017-02-26 19:44:1362 return YES;
kthelgasona2fb30c2017-03-09 11:34:2763#else
denicija070d5e32017-02-26 19:44:1364 return NO;
65#endif
66}
67
kthelgason954d9b92017-03-09 11:36:5868+ (MTKView *)createMetalView:(CGRect)frame {
69 MTKView *view = [[MTKViewClass alloc] initWithFrame:frame];
70 return view;
71}
72
denicijad2088152017-04-28 09:14:5473+ (RTCMTLNV12Renderer *)createNV12Renderer {
74 return [[RTCMTLNV12RendererClass alloc] init];
75}
76
77+ (RTCMTLI420Renderer *)createI420Renderer {
78 return [[RTCMTLI420RendererClass alloc] init];
kthelgason954d9b92017-03-09 11:36:5879}
80
denicija070d5e32017-02-26 19:44:1381- (void)configure {
denicijad2088152017-04-28 09:14:5482 NSAssert([RTCMTLVideoView isMetalAvailable], @"Metal not availiable on this device");
kthelgason954d9b92017-03-09 11:36:5883
84 _metalView = [RTCMTLVideoView createMetalView:self.bounds];
denicijad2088152017-04-28 09:14:5485 [self configureMetalView];
kthelgason954d9b92017-03-09 11:36:5886}
87
88- (void)configureMetalView {
89 if (_metalView) {
kthelgason96d91522017-03-08 14:33:5290 _metalView.delegate = self;
kthelgason954d9b92017-03-09 11:36:5891 [self addSubview:_metalView];
denicija070d5e32017-02-26 19:44:1392 _metalView.contentMode = UIViewContentModeScaleAspectFit;
93 _metalView.translatesAutoresizingMaskIntoConstraints = NO;
94 UILayoutGuide *margins = self.layoutMarginsGuide;
95 [_metalView.topAnchor constraintEqualToAnchor:margins.topAnchor].active = YES;
96 [_metalView.bottomAnchor constraintEqualToAnchor:margins.bottomAnchor].active = YES;
97 [_metalView.leftAnchor constraintEqualToAnchor:margins.leftAnchor].active = YES;
98 [_metalView.rightAnchor constraintEqualToAnchor:margins.rightAnchor].active = YES;
denicija070d5e32017-02-26 19:44:1399 }
100}
kthelgason954d9b92017-03-09 11:36:58101
denicija070d5e32017-02-26 19:44:13102#pragma mark - MTKViewDelegate methods
103
104- (void)drawInMTKView:(nonnull MTKView *)view {
105 NSAssert(view == self.metalView, @"Receiving draw callbacks from foreign instance.");
denicijad2088152017-04-28 09:14:54106 if (!self.videoFrame) {
107 return;
108 }
109
110 id<RTCMTLRenderer> renderer = nil;
111 if (self.videoFrame.nativeHandle) {
112 if (!self.rendererNV12) {
113 self.rendererNV12 = [RTCMTLVideoView createNV12Renderer];
114 if (![self.rendererNV12 addRenderingDestination:self.metalView]) {
115 self.rendererNV12 = nil;
116 RTCLogError(@"Failed to create NV12 renderer");
117 }
118 }
119 renderer = self.rendererNV12;
120 } else {
121 if (!self.rendererI420) {
122 self.rendererI420 = [RTCMTLVideoView createI420Renderer];
123 if (![self.rendererI420 addRenderingDestination:self.metalView]) {
124 self.rendererI420 = nil;
125 RTCLogError(@"Failed to create I420 renderer");
126 }
127 }
128 renderer = self.rendererI420;
129 }
130
131 [renderer drawFrame:self.videoFrame];
denicija070d5e32017-02-26 19:44:13132}
133
134- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size {
135}
136
137#pragma mark - RTCVideoRenderer
138
139- (void)setSize:(CGSize)size {
kthelgason954d9b92017-03-09 11:36:58140 self.metalView.drawableSize = size;
denicija070d5e32017-02-26 19:44:13141}
142
143- (void)renderFrame:(nullable RTCVideoFrame *)frame {
144 if (frame == nil) {
145 RTCLogInfo(@"Incoming frame is nil. Exiting render callback.");
146 return;
147 }
148 self.videoFrame = frame;
149}
150
151@end