blob: eff06af81e9e629f98b6ed533d5f228fb7302b24 [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
Kári Tristan Helgason06d094f2018-04-19 17:38:05 +020040@synthesize delegate = _delegate;
denicijad2088152017-04-28 02:14:54 -070041@synthesize rendererI420 = _rendererI420;
42@synthesize rendererNV12 = _rendererNV12;
denicija070d5e32017-02-26 11:44:13 -080043@synthesize metalView = _metalView;
44@synthesize videoFrame = _videoFrame;
45
46- (instancetype)initWithFrame:(CGRect)frameRect {
47 self = [super initWithFrame:frameRect];
48 if (self) {
49 [self configure];
50 }
51 return self;
52}
53
54- (instancetype)initWithCoder:(NSCoder *)aCoder {
55 self = [super initWithCoder:aCoder];
56 if (self) {
57 [self configure];
58 }
59 return self;
60}
61
62#pragma mark - Private
63
64+ (BOOL)isMetalAvailable {
kthelgasona2fb30c2017-03-09 03:34:27 -080065#if defined(RTC_SUPPORTS_METAL)
denicija070d5e32017-02-26 11:44:13 -080066 return YES;
kthelgasona2fb30c2017-03-09 03:34:27 -080067#else
denicija070d5e32017-02-26 11:44:13 -080068 return NO;
69#endif
70}
71
kthelgason954d9b92017-03-09 03:36:58 -080072+ (MTKView *)createMetalView:(CGRect)frame {
73 MTKView *view = [[MTKViewClass alloc] initWithFrame:frame];
74 return view;
75}
76
denicijad2088152017-04-28 02:14:54 -070077+ (RTCMTLNV12Renderer *)createNV12Renderer {
78 return [[RTCMTLNV12RendererClass alloc] init];
79}
80
81+ (RTCMTLI420Renderer *)createI420Renderer {
82 return [[RTCMTLI420RendererClass alloc] init];
kthelgason954d9b92017-03-09 03:36:58 -080083}
84
denicija070d5e32017-02-26 11:44:13 -080085- (void)configure {
denicijad2088152017-04-28 02:14:54 -070086 NSAssert([RTCMTLVideoView isMetalAvailable], @"Metal not availiable on this device");
kthelgason954d9b92017-03-09 03:36:58 -080087
88 _metalView = [RTCMTLVideoView createMetalView:self.bounds];
denicijad2088152017-04-28 02:14:54 -070089 [self configureMetalView];
kthelgason954d9b92017-03-09 03:36:58 -080090}
91
92- (void)configureMetalView {
93 if (_metalView) {
kthelgason96d91522017-03-08 06:33:52 -080094 _metalView.delegate = self;
kthelgason954d9b92017-03-09 03:36:58 -080095 [self addSubview:_metalView];
JT Tehd5601322018-04-09 23:48:41 +000096 _metalView.contentMode = UIViewContentModeScaleAspectFit;
denicija070d5e32017-02-26 11:44:13 -080097 }
98}
kthelgason954d9b92017-03-09 03:36:58 -080099
Danielac4a14322017-10-31 11:19:38 +0100100#pragma mark - Private
101
102- (void)layoutSubviews {
103 [super layoutSubviews];
104 _metalView.frame = self.bounds;
105}
106
denicija070d5e32017-02-26 11:44:13 -0800107#pragma mark - MTKViewDelegate methods
108
109- (void)drawInMTKView:(nonnull MTKView *)view {
110 NSAssert(view == self.metalView, @"Receiving draw callbacks from foreign instance.");
Kári Tristan Helgasone49452d2018-04-10 10:38:43 +0200111 RTCVideoFrame *videoFrame = self.videoFrame;
Kári Tristan Helgason4f7b6402018-04-16 11:39:53 +0200112 // Skip rendering if we've already rendered this frame.
113 if (!videoFrame || videoFrame.timeStampNs == _lastFrameTimeNs) {
denicijad2088152017-04-28 02:14:54 -0700114 return;
115 }
116
Kári Tristan Helgasone49452d2018-04-10 10:38:43 +0200117 if ([videoFrame.buffer isKindOfClass:[RTCCVPixelBuffer class]]) {
JT Teh6144fa52018-04-10 00:27:04 +0000118 if (!self.rendererNV12) {
denicijad2088152017-04-28 02:14:54 -0700119 self.rendererNV12 = [RTCMTLVideoView createNV12Renderer];
120 if (![self.rendererNV12 addRenderingDestination:self.metalView]) {
121 self.rendererNV12 = nil;
122 RTCLogError(@"Failed to create NV12 renderer");
Kári Tristan Helgason4f7b6402018-04-16 11:39:53 +0200123 return;
denicijad2088152017-04-28 02:14:54 -0700124 }
JT Teh6144fa52018-04-10 00:27:04 +0000125 }
Kári Tristan Helgasone49452d2018-04-10 10:38:43 +0200126 [self.rendererNV12 drawFrame:videoFrame];
denicijad2088152017-04-28 02:14:54 -0700127 } else {
JT Teh6144fa52018-04-10 00:27:04 +0000128 if (!self.rendererI420) {
denicijad2088152017-04-28 02:14:54 -0700129 self.rendererI420 = [RTCMTLVideoView createI420Renderer];
130 if (![self.rendererI420 addRenderingDestination:self.metalView]) {
131 self.rendererI420 = nil;
132 RTCLogError(@"Failed to create I420 renderer");
Kári Tristan Helgason4f7b6402018-04-16 11:39:53 +0200133 return;
denicijad2088152017-04-28 02:14:54 -0700134 }
JT Teh6144fa52018-04-10 00:27:04 +0000135 }
Kári Tristan Helgasone49452d2018-04-10 10:38:43 +0200136 [self.rendererI420 drawFrame:videoFrame];
denicijad2088152017-04-28 02:14:54 -0700137 }
Kári Tristan Helgason4f7b6402018-04-16 11:39:53 +0200138 _lastFrameTimeNs = videoFrame.timeStampNs;
denicija070d5e32017-02-26 11:44:13 -0800139}
140
141- (void)mtkView:(MTKView *)view drawableSizeWillChange:(CGSize)size {
142}
143
144#pragma mark - RTCVideoRenderer
145
146- (void)setSize:(CGSize)size {
kthelgason954d9b92017-03-09 03:36:58 -0800147 self.metalView.drawableSize = size;
Kári Tristan Helgason06d094f2018-04-19 17:38:05 +0200148 dispatch_async(dispatch_get_main_queue(), ^{
149 [self.delegate videoView:self didChangeVideoSize:size];
150 });
denicija070d5e32017-02-26 11:44:13 -0800151}
152
153- (void)renderFrame:(nullable RTCVideoFrame *)frame {
154 if (frame == nil) {
155 RTCLogInfo(@"Incoming frame is nil. Exiting render callback.");
156 return;
157 }
158 self.videoFrame = frame;
159}
160
161@end