blob: 0e17777c4af5804744bdc0a40dc6978c23d1dd3b [file] [log] [blame]
denicija070d5e32017-02-26 11:44:13 -08001/*
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"
Anders Carlssone5960ce2017-06-22 15:26:30 +020018#import "WebRTC/RTCVideoFrameBuffer.h"
denicija070d5e32017-02-26 11:44:13 -080019
denicijad2088152017-04-28 02:14:54 -070020#import "RTCMTLI420Renderer.h"
denicija070d5e32017-02-26 11:44:13 -080021#import "RTCMTLNV12Renderer.h"
22
kthelgason954d9b92017-03-09 03:36:58 -080023// To avoid unreconized symbol linker errors, we're taking advantage of the objc runtime.
24// Linking errors occur when compiling for architectures that don't support Metal.
25#define MTKViewClass NSClassFromString(@"MTKView")
26#define RTCMTLNV12RendererClass NSClassFromString(@"RTCMTLNV12Renderer")
denicijad2088152017-04-28 02:14:54 -070027#define RTCMTLI420RendererClass NSClassFromString(@"RTCMTLI420Renderer")
kthelgason954d9b92017-03-09 03:36:58 -080028
denicija070d5e32017-02-26 11:44:13 -080029@interface RTCMTLVideoView () <MTKViewDelegate>
denicijad2088152017-04-28 02:14:54 -070030@property(nonatomic, strong) RTCMTLI420Renderer *rendererI420;
31@property(nonatomic, strong) RTCMTLNV12Renderer *rendererNV12;
denicija070d5e32017-02-26 11:44:13 -080032@property(nonatomic, strong) MTKView *metalView;
33@property(atomic, strong) RTCVideoFrame *videoFrame;
34@end
35
kthelgason954d9b92017-03-09 03:36:58 -080036@implementation RTCMTLVideoView
denicija070d5e32017-02-26 11:44:13 -080037
denicijad2088152017-04-28 02:14:54 -070038@synthesize rendererI420 = _rendererI420;
39@synthesize rendererNV12 = _rendererNV12;
denicija070d5e32017-02-26 11:44:13 -080040@synthesize metalView = _metalView;
41@synthesize videoFrame = _videoFrame;
42
43- (instancetype)initWithFrame:(CGRect)frameRect {
44 self = [super initWithFrame:frameRect];
45 if (self) {
46 [self configure];
47 }
48 return self;
49}
50
51- (instancetype)initWithCoder:(NSCoder *)aCoder {
52 self = [super initWithCoder:aCoder];
53 if (self) {
54 [self configure];
55 }
56 return self;
57}
58
59#pragma mark - Private
60
61+ (BOOL)isMetalAvailable {
kthelgasona2fb30c2017-03-09 03:34:27 -080062#if defined(RTC_SUPPORTS_METAL)
denicija070d5e32017-02-26 11:44:13 -080063 return YES;
kthelgasona2fb30c2017-03-09 03:34:27 -080064#else
denicija070d5e32017-02-26 11:44:13 -080065 return NO;
66#endif
67}
68
kthelgason954d9b92017-03-09 03:36:58 -080069+ (MTKView *)createMetalView:(CGRect)frame {
70 MTKView *view = [[MTKViewClass alloc] initWithFrame:frame];
71 return view;
72}
73
denicijad2088152017-04-28 02:14:54 -070074+ (RTCMTLNV12Renderer *)createNV12Renderer {
75 return [[RTCMTLNV12RendererClass alloc] init];
76}
77
78+ (RTCMTLI420Renderer *)createI420Renderer {
79 return [[RTCMTLI420RendererClass alloc] init];
kthelgason954d9b92017-03-09 03:36:58 -080080}
81
denicija070d5e32017-02-26 11:44:13 -080082- (void)configure {
denicijad2088152017-04-28 02:14:54 -070083 NSAssert([RTCMTLVideoView isMetalAvailable], @"Metal not availiable on this device");
kthelgason954d9b92017-03-09 03:36:58 -080084
85 _metalView = [RTCMTLVideoView createMetalView:self.bounds];
denicijad2088152017-04-28 02:14:54 -070086 [self configureMetalView];
kthelgason954d9b92017-03-09 03:36:58 -080087}
88
89- (void)configureMetalView {
90 if (_metalView) {
kthelgason96d91522017-03-08 06:33:52 -080091 _metalView.delegate = self;
kthelgason954d9b92017-03-09 03:36:58 -080092 [self addSubview:_metalView];
JT Teh4feb2042018-02-13 16:20:35 -080093 _metalView.contentMode = UIViewContentModeScaleAspectFill;
denicija070d5e32017-02-26 11:44:13 -080094 }
95}
kthelgason954d9b92017-03-09 03:36:58 -080096
Danielac4a14322017-10-31 11:19:38 +010097#pragma mark - Private
98
99- (void)layoutSubviews {
100 [super layoutSubviews];
101 _metalView.frame = self.bounds;
102}
103
denicija070d5e32017-02-26 11:44:13 -0800104#pragma mark - MTKViewDelegate methods
105
106- (void)drawInMTKView:(nonnull MTKView *)view {
107 NSAssert(view == self.metalView, @"Receiving draw callbacks from foreign instance.");
denicijad2088152017-04-28 02:14:54 -0700108 if (!self.videoFrame) {
109 return;
110 }
111
112 id<RTCMTLRenderer> renderer = nil;
Anders Carlssone5960ce2017-06-22 15:26:30 +0200113 if ([self.videoFrame.buffer isKindOfClass:[RTCCVPixelBuffer class]]) {
denicijad2088152017-04-28 02:14:54 -0700114 if (!self.rendererNV12) {
115 self.rendererNV12 = [RTCMTLVideoView createNV12Renderer];
116 if (![self.rendererNV12 addRenderingDestination:self.metalView]) {
117 self.rendererNV12 = nil;
118 RTCLogError(@"Failed to create NV12 renderer");
119 }
120 }
121 renderer = self.rendererNV12;
122 } else {
123 if (!self.rendererI420) {
124 self.rendererI420 = [RTCMTLVideoView createI420Renderer];
125 if (![self.rendererI420 addRenderingDestination:self.metalView]) {
126 self.rendererI420 = nil;
127 RTCLogError(@"Failed to create I420 renderer");
128 }
129 }
130 renderer = self.rendererI420;
131 }
132
133 [renderer drawFrame:self.videoFrame];
denicija070d5e32017-02-26 11:44:13 -0800134}
135
136- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size {
137}
138
139#pragma mark - RTCVideoRenderer
140
141- (void)setSize:(CGSize)size {
kthelgason954d9b92017-03-09 03:36:58 -0800142 self.metalView.drawableSize = size;
denicija070d5e32017-02-26 11:44:13 -0800143}
144
145- (void)renderFrame:(nullable RTCVideoFrame *)frame {
146 if (frame == nil) {
147 RTCLogInfo(@"Incoming frame is nil. Exiting render callback.");
148 return;
149 }
150 self.videoFrame = frame;
151}
152
153@end