From 782dd20dddf602f89c27b730f51cbb39f723385a Mon Sep 17 00:00:00 2001 From: Ryan Zhao Date: Tue, 8 Feb 2022 11:04:18 +1100 Subject: [PATCH] implement screen rotation for video calls --- Session.xcodeproj/project.pbxproj | 4 ++ Session/Calls/CallVC+Camera.swift | 10 ++++- Session/Calls/CallVC.swift | 10 +++-- .../Calls/Views & Modals/MiniCallView.swift | 11 ++--- .../Views & Modals/RemoteVideoView.swift | 40 +++++++++++++++++++ 5 files changed, 65 insertions(+), 10 deletions(-) create mode 100644 Session/Calls/Views & Modals/RemoteVideoView.swift diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index 427531478..9b6fab347 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -153,6 +153,7 @@ 7BA68909272A27BE00EFC32F /* SessionCall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA68908272A27BE00EFC32F /* SessionCall.swift */; }; 7BA6890D27325CCC00EFC32F /* SessionCallManager+CXCallController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA6890C27325CCC00EFC32F /* SessionCallManager+CXCallController.swift */; }; 7BA6890F27325CE300EFC32F /* SessionCallManager+CXProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA6890E27325CE300EFC32F /* SessionCallManager+CXProvider.swift */; }; + 7BAADFCC27B0EF23007BCF92 /* RemoteVideoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAADFCB27B0EF23007BCF92 /* RemoteVideoView.swift */; }; 7BAF54CE27ACCEEC003D12F8 /* Storage+RecentSearchResults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAF54CB27ACCEEC003D12F8 /* Storage+RecentSearchResults.swift */; }; 7BAF54CF27ACCEEC003D12F8 /* GlobalSearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAF54CC27ACCEEC003D12F8 /* GlobalSearchViewController.swift */; }; 7BAF54D027ACCEEC003D12F8 /* EmptySearchResultCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAF54CD27ACCEEC003D12F8 /* EmptySearchResultCell.swift */; }; @@ -1161,6 +1162,7 @@ 7BA68908272A27BE00EFC32F /* SessionCall.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionCall.swift; sourceTree = ""; }; 7BA6890C27325CCC00EFC32F /* SessionCallManager+CXCallController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionCallManager+CXCallController.swift"; sourceTree = ""; }; 7BA6890E27325CE300EFC32F /* SessionCallManager+CXProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionCallManager+CXProvider.swift"; sourceTree = ""; }; + 7BAADFCB27B0EF23007BCF92 /* RemoteVideoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteVideoView.swift; sourceTree = ""; }; 7BAF54CB27ACCEEC003D12F8 /* Storage+RecentSearchResults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Storage+RecentSearchResults.swift"; sourceTree = ""; }; 7BAF54CC27ACCEEC003D12F8 /* GlobalSearchViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlobalSearchViewController.swift; sourceTree = ""; }; 7BAF54CD27ACCEEC003D12F8 /* EmptySearchResultCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmptySearchResultCell.swift; sourceTree = ""; }; @@ -2111,6 +2113,7 @@ 7B7CB18F270FB2150079FF93 /* MiniCallView.swift */, 7B1581E727210ECC00848B49 /* RenderView.swift */, 7B0EFDF52755CC5400FFAAE7 /* CallMissedTipsModal.swift */, + 7BAADFCB27B0EF23007BCF92 /* RemoteVideoView.swift */, ); path = "Views & Modals"; sourceTree = ""; @@ -5039,6 +5042,7 @@ 45F32C222057297A00A300D5 /* MediaDetailViewController.m in Sources */, B82149C125D605C6009C0F2A /* InfoBanner.swift in Sources */, C3DAB3242480CB2B00725F25 /* SRCopyableLabel.swift in Sources */, + 7BAADFCC27B0EF23007BCF92 /* RemoteVideoView.swift in Sources */, B8CCF63F23975CFB0091D419 /* JoinOpenGroupVC.swift in Sources */, 34ABC0E421DD20C500ED9469 /* ConversationMessageMapping.swift in Sources */, B85357C323A1BD1200AAF6CD /* SeedVC.swift in Sources */, diff --git a/Session/Calls/CallVC+Camera.swift b/Session/Calls/CallVC+Camera.swift index ef440e2e4..df2dbcfec 100644 --- a/Session/Calls/CallVC+Camera.swift +++ b/Session/Calls/CallVC+Camera.swift @@ -7,7 +7,15 @@ extension CallVC : CameraManagerDelegate { let rtcPixelBuffer = RTCCVPixelBuffer(pixelBuffer: pixelBuffer) let timestamp = CMTimeGetSeconds(CMSampleBufferGetPresentationTimeStamp(sampleBuffer)) let timestampNs = Int64(timestamp * 1000000000) - let frame = RTCVideoFrame(buffer: rtcPixelBuffer, rotation: RTCVideoRotation._0, timeStampNs: timestampNs) + let rotation: RTCVideoRotation = { + switch UIDevice.current.orientation { + case .landscapeRight: return RTCVideoRotation._90 + case .portraitUpsideDown: return RTCVideoRotation._180 + case .landscapeLeft: return RTCVideoRotation._270 + default: return RTCVideoRotation._0 + } + }() + let frame = RTCVideoFrame(buffer: rtcPixelBuffer, rotation: rotation, timeStampNs: timestampNs) frame.timeStamp = Int32(timestamp) call.webRTCSession.handleLocalFrameCaptured(frame) } diff --git a/Session/Calls/CallVC.swift b/Session/Calls/CallVC.swift index 9a196edd4..64d055a87 100644 --- a/Session/Calls/CallVC.swift +++ b/Session/Calls/CallVC.swift @@ -23,17 +23,19 @@ final class CallVC : UIViewController, VideoPreviewDelegate { private lazy var localVideoView: RTCMTLVideoView = { let result = RTCMTLVideoView() result.isHidden = !call.isVideoEnabled - result.contentMode = .scaleAspectFill + result.videoContentMode = .scaleAspectFit + result.rotationOverride = NSNumber(value: RTCVideoRotation._0.rawValue) result.set(.width, to: 80) result.set(.height, to: 173) result.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture))) return result }() - private lazy var remoteVideoView: RTCMTLVideoView = { - let result = RTCMTLVideoView() + private lazy var remoteVideoView: RemoteVideoView = { + let result = RemoteVideoView() result.alpha = 0 - result.contentMode = .scaleAspectFill + result.videoContentMode = .scaleAspectFit + result.backgroundColor = .black result.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleRemoteVieioViewTapped))) return result }() diff --git a/Session/Calls/Views & Modals/MiniCallView.swift b/Session/Calls/Views & Modals/MiniCallView.swift index 7fe48fb71..74e25882e 100644 --- a/Session/Calls/Views & Modals/MiniCallView.swift +++ b/Session/Calls/Views & Modals/MiniCallView.swift @@ -4,9 +4,10 @@ import WebRTC final class MiniCallView: UIView { var callVC: CallVC - private lazy var remoteVideoView: RTCMTLVideoView = { - let result = RTCMTLVideoView() - result.contentMode = .scaleAspectFill + private lazy var remoteVideoView: RemoteVideoView = { + let result = RemoteVideoView() + result.videoContentMode = .scaleAspectFit + result.backgroundColor = .black return result }() @@ -31,8 +32,8 @@ final class MiniCallView: UIView { } private func setUpViewHierarchy() { - self.set(.width, to: 80) - self.set(.height, to: 173) + self.set(.width, to: 160) + self.set(.height, to: 160) self.layer.masksToBounds = true // Background let background = getBackgroudView() diff --git a/Session/Calls/Views & Modals/RemoteVideoView.swift b/Session/Calls/Views & Modals/RemoteVideoView.swift new file mode 100644 index 000000000..be9d9dd32 --- /dev/null +++ b/Session/Calls/Views & Modals/RemoteVideoView.swift @@ -0,0 +1,40 @@ +// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. +import WebRTC +import Foundation + +class RemoteVideoView: RTCMTLVideoView { + + override func renderFrame(_ frame: RTCVideoFrame?) { + super.renderFrame(frame) + guard let frame = frame else { return } + DispatchMainThreadSafe { + let frameRotation = frame.rotation + let deviceRotation = UIDevice.current.orientation + switch deviceRotation { + case .portrait, .portraitUpsideDown: + // We don't have to do anything, the renderer will automatically make sure it's right-side-up. + self.rotationOverride = nil + case .landscapeLeft: + switch frameRotation { + case RTCVideoRotation._0: self.rotationOverride = NSNumber(value: RTCVideoRotation._90.rawValue) // Landscape left + case RTCVideoRotation._90: self.rotationOverride = NSNumber(value: RTCVideoRotation._180.rawValue) // Portrait + case RTCVideoRotation._180: self.rotationOverride = NSNumber(value: RTCVideoRotation._270.rawValue) // Landscape right + case RTCVideoRotation._270: self.rotationOverride = NSNumber(value: RTCVideoRotation._0.rawValue) // Portrait upside-down + default: self.rotationOverride = nil + } + case .landscapeRight: + switch frameRotation { + case RTCVideoRotation._0: self.rotationOverride = NSNumber(value: RTCVideoRotation._270.rawValue) // Landscape left + case RTCVideoRotation._90: self.rotationOverride = NSNumber(value: RTCVideoRotation._0.rawValue) // Portrait + case RTCVideoRotation._180: self.rotationOverride = NSNumber(value: RTCVideoRotation._90.rawValue) // Landscape right + case RTCVideoRotation._270: self.rotationOverride = NSNumber(value: RTCVideoRotation._180.rawValue) // Portrait upside-down + default: self.rotationOverride = nil + } + default: + // Do nothing if we're face down, up, etc. + // Assume we're already setup for the correct orientation. + break + } + } + } +}