blob: 24dc29f7f3332857ff45b63ad7fc86f1a9438bbf [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
Kári Tristan Helgason4f7b6402018-04-16 11:39:53 +020036@implementation RTCMTLVideoView {
37 int64_t _lastFrameTimeNs;
38}
denicija070d5e32017-02-26 11:44:13 -080039
denicijad2088152017-04-28 02:14:54 -070040@synthesize rendererI420 = _rendererI420;
41@synthesize rendererNV12 = _rendererNV12;
denicija070d5e32017-02-26 11:44:13 -080042@synthesize metalView = _metalView;
43@synthesize videoFrame = _videoFrame;
44
45- (instancetype)initWithFrame:(CGRect)frameRect {
46 self = [super initWithFrame:frameRect];
47 if (self) {
48 [self configure];
49 }
50 return self;
51}
52
53- (instancetype)initWithCoder:(NSCoder *)aCoder {
54 self = [super initWithCoder:aCoder];
55 if (self) {
56 [self configure];
57 }
58 return self;
59}
60
61#pragma mark - Private
62
63+ (BOOL)isMetalAvailable {
kthelgasona2fb30c2017-03-09 03:34:27 -080064#if defined(RTC_SUPPORTS_METAL)
denicija070d5e32017-02-26 11:44:13 -080065 return YES;
kthelgasona2fb30c2017-03-09 03:34:27 -080066#else
denicija070d5e32017-02-26 11:44:13 -080067 return NO;
68#endif
69}
70
kthelgason954d9b92017-03-09 03:36:58 -080071+ (MTKView *)createMetalView:(CGRect)frame {
72 MTKView *view = [[MTKViewClass alloc] initWithFrame:frame];
73 return view;
74}
75
denicijad2088152017-04-28 02:14:54 -070076+ (RTCMTLNV12Renderer *)createNV12Renderer {
77 return [[RTCMTLNV12RendererClass alloc] init];
78}
79
80+ (RTCMTLI420Renderer *)createI420Renderer {
81 return [[RTCMTLI420RendererClass alloc] init];
kthelgason954d9b92017-03-09 03:36:58 -080082}
83
denicija070d5e32017-02-26 11:44:13 -080084- (void)configure {
denicijad2088152017-04-28 02:14:54 -070085 NSAssert([RTCMTLVideoView isMetalAvailable], @"Metal not availiable on this device");
kthelgason954d9b92017-03-09 03:36:58 -080086
87 _metalView = [RTCMTLVideoView createMetalView:self.bounds];
denicijad2088152017-04-28 02:14:54 -070088 [self configureMetalView];
kthelgason954d9b92017-03-09 03:36:58 -080089}
90
91- (void)configureMetalView {
92 if (_metalView) {
kthelgason96d91522017-03-08 06:33:52 -080093 _metalView.delegate = self;
kthelgason954d9b92017-03-09 03:36:58 -080094 [self addSubview:_metalView];
JT Tehd5601322018-04-09 23:48:41 +000095 _metalView.contentMode = UIViewContentModeScaleAspectFit;
denicija070d5e32017-02-26 11:44:13 -080096 }
97}
kthelgason954d9b92017-03-09 03:36:58 -080098
Danielac4a14322017-10-31 11:19:38 +010099#pragma mark - Private
100
101- (void)layoutSubviews {
102 [super layoutSubviews];
103 _metalView.frame = self.bounds;
104}
105
denicija070d5e32017-02-26 11:44:13 -0800106#pragma mark - MTKViewDelegate methods
107
108- (void)drawInMTKView:(nonnull MTKView *)view {
109 NSAssert(view == self.metalView, @"Receiving draw callbacks from foreign instance.");
Kári Tristan Helgasone49452d2018-04-10 10:38:43 +0200110 RTCVideoFrame *videoFrame = self.videoFrame;
Kári Tristan Helgason4f7b6402018-04-16 11:39:53 +0200111 // Skip rendering if we've already rendered this frame.
112 if (!videoFrame || videoFrame.timeStampNs == _lastFrameTimeNs) {
denicijad2088152017-04-28 02:14:54 -0700113 return;
114 }
115
Kári Tristan Helgasone49452d2018-04-10 10:38:43 +0200116 if ([videoFrame.buffer isKindOfClass:[RTCCVPixelBuffer class]]) {
JT Teh6144fa52018-04-10 00:27:04 +0000117 if (!self.rendererNV12) {
denicijad2088152017-04-28 02:14:54 -0700118 self.rendererNV12 = [RTCMTLVideoView createNV12Renderer];
119 if (![self.rendererNV12 addRenderingDestination:self.metalView]) {
120 self.rendererNV12 = nil;
121 RTCLogError(@"Failed to create NV12 renderer");
Kári Tristan Helgason4f7b6402018-04-16 11:39:53 +0200122 return;
denicijad2088152017-04-28 02:14:54 -0700123 }
JT Teh6144fa52018-04-10 00:27:04 +0000124 }
Kári Tristan Helgasone49452d2018-04-10 10:38:43 +0200125 [self.rendererNV12 drawFrame:videoFrame];
denicijad2088152017-04-28 02:14:54 -0700126 } else {
JT Teh6144fa52018-04-10 00:27:04 +0000127 if (!self.rendererI420) {
denicijad2088152017-04-28 02:14:54 -0700128 self.rendererI420 = [RTCMTLVideoView createI420Renderer];
129 if (![self.rendererI420 addRenderingDestination:self.metalView]) {
130 self.rendererI420 = nil;
131 RTCLogError(@"Failed to create I420 renderer");
Kári Tristan Helgason4f7b6402018-04-16 11:39:53 +0200132 return;
denicijad2088152017-04-28 02:14:54 -0700133 }
JT Teh6144fa52018-04-10 00:27:04 +0000134 }
Kári Tristan Helgasone49452d2018-04-10 10:38:43 +0200135 [self.rendererI420 drawFrame:videoFrame];
denicijad2088152017-04-28 02:14:54 -0700136 }
Kári Tristan Helgason4f7b6402018-04-16 11:39:53 +0200137 _lastFrameTimeNs = videoFrame.timeStampNs;
denicija070d5e32017-02-26 11:44:13 -0800138}
139
140- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size {
141}
142
143#pragma mark - RTCVideoRenderer
144
145- (void)setSize:(CGSize)size {
kthelgason954d9b92017-03-09 03:36:58 -0800146 self.metalView.drawableSize = size;
denicija070d5e32017-02-26 11:44:13 -0800147}
148
149- (void)renderFrame:(nullable RTCVideoFrame *)frame {
150 if (frame == nil) {
151 RTCLogInfo(@"Incoming frame is nil. Exiting render callback.");
152 return;
153 }
154 self.videoFrame = frame;
155}
156
157@end