From 1ad6e8282bd58b0ec5b4994290f069ba7885d859 Mon Sep 17 00:00:00 2001 From: Tim Rozum Date: Mon, 8 Aug 2022 14:32:53 -0600 Subject: [PATCH 1/8] Add pip with UIKit --- VideoApp/VideoApp.xcodeproj/project.pbxproj | 77 +++-- .../xcshareddata/swiftpm/Package.resolved | 9 - .../ExampleSampleBufferView.swift | 278 ++++++++++++++++++ .../PictureInPictureViewController.swift | 127 ++++++++ ...SwiftUIPictureInPictureInPictureView.swift | 26 ++ .../Storyboards/Base.lproj/Main.storyboard | 71 ++++- .../SwiftUI/Screens/Home/HomeView.swift | 9 + 7 files changed, 556 insertions(+), 41 deletions(-) create mode 100644 VideoApp/VideoApp/PictureInPicture/ExampleSampleBufferView.swift create mode 100644 VideoApp/VideoApp/PictureInPicture/PictureInPictureViewController.swift create mode 100644 VideoApp/VideoApp/PictureInPicture/SwiftUIPictureInPictureInPictureView.swift diff --git a/VideoApp/VideoApp.xcodeproj/project.pbxproj b/VideoApp/VideoApp.xcodeproj/project.pbxproj index cb1bcca6..24c5790c 100644 --- a/VideoApp/VideoApp.xcodeproj/project.pbxproj +++ b/VideoApp/VideoApp.xcodeproj/project.pbxproj @@ -204,6 +204,14 @@ DC9C772E2500092100AC68FD /* RemoteConfigStoreFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC9C772C2500092100AC68FD /* RemoteConfigStoreFactory.swift */; }; DC9C773025003D5400AC68FD /* MockRemoteConfigStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC9C772F25003D5400AC68FD /* MockRemoteConfigStore.swift */; }; DC9C7732250043A600AC68FD /* RemoteConfigStoreSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC9C7731250043A600AC68FD /* RemoteConfigStoreSpec.swift */; }; + DCA29FB628A1769B00BAA147 /* PictureInPictureViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA29FB528A1769A00BAA147 /* PictureInPictureViewController.swift */; }; + DCA29FB728A1769B00BAA147 /* PictureInPictureViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA29FB528A1769A00BAA147 /* PictureInPictureViewController.swift */; }; + DCA29FB928A179CB00BAA147 /* SwiftUIPictureInPictureInPictureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA29FB828A179CB00BAA147 /* SwiftUIPictureInPictureInPictureView.swift */; }; + DCA29FBA28A179CB00BAA147 /* SwiftUIPictureInPictureInPictureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA29FB828A179CB00BAA147 /* SwiftUIPictureInPictureInPictureView.swift */; }; + DCA29FBC28A19E0F00BAA147 /* ExampleSampleBufferView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA29FBB28A19E0F00BAA147 /* ExampleSampleBufferView.swift */; }; + DCA29FBD28A19E0F00BAA147 /* ExampleSampleBufferView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA29FBB28A19E0F00BAA147 /* ExampleSampleBufferView.swift */; }; + DCA29FBF28A1B66C00BAA147 /* TwilioVideo.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCA29FBE28A1B66C00BAA147 /* TwilioVideo.xcframework */; }; + DCA29FC028A1B66C00BAA147 /* TwilioVideo.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DCA29FBE28A1B66C00BAA147 /* TwilioVideo.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; DCA53949235F53E700CA26FB /* InternalAuthStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA53947235F53E700CA26FB /* InternalAuthStore.swift */; }; DCA5394A235F53E700CA26FB /* InternalAuthStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA53947235F53E700CA26FB /* InternalAuthStore.swift */; }; DCA5394D235F740200CA26FB /* AuthStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA5394B235F740200CA26FB /* AuthStore.swift */; }; @@ -257,8 +265,6 @@ DCB3BAD8282DA2C400AF7072 /* AppCenterDistribute in Frameworks */ = {isa = PBXBuildFile; productRef = DCB3BAD7282DA2C400AF7072 /* AppCenterDistribute */; }; DCB3BADB282DA30B00AF7072 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = DCB3BADA282DA30B00AF7072 /* KeychainAccess */; }; DCB3BADD282DA31400AF7072 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = DCB3BADC282DA31400AF7072 /* KeychainAccess */; }; - DCB3BAE0282DA34400AF7072 /* TwilioVideo in Frameworks */ = {isa = PBXBuildFile; productRef = DCB3BADF282DA34400AF7072 /* TwilioVideo */; }; - DCB3BAE2282DA35100AF7072 /* TwilioVideo in Frameworks */ = {isa = PBXBuildFile; productRef = DCB3BAE1282DA35100AF7072 /* TwilioVideo */; }; DCB64B502409976F00E090BE /* APIRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCB64B4E2409976F00E090BE /* APIRequest.swift */; }; DCB64B512409976F00E090BE /* APIRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCB64B4E2409976F00E090BE /* APIRequest.swift */; }; DCB64B562409BC4E00E090BE /* APIConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCB64B542409BC4E00E090BE /* APIConfig.swift */; }; @@ -439,6 +445,20 @@ }; /* End PBXContainerItemProxy section */ +/* Begin PBXCopyFilesBuildPhase section */ + DCA29FC128A1B66C00BAA147 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + DCA29FC028A1B66C00BAA147 /* TwilioVideo.xcframework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 241282441E36A6AB002198BE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 241282461E36A6AB002198BE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -549,6 +569,10 @@ DC9C772C2500092100AC68FD /* RemoteConfigStoreFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteConfigStoreFactory.swift; sourceTree = ""; }; DC9C772F25003D5400AC68FD /* MockRemoteConfigStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRemoteConfigStore.swift; sourceTree = ""; }; DC9C7731250043A600AC68FD /* RemoteConfigStoreSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteConfigStoreSpec.swift; sourceTree = ""; }; + DCA29FB528A1769A00BAA147 /* PictureInPictureViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PictureInPictureViewController.swift; sourceTree = ""; }; + DCA29FB828A179CB00BAA147 /* SwiftUIPictureInPictureInPictureView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIPictureInPictureInPictureView.swift; sourceTree = ""; }; + DCA29FBB28A19E0F00BAA147 /* ExampleSampleBufferView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleSampleBufferView.swift; sourceTree = ""; }; + DCA29FBE28A1B66C00BAA147 /* TwilioVideo.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = TwilioVideo.xcframework; path = "../../twilio-video-ios-internal/temp/Artifacts/Debug/Dynamic/TwilioVideo.xcframework"; sourceTree = ""; }; DCA53947235F53E700CA26FB /* InternalAuthStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternalAuthStore.swift; sourceTree = ""; }; DCA5394B235F740200CA26FB /* AuthStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthStore.swift; sourceTree = ""; }; DCA5394F235F765D00CA26FB /* AuthFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthFlow.swift; sourceTree = ""; }; @@ -684,8 +708,8 @@ DC699C97282EB231009D3C1A /* FirebaseAnalytics in Frameworks */, DC699C7C282DB045009D3C1A /* FirebaseAuth in Frameworks */, DCB3BAD0282DA20000AF7072 /* Alamofire in Frameworks */, + DCA29FBF28A1B66C00BAA147 /* TwilioVideo.xcframework in Frameworks */, DC699C7E282DB045009D3C1A /* FirebaseCrashlytics in Frameworks */, - DCB3BAE0282DA34400AF7072 /* TwilioVideo in Frameworks */, DCB3BADB282DA30B00AF7072 /* KeychainAccess in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -697,7 +721,6 @@ DCB3BAD8282DA2C400AF7072 /* AppCenterDistribute in Frameworks */, DC699C89282DB536009D3C1A /* GoogleSignIn in Frameworks */, DCB3BAD3282DA23200AF7072 /* Alamofire in Frameworks */, - DCB3BAE2282DA35100AF7072 /* TwilioVideo in Frameworks */, DC699C82282DB084009D3C1A /* FirebaseCrashlytics in Frameworks */, DCB3BADD282DA31400AF7072 /* KeychainAccess in Frameworks */, DC699C84282DB08B009D3C1A /* FirebaseAuth in Frameworks */, @@ -765,6 +788,7 @@ DC44C2CC23871C2000205174 /* Helpers */, DCE9B05D235A46EA008E5B5B /* IOSVideoAppSecrets */, DC7790CA236B6B3800973B86 /* Launch */, + DCA29FB428A1765C00BAA147 /* PictureInPicture */, DCAEBEE32376011D00141D2D /* Stores */, DCAEBEE42376049700141D2D /* Storyboards */, DCE1A9E527C7E28E004680CA /* SwiftUI */, @@ -1199,6 +1223,16 @@ path = RemoteConfig; sourceTree = ""; }; + DCA29FB428A1765C00BAA147 /* PictureInPicture */ = { + isa = PBXGroup; + children = ( + DCA29FB528A1769A00BAA147 /* PictureInPictureViewController.swift */, + DCA29FB828A179CB00BAA147 /* SwiftUIPictureInPictureInPictureView.swift */, + DCA29FBB28A19E0F00BAA147 /* ExampleSampleBufferView.swift */, + ); + path = PictureInPicture; + sourceTree = ""; + }; DCA53946235F534400CA26FB /* Auth */ = { isa = PBXGroup; children = ( @@ -1344,6 +1378,7 @@ DCB3BAD1282DA23200AF7072 /* Frameworks */ = { isa = PBXGroup; children = ( + DCA29FBE28A1B66C00BAA147 /* TwilioVideo.xcframework */, ); name = Frameworks; sourceTree = ""; @@ -1567,6 +1602,7 @@ 4B0012231FBA52C4004A587E /* Sources */, 4B0012351FBA52C4004A587E /* Frameworks */, 4B0012401FBA52C4004A587E /* Resources */, + DCA29FC128A1B66C00BAA147 /* Embed Frameworks */, ); buildRules = ( ); @@ -1577,7 +1613,6 @@ DCB3BACF282DA20000AF7072 /* Alamofire */, DCB3BAD5282DA2B300AF7072 /* AppCenterDistribute */, DCB3BADA282DA30B00AF7072 /* KeychainAccess */, - DCB3BADF282DA34400AF7072 /* TwilioVideo */, DC699C7B282DB045009D3C1A /* FirebaseAuth */, DC699C7D282DB045009D3C1A /* FirebaseCrashlytics */, DC699C86282DB4E9009D3C1A /* GoogleSignIn */, @@ -1604,7 +1639,6 @@ DCB3BAD2282DA23200AF7072 /* Alamofire */, DCB3BAD7282DA2C400AF7072 /* AppCenterDistribute */, DCB3BADC282DA31400AF7072 /* KeychainAccess */, - DCB3BAE1282DA35100AF7072 /* TwilioVideo */, DC699C7F282DB07D009D3C1A /* FirebaseAnalyticsSwift */, DC699C81282DB084009D3C1A /* FirebaseCrashlytics */, DC699C83282DB08B009D3C1A /* FirebaseAuth */, @@ -1732,7 +1766,6 @@ DCB3BACE282DA20000AF7072 /* XCRemoteSwiftPackageReference "Alamofire" */, DCB3BAD4282DA2B300AF7072 /* XCRemoteSwiftPackageReference "appcenter-sdk-apple" */, DCB3BAD9282DA30B00AF7072 /* XCRemoteSwiftPackageReference "KeychainAccess" */, - DCB3BADE282DA34400AF7072 /* XCRemoteSwiftPackageReference "twilio-video-ios" */, DC699C78282DB045009D3C1A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, DC699C85282DB4E9009D3C1A /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */, DC699C8A282EAD46009D3C1A /* XCRemoteSwiftPackageReference "Quick" */, @@ -1877,6 +1910,7 @@ DC0446B923A182980072F597 /* CrashReportStore.swift in Sources */, DC76FD0224105500001B2FFC /* EnvironmentVariableStore.swift in Sources */, DCEDB387247489DA006AF70D /* SelectSettingViewModel.swift in Sources */, + DCA29FB928A179CB00BAA147 /* SwiftUIPictureInPictureInPictureView.swift in Sources */, DCB7C18A2405BE40009DEA70 /* KeychainStore.swift in Sources */, DCF4615623859BB800DD8FEA /* AppInfoStore.swift in Sources */, DCEDB35E24733641006AF70D /* LoginViewController.swift in Sources */, @@ -1910,6 +1944,7 @@ DC44C2E723876AFB00205174 /* SelectOptionSegueSender.swift in Sources */, DCB7C18F2405C0BE009DEA70 /* KeychainStorage.swift in Sources */, DC44C2E323875C4F00205174 /* StringHelpers.swift in Sources */, + DCA29FB628A1769B00BAA147 /* PictureInPictureViewController.swift in Sources */, DCE1AA2127C7E58A004680CA /* SpeakerLayoutView.swift in Sources */, 8A206F40263919CA00EFCD97 /* SelectClientTrackSwitchOffControlViewModelFactory.swift in Sources */, DCF4614D238598C700DD8FEA /* SettingsViewModel.swift in Sources */, @@ -1971,6 +2006,7 @@ DCC7E36224082EB300431AC3 /* AuthError.swift in Sources */, DCAEBF1D2383342C00141D2D /* DestructiveButtonCell.swift in Sources */, DCE1AA2F27C7E58A004680CA /* GalleryLayoutViewModel.swift in Sources */, + DCA29FBC28A19E0F00BAA147 /* ExampleSampleBufferView.swift in Sources */, DC9A5E5424638A9C00E4B079 /* ShowError.swift in Sources */, DCEC3D0B238608B500EBDEBF /* EditUserIdentityViewModelFactory.swift in Sources */, DCD7674C23DA336600A2939C /* DeepLink.swift in Sources */, @@ -2048,6 +2084,7 @@ DC0446BA23A182980072F597 /* CrashReportStore.swift in Sources */, DC76FD0324105501001B2FFC /* EnvironmentVariableStore.swift in Sources */, DCEDB388247489DA006AF70D /* SelectSettingViewModel.swift in Sources */, + DCA29FBA28A179CB00BAA147 /* SwiftUIPictureInPictureInPictureView.swift in Sources */, DCB7C18B2405BE40009DEA70 /* KeychainStore.swift in Sources */, DCF4615723859BB800DD8FEA /* AppInfoStore.swift in Sources */, DCEDB35F24733641006AF70D /* LoginViewController.swift in Sources */, @@ -2081,6 +2118,7 @@ DCB7C1902405C0BE009DEA70 /* KeychainStorage.swift in Sources */, DC44C2E423875C4F00205174 /* StringHelpers.swift in Sources */, DCE1AA2227C7E58A004680CA /* SpeakerLayoutView.swift in Sources */, + DCA29FB728A1769B00BAA147 /* PictureInPictureViewController.swift in Sources */, 8A206F41263919CA00EFCD97 /* SelectClientTrackSwitchOffControlViewModelFactory.swift in Sources */, DCF4614E238598C700DD8FEA /* SettingsViewModel.swift in Sources */, DCEDC63A27E38D3E00BD7C75 /* MediaSetupView.swift in Sources */, @@ -2142,6 +2180,7 @@ DCC7E36324082EB300431AC3 /* AuthError.swift in Sources */, DCAEBF1E2383342C00141D2D /* DestructiveButtonCell.swift in Sources */, DCE1AA3027C7E58A004680CA /* GalleryLayoutViewModel.swift in Sources */, + DCA29FBD28A19E0F00BAA147 /* ExampleSampleBufferView.swift in Sources */, DC9A5E5524638A9C00E4B079 /* ShowError.swift in Sources */, DCEC3D0C238608B500EBDEBF /* EditUserIdentityViewModelFactory.swift in Sources */, DCD7674D23DA336600A2939C /* DeepLink.swift in Sources */, @@ -2383,13 +2422,14 @@ CURRENT_PROJECT_VERSION = 95; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = SX5J6BN2KX; + ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", ); INFOPLIST_FILE = VideoApp/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.5; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2424,10 +2464,11 @@ CURRENT_PROJECT_VERSION = 95; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = SX5J6BN2KX; + ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; INFOPLIST_FILE = VideoApp/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.5; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2818,14 +2859,6 @@ minimumVersion = 4.0.0; }; }; - DCB3BADE282DA34400AF7072 /* XCRemoteSwiftPackageReference "twilio-video-ios" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/twilio/twilio-video-ios"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 5.0.0; - }; - }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -2924,16 +2957,6 @@ package = DCB3BAD9282DA30B00AF7072 /* XCRemoteSwiftPackageReference "KeychainAccess" */; productName = KeychainAccess; }; - DCB3BADF282DA34400AF7072 /* TwilioVideo */ = { - isa = XCSwiftPackageProductDependency; - package = DCB3BADE282DA34400AF7072 /* XCRemoteSwiftPackageReference "twilio-video-ios" */; - productName = TwilioVideo; - }; - DCB3BAE1282DA35100AF7072 /* TwilioVideo */ = { - isa = XCSwiftPackageProductDependency; - package = DCB3BADE282DA34400AF7072 /* XCRemoteSwiftPackageReference "twilio-video-ios" */; - productName = TwilioVideo; - }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 2412822F1E36A6AB002198BE /* Project object */; diff --git a/VideoApp/VideoApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/VideoApp/VideoApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index e92eadd7..76196871 100644 --- a/VideoApp/VideoApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/VideoApp/VideoApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -206,15 +206,6 @@ "revision" : "e1499bc69b9040b29184f7f2996f7bab467c1639", "version" : "1.19.0" } - }, - { - "identity" : "twilio-video-ios", - "kind" : "remoteSourceControl", - "location" : "https://github.com/twilio/twilio-video-ios", - "state" : { - "revision" : "21805ce89585481405bb914922be288d47c1fc62", - "version" : "5.1.1" - } } ], "version" : 2 diff --git a/VideoApp/VideoApp/PictureInPicture/ExampleSampleBufferView.swift b/VideoApp/VideoApp/PictureInPicture/ExampleSampleBufferView.swift new file mode 100644 index 00000000..c2678e5e --- /dev/null +++ b/VideoApp/VideoApp/PictureInPicture/ExampleSampleBufferView.swift @@ -0,0 +1,278 @@ +// +// ExampleSampleBufferView.swift +// VideoApp +// +// Created by Tim Rozum on 8/8/22. +// Copyright © 2022 Twilio, Inc. All rights reserved. +// + +import AVFoundation +import Foundation +import UIKit +import TwilioVideo + +protocol ExampleSampleBufferRendererDelegate { + func bufferViewVideoChanged(view: ExampleSampleBufferView, + dimensions: CMVideoDimensions, + orientation: VideoOrientation) +} + +class ExampleSampleBufferView : UIView, VideoRenderer { + + public var videoDimensions: CMVideoDimensions + public var videoOrientation: VideoOrientation + + var isRendering = UIApplication.shared.applicationState != .background + var outputFormatDescription: CMFormatDescription? + // Allows the renderer to enqueue frames from a background thread without accessing self.layer directly. + var cachedDisplayLayer : AVSampleBufferDisplayLayer? + + /* + * Register pixel formats that are known to work with AVSampleBufferDisplayLayer. + * At a minimum, the CVPixelBuffers are expected to be backed by an IOSurface so we will not support + * every possible input from CoreVideo. + */ + var optionalPixelFormats: [NSNumber] = [NSNumber.init(value: PixelFormat.formatYUV420BiPlanarFullRange.rawValue), + NSNumber.init(value: PixelFormat.formatYUV420BiPlanarVideoRange.rawValue), + NSNumber.init(value: PixelFormat.format32BGRA.rawValue), + NSNumber.init(value: PixelFormat.format32ARGB.rawValue)] + + required init?(coder aDecoder: NSCoder) { + // This example does not support storyboards. + assert(false, "Unsupported.") + return nil + } + + override init(frame: CGRect) { + videoDimensions = CMVideoDimensions(width: 0, height: 0) + videoOrientation = VideoOrientation.up + + super.init(frame: frame) + + backgroundColor = .black + + cachedDisplayLayer = super.layer as? AVSampleBufferDisplayLayer +// let center = NotificationCenter.default +// +// center.addObserver(self, selector: #selector(ExampleSampleBufferView.willEnterForeground), +// name: UIApplication.willEnterForegroundNotification, object: nil) +// center.addObserver(self, selector: #selector(ExampleSampleBufferView.didEnterBackground), +// name: UIApplication.didEnterBackgroundNotification, object: nil) +// center.addObserver(self, selector: #selector(ExampleSampleBufferView.willResignActive), +// name: UIApplication.willResignActiveNotification, object: nil) +// [_sampleView addObserver:self forKeyPath:@"layer.status" options:NSKeyValueObservingOptionNew context:NULL]; + } + + deinit { + outputFormatDescription = nil + + NotificationCenter.default.removeObserver(self) + +// [self.sampleView removeObserver:self forKeyPath:@"layer.status"]; + } + + override class var layerClass: AnyClass { + AVSampleBufferDisplayLayer.self + } + + var displayLayer: AVSampleBufferDisplayLayer { + layer as! AVSampleBufferDisplayLayer + } + + override var contentMode: UIView.ContentMode { + get { + return super.contentMode + } + set { + // Map UIViewContentMode to AVLayerVideoGravity. The layer supports a subset of possible content modes. + switch newValue { + case .scaleAspectFill: + displayLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill + case .scaleAspectFit: + displayLayer.videoGravity = AVLayerVideoGravity.resizeAspect + case .scaleToFill: + displayLayer.videoGravity = AVLayerVideoGravity.resize + default: + displayLayer.videoGravity = AVLayerVideoGravity.resize + } + setNeedsLayout() + + super.contentMode = newValue + } + } + + override func didMoveToSuperview() { + guard let superview = superview else { return } + + translatesAutoresizingMaskIntoConstraints = false + let constraints = [ + leadingAnchor.constraint(equalTo: superview.leadingAnchor), + trailingAnchor.constraint(equalTo: superview.trailingAnchor), + topAnchor.constraint(equalTo: superview.topAnchor), + bottomAnchor.constraint(equalTo: superview.bottomAnchor) + ] + NSLayoutConstraint.activate(constraints) + + print("TCR constraints: \(constraints)") + } +} + +extension ExampleSampleBufferView { + @objc func willEnterForeground(_: NSNotification) { + + if (displayLayer.status == AVQueuedSampleBufferRenderingStatus.failed) { + // TODO: Restore failed sample buffer view. AVErrorOperationInterrupted. + } + + isRendering = true + } + + @objc func didEnterBackground(_: NSNotification) { + isRendering = false + displayLayer.flushAndRemoveImage() + } + + @objc func willResignActive(_: NSNotification) { + // TODO: - Should we stop rendering when resigning active? + // AVSampleBufferDisplayLayer seems capable of handling this case. + } +} + +extension ExampleSampleBufferView { + + func renderFrame(_ frame: VideoFrame) { + +// print("renderFrame") + let pixelFormat = CVPixelBufferGetPixelFormatType(frame.imageBuffer) + + // Unfortunately I420 is not directly supported by AVSampleBufferDisplayLayer. + // This example renderer does not attempt to support I420, but please see ExampleVideoRecorder for an example of + // performing an I420 to NV12 format conversion. Doing so efficiently for a renderer would require maintaining + // a CVPixelBufferPool of NV12 frames which are ready to be displayed. +// if (self.isRendering == false ) { +// return +// } else + if (pixelFormat == PixelFormat.formatYUV420PlanarFullRange.rawValue || + pixelFormat == PixelFormat.formatYUV420PlanarVideoRange.rawValue) { + print("Unsupported I420 pixel format!"); + return + } + + // Enqueuing a frame to AVSampleDisplayLayer may cause UIKit related accesses if the resolution has changed. + // When a format change occurs ensure that we synchronize with the main queue to deliver the first frame. +// if (detectFormatChange(imageBuffer: frame.imageBuffer) && !Thread.isMainThread) { +// DispatchQueue.main.sync { +// self.enqueueFrame(frame: frame) +// } +// } else { +// enqueueFrame(frame: frame) +// } + DispatchQueue.main.sync { + _ = self.detectFormatChange(imageBuffer: frame.imageBuffer) + self.enqueueFrame(frame: frame) + } + } + + func updateVideoSize(_ videoSize: CMVideoDimensions, orientation: VideoOrientation) { + DispatchQueue.main.async { + // Update properties to help with View layout. + let orientationChanged = orientation != self.videoOrientation + let animate = orientationChanged && (videoSize.width == self.videoDimensions.width && videoSize.height == self.videoDimensions.height) + self.videoDimensions = videoSize + self.videoOrientation = orientation + + // TODO: Should we be doing this here, or delegating to a view controller? + UIView.animate(withDuration: animate ? 0.3 : 0, animations: { + let size = videoSize + let scaleFactor = size.height > size.width ? CGFloat(size.height) / CGFloat(size.width) : CGFloat(size.width) / CGFloat(size.height) + switch (orientation) { + case .up: + self.transform = CGAffineTransform.identity; + break + case .left: + let scale = CGAffineTransform.init(scaleX: scaleFactor, + y: scaleFactor) + self.transform = CGAffineTransform(rotationAngle: CGFloat.pi / 2).concatenating(scale) + break + case .down: + self.transform = CGAffineTransform(rotationAngle: CGFloat.pi) + break + case .right: + let scale = CGAffineTransform.init(scaleX: scaleFactor, + y: scaleFactor) + self.transform = CGAffineTransform(rotationAngle: CGFloat.pi * 3 / 2).concatenating(scale) + break + @unknown default: + fatalError() + } + }) + } + } + + func detectFormatChange(imageBuffer: CVPixelBuffer) -> Bool { + var didChange = false + if (self.outputFormatDescription == nil || + CMVideoFormatDescriptionMatchesImageBuffer(self.outputFormatDescription!, imageBuffer: imageBuffer) == false) { + let status = CMVideoFormatDescriptionCreateForImageBuffer(allocator: kCFAllocatorDefault, + imageBuffer: imageBuffer, + formatDescriptionOut: &self.outputFormatDescription) + + if let format = self.outputFormatDescription { + let dimensions = CMVideoFormatDescriptionGetDimensions(format) + let pixelFormat = CVPixelBufferGetPixelFormatType(imageBuffer) + let utf16 = [ + UInt16((pixelFormat >> 24) & 0xFF), + UInt16((pixelFormat >> 16) & 0xFF), + UInt16((pixelFormat >> 8) & 0xFF), + UInt16((pixelFormat & 0xFF)) ] + let pixelFormatString = String(utf16CodeUnits: utf16, count: 4) + print("Detected format change: \(dimensions.width) x \(dimensions.height) - \(pixelFormatString)") + didChange = true + } else { + print("Failed to create output format description with status: \(status)") + } + } + return didChange + } + + // TODO: Return OSStatus? + func enqueueFrame(frame: VideoFrame) { + let imageBuffer = frame.imageBuffer + + if (self.cachedDisplayLayer?.error != nil) { + return + } else if (self.cachedDisplayLayer?.isReadyForMoreMediaData == false) { + print("AVSampleBufferDisplayLayer is not ready for more frames."); + return + } + + // Use the frame's timestamp as the presentation timestamp. We will display immediately. + // Our uncompressed buffers do not need to be decoded. + var sampleTiming = CMSampleTimingInfo.init(duration: CMTime.invalid, + presentationTimeStamp: frame.timestamp, + decodeTimeStamp: CMTime.invalid) + + // Create a CMSampleBuffer + var sampleBuffer: CMSampleBuffer? + + let status = CMSampleBufferCreateReadyWithImageBuffer(allocator: kCFAllocatorDefault, + imageBuffer: imageBuffer, + formatDescription: self.outputFormatDescription!, + sampleTiming: &sampleTiming, + sampleBufferOut: &sampleBuffer) + + // Enqueue the frame for display via AVSampleBufferDisplayLayer. + if (status != kCVReturnSuccess) { + print("Couldn't create a SampleBuffer. Status=\(status)") + return + } else if let sampleBuffer = sampleBuffer, +// let displayLayer = cachedDisplayLayer, + let sampleAttachments = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, createIfNecessary: true) as NSArray? { + // Force immediate display of the buffer, since the renderer receives samples just in time. + let firstAttachment = sampleAttachments.firstObject as! NSMutableDictionary? + firstAttachment?[kCMSampleAttachmentKey_DisplayImmediately] = true + + displayLayer.enqueue(sampleBuffer) + } + } +} diff --git a/VideoApp/VideoApp/PictureInPicture/PictureInPictureViewController.swift b/VideoApp/VideoApp/PictureInPicture/PictureInPictureViewController.swift new file mode 100644 index 00000000..9ac1b6f4 --- /dev/null +++ b/VideoApp/VideoApp/PictureInPicture/PictureInPictureViewController.swift @@ -0,0 +1,127 @@ +// +// PictureInPictureViewController.swift +// VideoApp +// +// Created by Tim Rozum on 8/8/22. +// Copyright © 2022 Twilio, Inc. All rights reserved. +// + +import AVKit +import Combine +import UIKit + +class PictureInPictureViewController: UIViewController { + @IBOutlet weak var videoView: VideoTrackStoringVideoView! + + var callManager: CallManager! + var roomManager: RoomManager! + + private let accessTokenStore = TwilioAccessTokenStore() + private var pipController: AVPictureInPictureController! + private var pipVideoCallViewController: AVPictureInPictureVideoCallViewController! + private var subscriptions = Set() + + override func viewDidLoad() { + super.viewDidLoad() + +// let sampleBufferVideoCallView = ExampleSampleBufferView( +// frame: CGRect(x: 0, y: 0, width: 100, height: 200) +// ) + + let sampleBufferVideoCallView = ExampleSampleBufferView() + + + sampleBufferVideoCallView.contentMode = .scaleAspectFit + + + + pipVideoCallViewController = AVPictureInPictureVideoCallViewController() + + // Pretty much just for aspect ratio, normally used for pop-over + pipVideoCallViewController.preferredContentSize = CGSize(width: 100, height: 200) +// pipVideoCallViewController.preferredContentSize = CGSize(width: 1080, height: 1920) + + pipVideoCallViewController.view.addSubview(sampleBufferVideoCallView) + +// sampleBufferVideoCallView.translatesAutoresizingMaskIntoConstraints = false +// let constraints = [ +// sampleBufferVideoCallView.leadingAnchor.constraint(equalTo: view.leadingAnchor), +// sampleBufferVideoCallView.trailingAnchor.constraint(equalTo: view.trailingAnchor), +// sampleBufferVideoCallView.topAnchor.constraint(equalTo: view.topAnchor), +// sampleBufferVideoCallView.bottomAnchor.constraint(equalTo: view.bottomAnchor) +// ] +// NSLayoutConstraint.activate(constraints) + + +// sampleBufferVideoCallView.bounds = pipVideoCallViewController.view.frame + + let pipContentSource = AVPictureInPictureController.ContentSource( + activeVideoCallSourceView: videoView, + contentViewController: pipVideoCallViewController + ) + + pipController = AVPictureInPictureController(contentSource: pipContentSource) + pipController.canStartPictureInPictureAutomaticallyFromInline = true + pipController.delegate = self + + print("Is pip supported: \(AVPictureInPictureController.isPictureInPictureSupported())") + print("Is pip possible: \(pipController.isPictureInPicturePossible)") + + callManager.connectPublisher + .sink { + print("TCR: Did connect") + } + .store(in: &subscriptions) + + roomManager.remoteParticipantChangePublisher + .sink { [weak self] participant in + if let track = participant.cameraTrack { + + if track.renderers.first(where: { $0 === sampleBufferVideoCallView}) == nil { + self?.videoView.videoTrack = track + track.addRenderer(sampleBufferVideoCallView) + print("TCR: Added renderer") + } + } + } + .store(in: &subscriptions) + + callManager.connect(roomName: "tcr") + } + + @IBAction func startPipButtonTap(_ sender: Any) { + pipController.startPictureInPicture() + } + + @IBAction func stopPictureInPictureButtonTap(_ sender: Any) { + pipController.stopPictureInPicture() + } + + +} + +extension PictureInPictureViewController: AVPictureInPictureControllerDelegate { + func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { + print("pip controller delegate: will start") + } + + func pictureInPictureControllerDidStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { + print("pip controller delegate: did start") + } + + func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, failedToStartPictureInPictureWithError error: Error) { + print("pip controller delegate: failed to start \(error)") + } + + func pictureInPictureControllerWillStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { + print("pip controller delegate: will stop") + } + + func pictureInPictureControllerDidStopPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { + print("pip controller delegate: did stop") + } + + func pictureInPictureController(_ pictureInPictureController: AVPictureInPictureController, restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) { + print("pip controller delegate: restore UI") + } +} diff --git a/VideoApp/VideoApp/PictureInPicture/SwiftUIPictureInPictureInPictureView.swift b/VideoApp/VideoApp/PictureInPicture/SwiftUIPictureInPictureInPictureView.swift new file mode 100644 index 00000000..5351ec3e --- /dev/null +++ b/VideoApp/VideoApp/PictureInPicture/SwiftUIPictureInPictureInPictureView.swift @@ -0,0 +1,26 @@ +// +// SwiftUIPictureInPictureInPictureView.swift +// VideoApp +// +// Created by Tim Rozum on 8/8/22. +// Copyright © 2022 Twilio, Inc. All rights reserved. +// + +import SwiftUI + +struct SwiftUIPictureInPictureView: UIViewControllerRepresentable { + @EnvironmentObject var callManager: CallManager + @EnvironmentObject var roomManager: RoomManager + + func makeUIViewController(context: Context) -> PictureInPictureViewController { + let storyboard = UIStoryboard(name: "Main", bundle: nil) + let controller = storyboard.instantiateViewController(withIdentifier: "PictureInPictureViewController") as! PictureInPictureViewController + controller.callManager = callManager + controller.roomManager = roomManager + return controller + } + + func updateUIViewController(_ uiViewController: PictureInPictureViewController, context: Context) { + + } +} diff --git a/VideoApp/VideoApp/Storyboards/Base.lproj/Main.storyboard b/VideoApp/VideoApp/Storyboards/Base.lproj/Main.storyboard index 199fa6aa..031d12fc 100755 --- a/VideoApp/VideoApp/Storyboards/Base.lproj/Main.storyboard +++ b/VideoApp/VideoApp/Storyboards/Base.lproj/Main.storyboard @@ -1,9 +1,8 @@ - + - - + @@ -318,7 +317,7 @@ - + @@ -351,7 +350,7 @@ - + @@ -485,6 +484,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/VideoApp/VideoApp/SwiftUI/Screens/Home/HomeView.swift b/VideoApp/VideoApp/SwiftUI/Screens/Home/HomeView.swift index dfc3eb68..9b02f09d 100644 --- a/VideoApp/VideoApp/SwiftUI/Screens/Home/HomeView.swift +++ b/VideoApp/VideoApp/SwiftUI/Screens/Home/HomeView.swift @@ -26,6 +26,7 @@ struct HomeView: View { @State private var isShowingMediaSetup = false @State private var isShowingRoom = false @State private var isShowingSettings = false + @State private var isShowingPictureInPicture = false @State private var isMediaSetup = false var body: some View { @@ -49,6 +50,11 @@ struct HomeView: View { } .buttonStyle(PrimaryButtonStyle(isEnabled: !roomName.isEmpty)) .disabled(roomName.isEmpty) + + Button("PiP Test") { + isShowingPictureInPicture = true + } + .buttonStyle(.borderedProminent) } .toolbar { Button(action: { isShowingSettings.toggle() }) { @@ -65,6 +71,9 @@ struct HomeView: View { .sheet(isPresented: $isShowingSettings) { SettingsView() } + .sheet(isPresented: $isShowingPictureInPicture) { + SwiftUIPictureInPictureView() + } .fullScreenCover(isPresented: $isShowingRoom) { RoomViewDependencyWrapper(roomName: roomName) } From d8a0e3922b32cc2ca440f62e8d107fab0354ad55 Mon Sep 17 00:00:00 2001 From: Tim Rozum Date: Thu, 1 Sep 2022 11:22:55 -0600 Subject: [PATCH 2/8] Test placeholder view --- VideoApp/VideoApp.xcodeproj/project.pbxproj | 43 ++- .../ExampleSampleBufferView.swift | 278 ------------------ .../PictureInPicture/PIPPlaceholderView.swift | 58 ++++ .../PictureInPictureViewController.swift | 9 +- .../Screens/MediaSetup/MediaSetupView.swift | 8 +- 5 files changed, 96 insertions(+), 300 deletions(-) delete mode 100644 VideoApp/VideoApp/PictureInPicture/ExampleSampleBufferView.swift create mode 100644 VideoApp/VideoApp/PictureInPicture/PIPPlaceholderView.swift diff --git a/VideoApp/VideoApp.xcodeproj/project.pbxproj b/VideoApp/VideoApp.xcodeproj/project.pbxproj index 24c5790c..77c4a8db 100644 --- a/VideoApp/VideoApp.xcodeproj/project.pbxproj +++ b/VideoApp/VideoApp.xcodeproj/project.pbxproj @@ -95,6 +95,8 @@ DC4568882385D5490069BB5A /* ViewControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC4568852385D5490069BB5A /* ViewControllerFactory.swift */; }; DC4E4E2023577EA700C5D313 /* AppSettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC4E4E1E23577EA700C5D313 /* AppSettingsStore.swift */; }; DC4E4E2123577EA700C5D313 /* AppSettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC4E4E1E23577EA700C5D313 /* AppSettingsStore.swift */; }; + DC4F28BE28B80EDB00112FA8 /* PIPPlaceholderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC4F28BD28B80EDB00112FA8 /* PIPPlaceholderView.swift */; }; + DC4F28BF28B80EDB00112FA8 /* PIPPlaceholderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC4F28BD28B80EDB00112FA8 /* PIPPlaceholderView.swift */; }; DC529FF7289333A2005E5803 /* CallManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC89D2B286649FC00B3DD84 /* CallManager.swift */; }; DC5CC370249C0D0300355CC6 /* UserStoreSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC04469C238F29500072F597 /* UserStoreSpec.swift */; }; DC5CC372249C0D0300355CC6 /* DeepLinkSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCD7674E23DA350A00A2939C /* DeepLinkSpec.swift */; }; @@ -124,6 +126,8 @@ DC5CC39F249C0F5900355CC6 /* TestSecrets.json in Resources */ = {isa = PBXBuildFile; fileRef = DC9A4C9123D161A200D37CEC /* TestSecrets.json */; }; DC5CC3A0249C0F5900355CC6 /* TestSecrets.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC0446A2239716860072F597 /* TestSecrets.swift */; }; DC5CC3A1249C0F5900355CC6 /* TestSecretsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC0446A0239716530072F597 /* TestSecretsStore.swift */; }; + DC5E3D9F28B6D21B009E3CF2 /* TwilioVideo.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCA29FBE28A1B66C00BAA147 /* TwilioVideo.xcframework */; }; + DC5E3DA028B6D21B009E3CF2 /* TwilioVideo.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DCA29FBE28A1B66C00BAA147 /* TwilioVideo.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; DC699C7C282DB045009D3C1A /* FirebaseAuth in Frameworks */ = {isa = PBXBuildFile; productRef = DC699C7B282DB045009D3C1A /* FirebaseAuth */; }; DC699C7E282DB045009D3C1A /* FirebaseCrashlytics in Frameworks */ = {isa = PBXBuildFile; productRef = DC699C7D282DB045009D3C1A /* FirebaseCrashlytics */; }; DC699C80282DB07D009D3C1A /* FirebaseAnalyticsSwift in Frameworks */ = {isa = PBXBuildFile; productRef = DC699C7F282DB07D009D3C1A /* FirebaseAnalyticsSwift */; }; @@ -139,6 +143,8 @@ DC699C97282EB231009D3C1A /* FirebaseAnalytics in Frameworks */ = {isa = PBXBuildFile; productRef = DC699C96282EB231009D3C1A /* FirebaseAnalytics */; }; DC76FD0224105500001B2FFC /* EnvironmentVariableStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC76FD00241054FC001B2FFC /* EnvironmentVariableStore.swift */; }; DC76FD0324105501001B2FFC /* EnvironmentVariableStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC76FD00241054FC001B2FFC /* EnvironmentVariableStore.swift */; }; + DC777A5A28B0272500FD9925 /* TwilioVideo.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCA29FBE28A1B66C00BAA147 /* TwilioVideo.xcframework */; }; + DC777A5B28B0272500FD9925 /* TwilioVideo.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DCA29FBE28A1B66C00BAA147 /* TwilioVideo.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; DC82A35023FC9AAA0081578C /* JSONContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC82A34E23FC9AAA0081578C /* JSONContainer.swift */; }; DC82A35123FC9AAA0081578C /* JSONContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC82A34E23FC9AAA0081578C /* JSONContainer.swift */; }; DC82A35423FDB6890081578C /* SDKLogLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC82A35223FDB6890081578C /* SDKLogLevel.swift */; }; @@ -208,10 +214,6 @@ DCA29FB728A1769B00BAA147 /* PictureInPictureViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA29FB528A1769A00BAA147 /* PictureInPictureViewController.swift */; }; DCA29FB928A179CB00BAA147 /* SwiftUIPictureInPictureInPictureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA29FB828A179CB00BAA147 /* SwiftUIPictureInPictureInPictureView.swift */; }; DCA29FBA28A179CB00BAA147 /* SwiftUIPictureInPictureInPictureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA29FB828A179CB00BAA147 /* SwiftUIPictureInPictureInPictureView.swift */; }; - DCA29FBC28A19E0F00BAA147 /* ExampleSampleBufferView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA29FBB28A19E0F00BAA147 /* ExampleSampleBufferView.swift */; }; - DCA29FBD28A19E0F00BAA147 /* ExampleSampleBufferView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA29FBB28A19E0F00BAA147 /* ExampleSampleBufferView.swift */; }; - DCA29FBF28A1B66C00BAA147 /* TwilioVideo.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCA29FBE28A1B66C00BAA147 /* TwilioVideo.xcframework */; }; - DCA29FC028A1B66C00BAA147 /* TwilioVideo.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DCA29FBE28A1B66C00BAA147 /* TwilioVideo.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; DCA53949235F53E700CA26FB /* InternalAuthStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA53947235F53E700CA26FB /* InternalAuthStore.swift */; }; DCA5394A235F53E700CA26FB /* InternalAuthStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA53947235F53E700CA26FB /* InternalAuthStore.swift */; }; DCA5394D235F740200CA26FB /* AuthStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA5394B235F740200CA26FB /* AuthStore.swift */; }; @@ -446,13 +448,24 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - DCA29FC128A1B66C00BAA147 /* Embed Frameworks */ = { + DC5E3DA128B6D21C009E3CF2 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( - DCA29FC028A1B66C00BAA147 /* TwilioVideo.xcframework in Embed Frameworks */, + DC5E3DA028B6D21B009E3CF2 /* TwilioVideo.xcframework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; + DC777A5C28B0272500FD9925 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + DC777A5B28B0272500FD9925 /* TwilioVideo.xcframework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -521,6 +534,7 @@ DC44C2E923876B4700205174 /* EditTextSegueSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditTextSegueSender.swift; sourceTree = ""; }; DC4568852385D5490069BB5A /* ViewControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewControllerFactory.swift; sourceTree = ""; }; DC4E4E1E23577EA700C5D313 /* AppSettingsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettingsStore.swift; sourceTree = ""; }; + DC4F28BD28B80EDB00112FA8 /* PIPPlaceholderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PIPPlaceholderView.swift; sourceTree = ""; }; DC5CC38D249C0EC000355CC6 /* Video-InternalUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Video-InternalUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; DC5CC391249C0EC000355CC6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; DC6EABBE2357CAC50064E9E0 /* MockAppSettingsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockAppSettingsStore.swift; sourceTree = ""; }; @@ -571,7 +585,6 @@ DC9C7731250043A600AC68FD /* RemoteConfigStoreSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteConfigStoreSpec.swift; sourceTree = ""; }; DCA29FB528A1769A00BAA147 /* PictureInPictureViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PictureInPictureViewController.swift; sourceTree = ""; }; DCA29FB828A179CB00BAA147 /* SwiftUIPictureInPictureInPictureView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIPictureInPictureInPictureView.swift; sourceTree = ""; }; - DCA29FBB28A19E0F00BAA147 /* ExampleSampleBufferView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleSampleBufferView.swift; sourceTree = ""; }; DCA29FBE28A1B66C00BAA147 /* TwilioVideo.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = TwilioVideo.xcframework; path = "../../twilio-video-ios-internal/temp/Artifacts/Debug/Dynamic/TwilioVideo.xcframework"; sourceTree = ""; }; DCA53947235F53E700CA26FB /* InternalAuthStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternalAuthStore.swift; sourceTree = ""; }; DCA5394B235F740200CA26FB /* AuthStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthStore.swift; sourceTree = ""; }; @@ -708,7 +721,7 @@ DC699C97282EB231009D3C1A /* FirebaseAnalytics in Frameworks */, DC699C7C282DB045009D3C1A /* FirebaseAuth in Frameworks */, DCB3BAD0282DA20000AF7072 /* Alamofire in Frameworks */, - DCA29FBF28A1B66C00BAA147 /* TwilioVideo.xcframework in Frameworks */, + DC777A5A28B0272500FD9925 /* TwilioVideo.xcframework in Frameworks */, DC699C7E282DB045009D3C1A /* FirebaseCrashlytics in Frameworks */, DCB3BADB282DA30B00AF7072 /* KeychainAccess in Frameworks */, ); @@ -723,6 +736,7 @@ DCB3BAD3282DA23200AF7072 /* Alamofire in Frameworks */, DC699C82282DB084009D3C1A /* FirebaseCrashlytics in Frameworks */, DCB3BADD282DA31400AF7072 /* KeychainAccess in Frameworks */, + DC5E3D9F28B6D21B009E3CF2 /* TwilioVideo.xcframework in Frameworks */, DC699C84282DB08B009D3C1A /* FirebaseAuth in Frameworks */, DC699C80282DB07D009D3C1A /* FirebaseAnalyticsSwift in Frameworks */, ); @@ -1228,7 +1242,7 @@ children = ( DCA29FB528A1769A00BAA147 /* PictureInPictureViewController.swift */, DCA29FB828A179CB00BAA147 /* SwiftUIPictureInPictureInPictureView.swift */, - DCA29FBB28A19E0F00BAA147 /* ExampleSampleBufferView.swift */, + DC4F28BD28B80EDB00112FA8 /* PIPPlaceholderView.swift */, ); path = PictureInPicture; sourceTree = ""; @@ -1602,7 +1616,7 @@ 4B0012231FBA52C4004A587E /* Sources */, 4B0012351FBA52C4004A587E /* Frameworks */, 4B0012401FBA52C4004A587E /* Resources */, - DCA29FC128A1B66C00BAA147 /* Embed Frameworks */, + DC777A5C28B0272500FD9925 /* Embed Frameworks */, ); buildRules = ( ); @@ -1629,6 +1643,7 @@ 4B0012601FBA52E5004A587E /* Sources */, 4B0012721FBA52E5004A587E /* Frameworks */, 4B00127D1FBA52E5004A587E /* Resources */, + DC5E3DA128B6D21C009E3CF2 /* Embed Frameworks */, ); buildRules = ( ); @@ -1927,6 +1942,7 @@ DCAEBF40238444D500141D2D /* SelectOptionViewModel.swift in Sources */, DCE1AA1927C7E58A004680CA /* GalleryLayoutView.swift in Sources */, DCEC3D1C23861E1700EBDEBF /* SelectVideoCodecViewModelFactory.swift in Sources */, + DC4F28BE28B80EDB00112FA8 /* PIPPlaceholderView.swift in Sources */, DCE1A9FF27C7E556004680CA /* Color.swift in Sources */, DCF4615223859BA400DD8FEA /* AppInfo.swift in Sources */, DC9A5E2E2462153000E4B079 /* CameraManager.swift in Sources */, @@ -2006,7 +2022,6 @@ DCC7E36224082EB300431AC3 /* AuthError.swift in Sources */, DCAEBF1D2383342C00141D2D /* DestructiveButtonCell.swift in Sources */, DCE1AA2F27C7E58A004680CA /* GalleryLayoutViewModel.swift in Sources */, - DCA29FBC28A19E0F00BAA147 /* ExampleSampleBufferView.swift in Sources */, DC9A5E5424638A9C00E4B079 /* ShowError.swift in Sources */, DCEC3D0B238608B500EBDEBF /* EditUserIdentityViewModelFactory.swift in Sources */, DCD7674C23DA336600A2939C /* DeepLink.swift in Sources */, @@ -2101,6 +2116,7 @@ DCE1AA1A27C7E58A004680CA /* GalleryLayoutView.swift in Sources */, DCEC3D1D23861E1700EBDEBF /* SelectVideoCodecViewModelFactory.swift in Sources */, DCE1AA0027C7E556004680CA /* Color.swift in Sources */, + DC4F28BF28B80EDB00112FA8 /* PIPPlaceholderView.swift in Sources */, DCF4615323859BA400DD8FEA /* AppInfo.swift in Sources */, DC9A5E2F2462153000E4B079 /* CameraManager.swift in Sources */, DCF4615B2385A51700DD8FEA /* SettingsViewController.swift in Sources */, @@ -2180,7 +2196,6 @@ DCC7E36324082EB300431AC3 /* AuthError.swift in Sources */, DCAEBF1E2383342C00141D2D /* DestructiveButtonCell.swift in Sources */, DCE1AA3027C7E58A004680CA /* GalleryLayoutViewModel.swift in Sources */, - DCA29FBD28A19E0F00BAA147 /* ExampleSampleBufferView.swift in Sources */, DC9A5E5524638A9C00E4B079 /* ShowError.swift in Sources */, DCEC3D0C238608B500EBDEBF /* EditUserIdentityViewModelFactory.swift in Sources */, DCD7674D23DA336600A2939C /* DeepLink.swift in Sources */, @@ -2506,7 +2521,7 @@ "$(inherited)", ); INFOPLIST_FILE = VideoApp/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.5; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2542,7 +2557,7 @@ FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; INFOPLIST_FILE = VideoApp/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.5; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/VideoApp/VideoApp/PictureInPicture/ExampleSampleBufferView.swift b/VideoApp/VideoApp/PictureInPicture/ExampleSampleBufferView.swift deleted file mode 100644 index c2678e5e..00000000 --- a/VideoApp/VideoApp/PictureInPicture/ExampleSampleBufferView.swift +++ /dev/null @@ -1,278 +0,0 @@ -// -// ExampleSampleBufferView.swift -// VideoApp -// -// Created by Tim Rozum on 8/8/22. -// Copyright © 2022 Twilio, Inc. All rights reserved. -// - -import AVFoundation -import Foundation -import UIKit -import TwilioVideo - -protocol ExampleSampleBufferRendererDelegate { - func bufferViewVideoChanged(view: ExampleSampleBufferView, - dimensions: CMVideoDimensions, - orientation: VideoOrientation) -} - -class ExampleSampleBufferView : UIView, VideoRenderer { - - public var videoDimensions: CMVideoDimensions - public var videoOrientation: VideoOrientation - - var isRendering = UIApplication.shared.applicationState != .background - var outputFormatDescription: CMFormatDescription? - // Allows the renderer to enqueue frames from a background thread without accessing self.layer directly. - var cachedDisplayLayer : AVSampleBufferDisplayLayer? - - /* - * Register pixel formats that are known to work with AVSampleBufferDisplayLayer. - * At a minimum, the CVPixelBuffers are expected to be backed by an IOSurface so we will not support - * every possible input from CoreVideo. - */ - var optionalPixelFormats: [NSNumber] = [NSNumber.init(value: PixelFormat.formatYUV420BiPlanarFullRange.rawValue), - NSNumber.init(value: PixelFormat.formatYUV420BiPlanarVideoRange.rawValue), - NSNumber.init(value: PixelFormat.format32BGRA.rawValue), - NSNumber.init(value: PixelFormat.format32ARGB.rawValue)] - - required init?(coder aDecoder: NSCoder) { - // This example does not support storyboards. - assert(false, "Unsupported.") - return nil - } - - override init(frame: CGRect) { - videoDimensions = CMVideoDimensions(width: 0, height: 0) - videoOrientation = VideoOrientation.up - - super.init(frame: frame) - - backgroundColor = .black - - cachedDisplayLayer = super.layer as? AVSampleBufferDisplayLayer -// let center = NotificationCenter.default -// -// center.addObserver(self, selector: #selector(ExampleSampleBufferView.willEnterForeground), -// name: UIApplication.willEnterForegroundNotification, object: nil) -// center.addObserver(self, selector: #selector(ExampleSampleBufferView.didEnterBackground), -// name: UIApplication.didEnterBackgroundNotification, object: nil) -// center.addObserver(self, selector: #selector(ExampleSampleBufferView.willResignActive), -// name: UIApplication.willResignActiveNotification, object: nil) -// [_sampleView addObserver:self forKeyPath:@"layer.status" options:NSKeyValueObservingOptionNew context:NULL]; - } - - deinit { - outputFormatDescription = nil - - NotificationCenter.default.removeObserver(self) - -// [self.sampleView removeObserver:self forKeyPath:@"layer.status"]; - } - - override class var layerClass: AnyClass { - AVSampleBufferDisplayLayer.self - } - - var displayLayer: AVSampleBufferDisplayLayer { - layer as! AVSampleBufferDisplayLayer - } - - override var contentMode: UIView.ContentMode { - get { - return super.contentMode - } - set { - // Map UIViewContentMode to AVLayerVideoGravity. The layer supports a subset of possible content modes. - switch newValue { - case .scaleAspectFill: - displayLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill - case .scaleAspectFit: - displayLayer.videoGravity = AVLayerVideoGravity.resizeAspect - case .scaleToFill: - displayLayer.videoGravity = AVLayerVideoGravity.resize - default: - displayLayer.videoGravity = AVLayerVideoGravity.resize - } - setNeedsLayout() - - super.contentMode = newValue - } - } - - override func didMoveToSuperview() { - guard let superview = superview else { return } - - translatesAutoresizingMaskIntoConstraints = false - let constraints = [ - leadingAnchor.constraint(equalTo: superview.leadingAnchor), - trailingAnchor.constraint(equalTo: superview.trailingAnchor), - topAnchor.constraint(equalTo: superview.topAnchor), - bottomAnchor.constraint(equalTo: superview.bottomAnchor) - ] - NSLayoutConstraint.activate(constraints) - - print("TCR constraints: \(constraints)") - } -} - -extension ExampleSampleBufferView { - @objc func willEnterForeground(_: NSNotification) { - - if (displayLayer.status == AVQueuedSampleBufferRenderingStatus.failed) { - // TODO: Restore failed sample buffer view. AVErrorOperationInterrupted. - } - - isRendering = true - } - - @objc func didEnterBackground(_: NSNotification) { - isRendering = false - displayLayer.flushAndRemoveImage() - } - - @objc func willResignActive(_: NSNotification) { - // TODO: - Should we stop rendering when resigning active? - // AVSampleBufferDisplayLayer seems capable of handling this case. - } -} - -extension ExampleSampleBufferView { - - func renderFrame(_ frame: VideoFrame) { - -// print("renderFrame") - let pixelFormat = CVPixelBufferGetPixelFormatType(frame.imageBuffer) - - // Unfortunately I420 is not directly supported by AVSampleBufferDisplayLayer. - // This example renderer does not attempt to support I420, but please see ExampleVideoRecorder for an example of - // performing an I420 to NV12 format conversion. Doing so efficiently for a renderer would require maintaining - // a CVPixelBufferPool of NV12 frames which are ready to be displayed. -// if (self.isRendering == false ) { -// return -// } else - if (pixelFormat == PixelFormat.formatYUV420PlanarFullRange.rawValue || - pixelFormat == PixelFormat.formatYUV420PlanarVideoRange.rawValue) { - print("Unsupported I420 pixel format!"); - return - } - - // Enqueuing a frame to AVSampleDisplayLayer may cause UIKit related accesses if the resolution has changed. - // When a format change occurs ensure that we synchronize with the main queue to deliver the first frame. -// if (detectFormatChange(imageBuffer: frame.imageBuffer) && !Thread.isMainThread) { -// DispatchQueue.main.sync { -// self.enqueueFrame(frame: frame) -// } -// } else { -// enqueueFrame(frame: frame) -// } - DispatchQueue.main.sync { - _ = self.detectFormatChange(imageBuffer: frame.imageBuffer) - self.enqueueFrame(frame: frame) - } - } - - func updateVideoSize(_ videoSize: CMVideoDimensions, orientation: VideoOrientation) { - DispatchQueue.main.async { - // Update properties to help with View layout. - let orientationChanged = orientation != self.videoOrientation - let animate = orientationChanged && (videoSize.width == self.videoDimensions.width && videoSize.height == self.videoDimensions.height) - self.videoDimensions = videoSize - self.videoOrientation = orientation - - // TODO: Should we be doing this here, or delegating to a view controller? - UIView.animate(withDuration: animate ? 0.3 : 0, animations: { - let size = videoSize - let scaleFactor = size.height > size.width ? CGFloat(size.height) / CGFloat(size.width) : CGFloat(size.width) / CGFloat(size.height) - switch (orientation) { - case .up: - self.transform = CGAffineTransform.identity; - break - case .left: - let scale = CGAffineTransform.init(scaleX: scaleFactor, - y: scaleFactor) - self.transform = CGAffineTransform(rotationAngle: CGFloat.pi / 2).concatenating(scale) - break - case .down: - self.transform = CGAffineTransform(rotationAngle: CGFloat.pi) - break - case .right: - let scale = CGAffineTransform.init(scaleX: scaleFactor, - y: scaleFactor) - self.transform = CGAffineTransform(rotationAngle: CGFloat.pi * 3 / 2).concatenating(scale) - break - @unknown default: - fatalError() - } - }) - } - } - - func detectFormatChange(imageBuffer: CVPixelBuffer) -> Bool { - var didChange = false - if (self.outputFormatDescription == nil || - CMVideoFormatDescriptionMatchesImageBuffer(self.outputFormatDescription!, imageBuffer: imageBuffer) == false) { - let status = CMVideoFormatDescriptionCreateForImageBuffer(allocator: kCFAllocatorDefault, - imageBuffer: imageBuffer, - formatDescriptionOut: &self.outputFormatDescription) - - if let format = self.outputFormatDescription { - let dimensions = CMVideoFormatDescriptionGetDimensions(format) - let pixelFormat = CVPixelBufferGetPixelFormatType(imageBuffer) - let utf16 = [ - UInt16((pixelFormat >> 24) & 0xFF), - UInt16((pixelFormat >> 16) & 0xFF), - UInt16((pixelFormat >> 8) & 0xFF), - UInt16((pixelFormat & 0xFF)) ] - let pixelFormatString = String(utf16CodeUnits: utf16, count: 4) - print("Detected format change: \(dimensions.width) x \(dimensions.height) - \(pixelFormatString)") - didChange = true - } else { - print("Failed to create output format description with status: \(status)") - } - } - return didChange - } - - // TODO: Return OSStatus? - func enqueueFrame(frame: VideoFrame) { - let imageBuffer = frame.imageBuffer - - if (self.cachedDisplayLayer?.error != nil) { - return - } else if (self.cachedDisplayLayer?.isReadyForMoreMediaData == false) { - print("AVSampleBufferDisplayLayer is not ready for more frames."); - return - } - - // Use the frame's timestamp as the presentation timestamp. We will display immediately. - // Our uncompressed buffers do not need to be decoded. - var sampleTiming = CMSampleTimingInfo.init(duration: CMTime.invalid, - presentationTimeStamp: frame.timestamp, - decodeTimeStamp: CMTime.invalid) - - // Create a CMSampleBuffer - var sampleBuffer: CMSampleBuffer? - - let status = CMSampleBufferCreateReadyWithImageBuffer(allocator: kCFAllocatorDefault, - imageBuffer: imageBuffer, - formatDescription: self.outputFormatDescription!, - sampleTiming: &sampleTiming, - sampleBufferOut: &sampleBuffer) - - // Enqueue the frame for display via AVSampleBufferDisplayLayer. - if (status != kCVReturnSuccess) { - print("Couldn't create a SampleBuffer. Status=\(status)") - return - } else if let sampleBuffer = sampleBuffer, -// let displayLayer = cachedDisplayLayer, - let sampleAttachments = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, createIfNecessary: true) as NSArray? { - // Force immediate display of the buffer, since the renderer receives samples just in time. - let firstAttachment = sampleAttachments.firstObject as! NSMutableDictionary? - firstAttachment?[kCMSampleAttachmentKey_DisplayImmediately] = true - - displayLayer.enqueue(sampleBuffer) - } - } -} diff --git a/VideoApp/VideoApp/PictureInPicture/PIPPlaceholderView.swift b/VideoApp/VideoApp/PictureInPicture/PIPPlaceholderView.swift new file mode 100644 index 00000000..26c6cc0c --- /dev/null +++ b/VideoApp/VideoApp/PictureInPicture/PIPPlaceholderView.swift @@ -0,0 +1,58 @@ +// +// PIPPlaceholderView.swift +// VideoApp +// +// Created by Tim Rozum on 8/25/22. +// Copyright © 2022 Twilio, Inc. All rights reserved. +// + +import UIKit + +class PIPPlaceholderView: UIView { + let label = UILabel() + private var count = 0 + private var timer: Timer? + + required init?(coder aDecoder: NSCoder) { + // This example does not support storyboards. + assert(false, "Unsupported.") + return nil + } + + override init(frame: CGRect) { + super.init(frame: frame) + + backgroundColor = .green + + addSubview(label) + + timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in + self.count += 1 + self.label.text = "Hello \(self.count)" + } + } + + override func didMoveToSuperview() { + guard let superview = superview else { return } + + translatesAutoresizingMaskIntoConstraints = false + let constraints = [ + leadingAnchor.constraint(equalTo: superview.leadingAnchor), + trailingAnchor.constraint(equalTo: superview.trailingAnchor), + topAnchor.constraint(equalTo: superview.topAnchor), + bottomAnchor.constraint(equalTo: superview.bottomAnchor, constant: -200) + ] + NSLayoutConstraint.activate(constraints) + + label.translatesAutoresizingMaskIntoConstraints = false + let labelConstraints = [ + leadingAnchor.constraint(equalTo: label.leadingAnchor), + trailingAnchor.constraint(equalTo: label.trailingAnchor), + topAnchor.constraint(equalTo: label.topAnchor), + bottomAnchor.constraint(equalTo: label.bottomAnchor) + ] + NSLayoutConstraint.activate(labelConstraints) + + + } +} diff --git a/VideoApp/VideoApp/PictureInPicture/PictureInPictureViewController.swift b/VideoApp/VideoApp/PictureInPicture/PictureInPictureViewController.swift index 9ac1b6f4..5ab4fd19 100644 --- a/VideoApp/VideoApp/PictureInPicture/PictureInPictureViewController.swift +++ b/VideoApp/VideoApp/PictureInPicture/PictureInPictureViewController.swift @@ -9,6 +9,7 @@ import AVKit import Combine import UIKit +import TwilioVideo class PictureInPictureViewController: UIViewController { @IBOutlet weak var videoView: VideoTrackStoringVideoView! @@ -24,11 +25,8 @@ class PictureInPictureViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() -// let sampleBufferVideoCallView = ExampleSampleBufferView( -// frame: CGRect(x: 0, y: 0, width: 100, height: 200) -// ) - let sampleBufferVideoCallView = ExampleSampleBufferView() + let sampleBufferVideoCallView = SampleBufferVideoView() sampleBufferVideoCallView.contentMode = .scaleAspectFit @@ -42,6 +40,9 @@ class PictureInPictureViewController: UIViewController { // pipVideoCallViewController.preferredContentSize = CGSize(width: 1080, height: 1920) pipVideoCallViewController.view.addSubview(sampleBufferVideoCallView) + + let placeholderView = PIPPlaceholderView() + pipVideoCallViewController.view.addSubview(placeholderView) // sampleBufferVideoCallView.translatesAutoresizingMaskIntoConstraints = false // let constraints = [ diff --git a/VideoApp/VideoApp/SwiftUI/Screens/MediaSetup/MediaSetupView.swift b/VideoApp/VideoApp/SwiftUI/Screens/MediaSetup/MediaSetupView.swift index 8c62c682..c47009ab 100644 --- a/VideoApp/VideoApp/SwiftUI/Screens/MediaSetup/MediaSetupView.swift +++ b/VideoApp/VideoApp/SwiftUI/Screens/MediaSetup/MediaSetupView.swift @@ -32,10 +32,10 @@ struct MediaSetupView: View { Text("Join " + roomName) .font(.title2) - ParticipantView(viewModel: $viewModel.participant) - .aspectRatio(1, contentMode: .fit) - .padding(.horizontal, 70) - .padding(.vertical) +// ParticipantView(viewModel: $viewModel.participant) +// .aspectRatio(1, contentMode: .fit) +// .padding(.horizontal, 70) +// .padding(.vertical) HStack { Spacer() From 70611f7b6abc2cff5f2968d00c5fb4644ae051ec Mon Sep 17 00:00:00 2001 From: Tim Rozum Date: Fri, 2 Sep 2022 09:36:06 -0600 Subject: [PATCH 3/8] Make pip work with room view --- VideoApp/VideoApp.xcodeproj/project.pbxproj | 6 + .../PictureInPicture/PIPPlaceholderView.swift | 22 ++- .../PictureInPictureSourceView.swift | 143 ++++++++++++++++++ .../Screens/MediaSetup/MediaSetupView.swift | 8 +- .../SwiftUI/Screens/Room/RoomView.swift | 4 + 5 files changed, 166 insertions(+), 17 deletions(-) create mode 100644 VideoApp/VideoApp/PictureInPicture/PictureInPictureSourceView.swift diff --git a/VideoApp/VideoApp.xcodeproj/project.pbxproj b/VideoApp/VideoApp.xcodeproj/project.pbxproj index 77c4a8db..7ecc275a 100644 --- a/VideoApp/VideoApp.xcodeproj/project.pbxproj +++ b/VideoApp/VideoApp.xcodeproj/project.pbxproj @@ -67,6 +67,8 @@ DC1D3CBA27CEC100001FCB1A /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC1D3CB827CEC100001FCB1A /* SettingsView.swift */; }; DC1D3CBC27CFD459001FCB1A /* HideKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC1D3CBB27CFD458001FCB1A /* HideKeyboard.swift */; }; DC1D3CBD27CFD459001FCB1A /* HideKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC1D3CBB27CFD458001FCB1A /* HideKeyboard.swift */; }; + DC243F8028C13D4200100C6B /* PictureInPictureSourceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC243F7F28C13D4200100C6B /* PictureInPictureSourceView.swift */; }; + DC243F8128C13D4200100C6B /* PictureInPictureSourceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC243F7F28C13D4200100C6B /* PictureInPictureSourceView.swift */; }; DC2511AC2478213400C776D6 /* EditMaxSubscriptionBitrateViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC2511AA2478213400C776D6 /* EditMaxSubscriptionBitrateViewModel.swift */; }; DC2511AD2478213400C776D6 /* EditMaxSubscriptionBitrateViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC2511AA2478213400C776D6 /* EditMaxSubscriptionBitrateViewModel.swift */; }; DC39248A2416A1D80068E33F /* SelectEnvironmentViewModelFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3924872416A1D80068E33F /* SelectEnvironmentViewModelFactory.swift */; }; @@ -518,6 +520,7 @@ DC1D3CB427CE9787001FCB1A /* RoomViewDependencyWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomViewDependencyWrapper.swift; sourceTree = ""; }; DC1D3CB827CEC100001FCB1A /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; DC1D3CBB27CFD458001FCB1A /* HideKeyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HideKeyboard.swift; sourceTree = ""; }; + DC243F7F28C13D4200100C6B /* PictureInPictureSourceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PictureInPictureSourceView.swift; sourceTree = ""; }; DC2511AA2478213400C776D6 /* EditMaxSubscriptionBitrateViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditMaxSubscriptionBitrateViewModel.swift; sourceTree = ""; }; DC338A2724367AEC0093E855 /* Unit.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = Unit.xctestplan; sourceTree = ""; }; DC338A2A2437E7380093E855 /* UI.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UI.xctestplan; sourceTree = ""; }; @@ -1243,6 +1246,7 @@ DCA29FB528A1769A00BAA147 /* PictureInPictureViewController.swift */, DCA29FB828A179CB00BAA147 /* SwiftUIPictureInPictureInPictureView.swift */, DC4F28BD28B80EDB00112FA8 /* PIPPlaceholderView.swift */, + DC243F7F28C13D4200100C6B /* PictureInPictureSourceView.swift */, ); path = PictureInPicture; sourceTree = ""; @@ -1960,6 +1964,7 @@ DC44C2E723876AFB00205174 /* SelectOptionSegueSender.swift in Sources */, DCB7C18F2405C0BE009DEA70 /* KeychainStorage.swift in Sources */, DC44C2E323875C4F00205174 /* StringHelpers.swift in Sources */, + DC243F8028C13D4200100C6B /* PictureInPictureSourceView.swift in Sources */, DCA29FB628A1769B00BAA147 /* PictureInPictureViewController.swift in Sources */, DCE1AA2127C7E58A004680CA /* SpeakerLayoutView.swift in Sources */, 8A206F40263919CA00EFCD97 /* SelectClientTrackSwitchOffControlViewModelFactory.swift in Sources */, @@ -2134,6 +2139,7 @@ DCB7C1902405C0BE009DEA70 /* KeychainStorage.swift in Sources */, DC44C2E423875C4F00205174 /* StringHelpers.swift in Sources */, DCE1AA2227C7E58A004680CA /* SpeakerLayoutView.swift in Sources */, + DC243F8128C13D4200100C6B /* PictureInPictureSourceView.swift in Sources */, DCA29FB728A1769B00BAA147 /* PictureInPictureViewController.swift in Sources */, 8A206F41263919CA00EFCD97 /* SelectClientTrackSwitchOffControlViewModelFactory.swift in Sources */, DCF4614E238598C700DD8FEA /* SettingsViewModel.swift in Sources */, diff --git a/VideoApp/VideoApp/PictureInPicture/PIPPlaceholderView.swift b/VideoApp/VideoApp/PictureInPicture/PIPPlaceholderView.swift index 26c6cc0c..7a387d69 100644 --- a/VideoApp/VideoApp/PictureInPicture/PIPPlaceholderView.swift +++ b/VideoApp/VideoApp/PictureInPicture/PIPPlaceholderView.swift @@ -10,26 +10,20 @@ import UIKit class PIPPlaceholderView: UIView { let label = UILabel() - private var count = 0 - private var timer: Timer? required init?(coder aDecoder: NSCoder) { - // This example does not support storyboards. - assert(false, "Unsupported.") - return nil + fatalError() } override init(frame: CGRect) { super.init(frame: frame) - backgroundColor = .green + backgroundColor = UIColor(named: "BackgroundStronger") addSubview(label) - timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in - self.count += 1 - self.label.text = "Hello \(self.count)" - } + label.textColor = .white + label.textAlignment = .center } override func didMoveToSuperview() { @@ -40,7 +34,7 @@ class PIPPlaceholderView: UIView { leadingAnchor.constraint(equalTo: superview.leadingAnchor), trailingAnchor.constraint(equalTo: superview.trailingAnchor), topAnchor.constraint(equalTo: superview.topAnchor), - bottomAnchor.constraint(equalTo: superview.bottomAnchor, constant: -200) + bottomAnchor.constraint(equalTo: superview.bottomAnchor) ] NSLayoutConstraint.activate(constraints) @@ -52,7 +46,9 @@ class PIPPlaceholderView: UIView { bottomAnchor.constraint(equalTo: label.bottomAnchor) ] NSLayoutConstraint.activate(labelConstraints) - - + } + + func configure(particiipant: ParticipantViewModel) { + label.text = particiipant.displayName } } diff --git a/VideoApp/VideoApp/PictureInPicture/PictureInPictureSourceView.swift b/VideoApp/VideoApp/PictureInPicture/PictureInPictureSourceView.swift new file mode 100644 index 00000000..89c42110 --- /dev/null +++ b/VideoApp/VideoApp/PictureInPicture/PictureInPictureSourceView.swift @@ -0,0 +1,143 @@ +// +// Copyright (C) 2022 Twilio, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SwiftUI + +/// A SwiftUI video view that is automatically removed from the video track when the view is no longer in use. +struct PictureInPictureSourceView: UIViewRepresentable { + @Binding var participant: ParticipantViewModel + + func makeUIView(context: Context) -> PictureInPictureSetupView { + let view = PictureInPictureSetupView() + view.configure(participant: participant) + return view + } + + func updateUIView(_ uiView: PictureInPictureSetupView, context: Context) { + uiView.configure(participant: participant) + } + + static func dismantleUIView(_ uiView: PictureInPictureSetupView, coordinator: ()) { + uiView.videoView.videoTrack?.removeRenderer(uiView.videoView) // TODO: Improve + } +} + + + + +import AVKit +import Combine +import TwilioVideo +import UIKit + +class PictureInPictureSetupView: UIView { + var videoView: VideoTrackStoringSampleBufferVideoView! + var placeholderView: PIPPlaceholderView! + private var pipController: AVPictureInPictureController! + private var pipVideoCallViewController: AVPictureInPictureVideoCallViewController! + + override init(frame: CGRect) { + super.init(frame: frame) + videoView = VideoTrackStoringSampleBufferVideoView() + + videoView.contentMode = .scaleAspectFill + + pipVideoCallViewController = AVPictureInPictureVideoCallViewController() + + // Pretty much just for aspect ratio, normally used for pop-over + pipVideoCallViewController.preferredContentSize = CGSize(width: 100, height: 150) + + placeholderView = PIPPlaceholderView() + pipVideoCallViewController.view.addSubview(placeholderView) + + pipVideoCallViewController.view.addSubview(videoView) + + let pipContentSource = AVPictureInPictureController.ContentSource( + activeVideoCallSourceView: self, + contentViewController: pipVideoCallViewController + ) + + pipController = AVPictureInPictureController(contentSource: pipContentSource) + pipController.canStartPictureInPictureAutomaticallyFromInline = true + pipController.delegate = self + } + + required init?(coder aDecoder: NSCoder) { + fatalError() + } + + func configure(participant: ParticipantViewModel) { + placeholderView.configure(particiipant: participant) + + videoView.videoTrack = participant.cameraTrack + videoView.alpha = participant.isCameraTrackSwitchedOff || participant.cameraTrack == nil ? 0 : 1 + } +} + +extension PictureInPictureSetupView: AVPictureInPictureControllerDelegate { + func pictureInPictureControllerWillStartPictureInPicture( + _ pictureInPictureController: AVPictureInPictureController + ) { + print("pip controller delegate: will start") + } + + func pictureInPictureControllerDidStartPictureInPicture( + _ pictureInPictureController: AVPictureInPictureController + ) { + print("pip controller delegate: did start") + } + + func pictureInPictureController( + _ pictureInPictureController: AVPictureInPictureController, + failedToStartPictureInPictureWithError error: Error + ) { + print("pip controller delegate: failed to start \(error)") + } + + func pictureInPictureControllerWillStopPictureInPicture( + _ pictureInPictureController: AVPictureInPictureController + ) { + print("pip controller delegate: will stop") + } + + func pictureInPictureControllerDidStopPictureInPicture( + _ pictureInPictureController: AVPictureInPictureController + ) { + print("pip controller delegate: did stop") + } + + func pictureInPictureController( + _ pictureInPictureController: AVPictureInPictureController, + restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void + ) { + print("pip controller delegate: restore UI") + } +} + +// TODO: Move to the SDK +class VideoTrackStoringSampleBufferVideoView: SampleBufferVideoView { + var videoTrack: VideoTrack? { + didSet { + guard oldValue != videoTrack else { return } + + oldValue?.removeRenderer(self) + + if let videoTrack = videoTrack { + videoTrack.addRenderer(self) + } + } + } +} diff --git a/VideoApp/VideoApp/SwiftUI/Screens/MediaSetup/MediaSetupView.swift b/VideoApp/VideoApp/SwiftUI/Screens/MediaSetup/MediaSetupView.swift index c47009ab..8c62c682 100644 --- a/VideoApp/VideoApp/SwiftUI/Screens/MediaSetup/MediaSetupView.swift +++ b/VideoApp/VideoApp/SwiftUI/Screens/MediaSetup/MediaSetupView.swift @@ -32,10 +32,10 @@ struct MediaSetupView: View { Text("Join " + roomName) .font(.title2) -// ParticipantView(viewModel: $viewModel.participant) -// .aspectRatio(1, contentMode: .fit) -// .padding(.horizontal, 70) -// .padding(.vertical) + ParticipantView(viewModel: $viewModel.participant) + .aspectRatio(1, contentMode: .fit) + .padding(.horizontal, 70) + .padding(.vertical) HStack { Spacer() diff --git a/VideoApp/VideoApp/SwiftUI/Screens/Room/RoomView.swift b/VideoApp/VideoApp/SwiftUI/Screens/Room/RoomView.swift index bde26b30..3449c71b 100644 --- a/VideoApp/VideoApp/SwiftUI/Screens/Room/RoomView.swift +++ b/VideoApp/VideoApp/SwiftUI/Screens/Room/RoomView.swift @@ -19,6 +19,7 @@ import SwiftUI /// Room screen that is shown when a user connects to a video room. struct RoomView: View { @EnvironmentObject var viewModel: RoomViewModel + @EnvironmentObject var speakerLayoutViewModel: SpeakerLayoutViewModel @Environment(\.presentationMode) var presentationMode @Environment(\.horizontalSizeClass) var horizontalSizeClass @Environment(\.verticalSizeClass) var verticalSizeClass @@ -34,6 +35,9 @@ struct RoomView: View { GeometryReader { geometry in ZStack { Color.roomBackground.ignoresSafeArea() + + // TODO: Improve layout? + PictureInPictureSourceView(participant: $speakerLayoutViewModel.dominantSpeaker) VStack(spacing: 0) { VStack(spacing: 0) { From 5f87cc933367ae6180002595a89e00f54bf59f74 Mon Sep 17 00:00:00 2001 From: Tim Rozum Date: Wed, 14 Sep 2022 09:15:47 -0600 Subject: [PATCH 4/8] Make layout changes since the SDK view no longer handles layout---probably not working yet --- VideoApp/VideoApp.xcodeproj/project.pbxproj | 12 +++++- .../PictureInPicture/PIPPlaceholderView.swift | 37 +++++++++++++++++++ .../PictureInPictureSourceView.swift | 13 ++++++- .../PictureInPictureViewController.swift | 0 ...SwiftUIPictureInPictureInPictureView.swift | 0 .../SwiftUI/Views/Core/SwiftUIVideoView.swift | 2 +- 6 files changed, 60 insertions(+), 4 deletions(-) rename VideoApp/VideoApp/PictureInPicture/{ => Test}/PictureInPictureViewController.swift (100%) rename VideoApp/VideoApp/PictureInPicture/{ => Test}/SwiftUIPictureInPictureInPictureView.swift (100%) diff --git a/VideoApp/VideoApp.xcodeproj/project.pbxproj b/VideoApp/VideoApp.xcodeproj/project.pbxproj index 7ecc275a..47b16e28 100644 --- a/VideoApp/VideoApp.xcodeproj/project.pbxproj +++ b/VideoApp/VideoApp.xcodeproj/project.pbxproj @@ -844,6 +844,15 @@ path = API; sourceTree = ""; }; + DC006CCD28D0E71300D234E5 /* Test */ = { + isa = PBXGroup; + children = ( + DCA29FB528A1769A00BAA147 /* PictureInPictureViewController.swift */, + DCA29FB828A179CB00BAA147 /* SwiftUIPictureInPictureInPictureView.swift */, + ); + path = Test; + sourceTree = ""; + }; DC04468F238F1E2D0072F597 /* User */ = { isa = PBXGroup; children = ( @@ -1243,10 +1252,9 @@ DCA29FB428A1765C00BAA147 /* PictureInPicture */ = { isa = PBXGroup; children = ( - DCA29FB528A1769A00BAA147 /* PictureInPictureViewController.swift */, - DCA29FB828A179CB00BAA147 /* SwiftUIPictureInPictureInPictureView.swift */, DC4F28BD28B80EDB00112FA8 /* PIPPlaceholderView.swift */, DC243F7F28C13D4200100C6B /* PictureInPictureSourceView.swift */, + DC006CCD28D0E71300D234E5 /* Test */, ); path = PictureInPicture; sourceTree = ""; diff --git a/VideoApp/VideoApp/PictureInPicture/PIPPlaceholderView.swift b/VideoApp/VideoApp/PictureInPicture/PIPPlaceholderView.swift index 7a387d69..d786bb54 100644 --- a/VideoApp/VideoApp/PictureInPicture/PIPPlaceholderView.swift +++ b/VideoApp/VideoApp/PictureInPicture/PIPPlaceholderView.swift @@ -52,3 +52,40 @@ class PIPPlaceholderView: UIView { label.text = particiipant.displayName } } + +import UIKit + +class PIPContainerView: UIView { + let placeholder = PIPPlaceholderView() + + required init?(coder aDecoder: NSCoder) { + fatalError() + } + + override init(frame: CGRect) { + super.init(frame: frame) + + addSubview(placeholder) + + } + + override func didMoveToSuperview() { + guard let superview = superview else { return } + + translatesAutoresizingMaskIntoConstraints = false + + let constraints = [ + leadingAnchor.constraint(equalTo: superview.leadingAnchor), + trailingAnchor.constraint(equalTo: superview.trailingAnchor), + topAnchor.constraint(equalTo: superview.topAnchor), + bottomAnchor.constraint(equalTo: superview.bottomAnchor) + ] + + NSLayoutConstraint.activate(constraints) + } + + func configure(particiipant: ParticipantViewModel) { + placeholder.configure(particiipant: particiipant) + + } +} diff --git a/VideoApp/VideoApp/PictureInPicture/PictureInPictureSourceView.swift b/VideoApp/VideoApp/PictureInPicture/PictureInPictureSourceView.swift index 89c42110..8985feb5 100644 --- a/VideoApp/VideoApp/PictureInPicture/PictureInPictureSourceView.swift +++ b/VideoApp/VideoApp/PictureInPicture/PictureInPictureSourceView.swift @@ -64,6 +64,17 @@ class PictureInPictureSetupView: UIView { pipVideoCallViewController.view.addSubview(placeholderView) pipVideoCallViewController.view.addSubview(videoView) + + videoView.translatesAutoresizingMaskIntoConstraints = false; + + let constraints = [ + videoView.leadingAnchor.constraint(equalTo: pipVideoCallViewController.view.leadingAnchor), + videoView.trailingAnchor.constraint(equalTo: pipVideoCallViewController.view.trailingAnchor), + videoView.topAnchor.constraint(equalTo: pipVideoCallViewController.view.topAnchor), + videoView.bottomAnchor.constraint(equalTo: pipVideoCallViewController.view.bottomAnchor) + ] + + NSLayoutConstraint.activate(constraints) let pipContentSource = AVPictureInPictureController.ContentSource( activeVideoCallSourceView: self, @@ -83,7 +94,7 @@ class PictureInPictureSetupView: UIView { placeholderView.configure(particiipant: participant) videoView.videoTrack = participant.cameraTrack - videoView.alpha = participant.isCameraTrackSwitchedOff || participant.cameraTrack == nil ? 0 : 1 +// videoView.alpha = participant.isCameraTrackSwitchedOff || participant.cameraTrack == nil ? 0 : 1 } } diff --git a/VideoApp/VideoApp/PictureInPicture/PictureInPictureViewController.swift b/VideoApp/VideoApp/PictureInPicture/Test/PictureInPictureViewController.swift similarity index 100% rename from VideoApp/VideoApp/PictureInPicture/PictureInPictureViewController.swift rename to VideoApp/VideoApp/PictureInPicture/Test/PictureInPictureViewController.swift diff --git a/VideoApp/VideoApp/PictureInPicture/SwiftUIPictureInPictureInPictureView.swift b/VideoApp/VideoApp/PictureInPicture/Test/SwiftUIPictureInPictureInPictureView.swift similarity index 100% rename from VideoApp/VideoApp/PictureInPicture/SwiftUIPictureInPictureInPictureView.swift rename to VideoApp/VideoApp/PictureInPicture/Test/SwiftUIPictureInPictureInPictureView.swift diff --git a/VideoApp/VideoApp/SwiftUI/Views/Core/SwiftUIVideoView.swift b/VideoApp/VideoApp/SwiftUI/Views/Core/SwiftUIVideoView.swift index 4fe92fc3..5ce5a39b 100644 --- a/VideoApp/VideoApp/SwiftUI/Views/Core/SwiftUIVideoView.swift +++ b/VideoApp/VideoApp/SwiftUI/Views/Core/SwiftUIVideoView.swift @@ -46,7 +46,7 @@ struct SwiftUIVideoView: UIViewRepresentable { /// /// It also provides a `VideoTrack` reference to `SwiftUIVideoView` so that `SwiftUIVideoView` can /// remove the `VideoView` from the `VideoTrack` when `dismantleUIView` is called.` -class VideoTrackStoringVideoView: VideoView { +class VideoTrackStoringVideoView: SampleBufferVideoView { var videoTrack: VideoTrack? { didSet { guard oldValue != videoTrack else { return } From aed0a936573123e606bb7619fea100a669f76543 Mon Sep 17 00:00:00 2001 From: dipankadas <38047911+dipankadas@users.noreply.github.com> Date: Tue, 18 Oct 2022 10:29:13 -0700 Subject: [PATCH 5/8] VIDEO-11292 fix pip integration (#218) --- VideoApp/VideoApp.xcodeproj/project.pbxproj | 40 +++++++++---------- .../xcshareddata/swiftpm/Package.resolved | 9 +++++ .../Test/PictureInPictureViewController.swift | 31 +++++--------- .../SwiftUI/Screens/Home/HomeView.swift | 1 + .../SwiftUI/Views/Core/SwiftUIVideoView.swift | 2 +- .../TwilioVideo/Room/RoomManager.swift | 2 +- 6 files changed, 42 insertions(+), 43 deletions(-) diff --git a/VideoApp/VideoApp.xcodeproj/project.pbxproj b/VideoApp/VideoApp.xcodeproj/project.pbxproj index 47b16e28..a4734738 100644 --- a/VideoApp/VideoApp.xcodeproj/project.pbxproj +++ b/VideoApp/VideoApp.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 24C5217A20FFAD030018E2D8 /* EmailPasswordLoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24C5217820FFAD030018E2D8 /* EmailPasswordLoginViewController.swift */; }; 24C5217B20FFAD030018E2D8 /* EmailPasswordLoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24C5217820FFAD030018E2D8 /* EmailPasswordLoginViewController.swift */; }; + 2FAF8BA028F7351600285505 /* TwilioVideo in Frameworks */ = {isa = PBXBuildFile; productRef = 2FAF8B9F28F7351600285505 /* TwilioVideo */; }; 4B0012261FBA52C4004A587E /* StatsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 24A723E21ECE1C8500486E7A /* StatsViewController.m */; }; 4B0012291FBA52C4004A587E /* TrackStatsTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 24A723EC1ECE1D2A00486E7A /* TrackStatsTableViewCell.m */; }; 4B00122C1FBA52C4004A587E /* StatsUIModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 24A723E61ECE1CBD00486E7A /* StatsUIModel.m */; }; @@ -128,8 +129,6 @@ DC5CC39F249C0F5900355CC6 /* TestSecrets.json in Resources */ = {isa = PBXBuildFile; fileRef = DC9A4C9123D161A200D37CEC /* TestSecrets.json */; }; DC5CC3A0249C0F5900355CC6 /* TestSecrets.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC0446A2239716860072F597 /* TestSecrets.swift */; }; DC5CC3A1249C0F5900355CC6 /* TestSecretsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC0446A0239716530072F597 /* TestSecretsStore.swift */; }; - DC5E3D9F28B6D21B009E3CF2 /* TwilioVideo.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCA29FBE28A1B66C00BAA147 /* TwilioVideo.xcframework */; }; - DC5E3DA028B6D21B009E3CF2 /* TwilioVideo.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DCA29FBE28A1B66C00BAA147 /* TwilioVideo.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; DC699C7C282DB045009D3C1A /* FirebaseAuth in Frameworks */ = {isa = PBXBuildFile; productRef = DC699C7B282DB045009D3C1A /* FirebaseAuth */; }; DC699C7E282DB045009D3C1A /* FirebaseCrashlytics in Frameworks */ = {isa = PBXBuildFile; productRef = DC699C7D282DB045009D3C1A /* FirebaseCrashlytics */; }; DC699C80282DB07D009D3C1A /* FirebaseAnalyticsSwift in Frameworks */ = {isa = PBXBuildFile; productRef = DC699C7F282DB07D009D3C1A /* FirebaseAnalyticsSwift */; }; @@ -145,8 +144,6 @@ DC699C97282EB231009D3C1A /* FirebaseAnalytics in Frameworks */ = {isa = PBXBuildFile; productRef = DC699C96282EB231009D3C1A /* FirebaseAnalytics */; }; DC76FD0224105500001B2FFC /* EnvironmentVariableStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC76FD00241054FC001B2FFC /* EnvironmentVariableStore.swift */; }; DC76FD0324105501001B2FFC /* EnvironmentVariableStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC76FD00241054FC001B2FFC /* EnvironmentVariableStore.swift */; }; - DC777A5A28B0272500FD9925 /* TwilioVideo.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCA29FBE28A1B66C00BAA147 /* TwilioVideo.xcframework */; }; - DC777A5B28B0272500FD9925 /* TwilioVideo.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DCA29FBE28A1B66C00BAA147 /* TwilioVideo.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; DC82A35023FC9AAA0081578C /* JSONContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC82A34E23FC9AAA0081578C /* JSONContainer.swift */; }; DC82A35123FC9AAA0081578C /* JSONContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC82A34E23FC9AAA0081578C /* JSONContainer.swift */; }; DC82A35423FDB6890081578C /* SDKLogLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC82A35223FDB6890081578C /* SDKLogLevel.swift */; }; @@ -456,18 +453,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - DC5E3DA028B6D21B009E3CF2 /* TwilioVideo.xcframework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; - DC777A5C28B0272500FD9925 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - DC777A5B28B0272500FD9925 /* TwilioVideo.xcframework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -490,6 +475,7 @@ 24A723EC1ECE1D2A00486E7A /* TrackStatsTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TrackStatsTableViewCell.m; sourceTree = ""; }; 24C5217720FFAD030018E2D8 /* Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = ""; }; 24C5217820FFAD030018E2D8 /* EmailPasswordLoginViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailPasswordLoginViewController.swift; sourceTree = ""; }; + 2FFFBC0628ECE5B000210E81 /* TwilioVideo.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = TwilioVideo.xcframework; path = "../../video-ios-internal/temp/Artifacts/Debug/Dynamic/TwilioVideo.xcframework"; sourceTree = ""; }; 4B00124E1FBA52C4004A587E /* Video-Internal.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Video-Internal.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 4B00128B1FBA52E5004A587E /* Video-Community.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Video-Community.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 8A206F0D263912B600EFCD97 /* ClientTrackSwitchOffControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientTrackSwitchOffControl.swift; sourceTree = ""; }; @@ -588,7 +574,6 @@ DC9C7731250043A600AC68FD /* RemoteConfigStoreSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteConfigStoreSpec.swift; sourceTree = ""; }; DCA29FB528A1769A00BAA147 /* PictureInPictureViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PictureInPictureViewController.swift; sourceTree = ""; }; DCA29FB828A179CB00BAA147 /* SwiftUIPictureInPictureInPictureView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIPictureInPictureInPictureView.swift; sourceTree = ""; }; - DCA29FBE28A1B66C00BAA147 /* TwilioVideo.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = TwilioVideo.xcframework; path = "../../twilio-video-ios-internal/temp/Artifacts/Debug/Dynamic/TwilioVideo.xcframework"; sourceTree = ""; }; DCA53947235F53E700CA26FB /* InternalAuthStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternalAuthStore.swift; sourceTree = ""; }; DCA5394B235F740200CA26FB /* AuthStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthStore.swift; sourceTree = ""; }; DCA5394F235F765D00CA26FB /* AuthFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthFlow.swift; sourceTree = ""; }; @@ -721,10 +706,10 @@ files = ( DCB3BAD6282DA2B300AF7072 /* AppCenterDistribute in Frameworks */, DC699C87282DB4E9009D3C1A /* GoogleSignIn in Frameworks */, + 2FAF8BA028F7351600285505 /* TwilioVideo in Frameworks */, DC699C97282EB231009D3C1A /* FirebaseAnalytics in Frameworks */, DC699C7C282DB045009D3C1A /* FirebaseAuth in Frameworks */, DCB3BAD0282DA20000AF7072 /* Alamofire in Frameworks */, - DC777A5A28B0272500FD9925 /* TwilioVideo.xcframework in Frameworks */, DC699C7E282DB045009D3C1A /* FirebaseCrashlytics in Frameworks */, DCB3BADB282DA30B00AF7072 /* KeychainAccess in Frameworks */, ); @@ -739,7 +724,6 @@ DCB3BAD3282DA23200AF7072 /* Alamofire in Frameworks */, DC699C82282DB084009D3C1A /* FirebaseCrashlytics in Frameworks */, DCB3BADD282DA31400AF7072 /* KeychainAccess in Frameworks */, - DC5E3D9F28B6D21B009E3CF2 /* TwilioVideo.xcframework in Frameworks */, DC699C84282DB08B009D3C1A /* FirebaseAuth in Frameworks */, DC699C80282DB07D009D3C1A /* FirebaseAnalyticsSwift in Frameworks */, ); @@ -1404,7 +1388,7 @@ DCB3BAD1282DA23200AF7072 /* Frameworks */ = { isa = PBXGroup; children = ( - DCA29FBE28A1B66C00BAA147 /* TwilioVideo.xcframework */, + 2FFFBC0628ECE5B000210E81 /* TwilioVideo.xcframework */, ); name = Frameworks; sourceTree = ""; @@ -1628,7 +1612,6 @@ 4B0012231FBA52C4004A587E /* Sources */, 4B0012351FBA52C4004A587E /* Frameworks */, 4B0012401FBA52C4004A587E /* Resources */, - DC777A5C28B0272500FD9925 /* Embed Frameworks */, ); buildRules = ( ); @@ -1643,6 +1626,7 @@ DC699C7D282DB045009D3C1A /* FirebaseCrashlytics */, DC699C86282DB4E9009D3C1A /* GoogleSignIn */, DC699C96282EB231009D3C1A /* FirebaseAnalytics */, + 2FAF8B9F28F7351600285505 /* TwilioVideo */, ); productName = VideoApp; productReference = 4B00124E1FBA52C4004A587E /* Video-Internal.app */; @@ -1797,6 +1781,7 @@ DC699C85282DB4E9009D3C1A /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */, DC699C8A282EAD46009D3C1A /* XCRemoteSwiftPackageReference "Quick" */, DC699C8F282EAE21009D3C1A /* XCRemoteSwiftPackageReference "Nimble" */, + 2FAF8B9E28F7351600285505 /* XCRemoteSwiftPackageReference "twilio-video-ios" */, ); productRefGroup = 241282381E36A6AB002198BE /* Products */; projectDirPath = ""; @@ -2832,6 +2817,14 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ + 2FAF8B9E28F7351600285505 /* XCRemoteSwiftPackageReference "twilio-video-ios" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "git@github.com:twilio/twilio-video-ios.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 5.0.0; + }; + }; DC699C78282DB045009D3C1A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/firebase/firebase-ios-sdk.git"; @@ -2891,6 +2884,11 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 2FAF8B9F28F7351600285505 /* TwilioVideo */ = { + isa = XCSwiftPackageProductDependency; + package = 2FAF8B9E28F7351600285505 /* XCRemoteSwiftPackageReference "twilio-video-ios" */; + productName = TwilioVideo; + }; DC699C7B282DB045009D3C1A /* FirebaseAuth */ = { isa = XCSwiftPackageProductDependency; package = DC699C78282DB045009D3C1A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; diff --git a/VideoApp/VideoApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/VideoApp/VideoApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 76196871..0c85da3a 100644 --- a/VideoApp/VideoApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/VideoApp/VideoApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -206,6 +206,15 @@ "revision" : "e1499bc69b9040b29184f7f2996f7bab467c1639", "version" : "1.19.0" } + }, + { + "identity" : "twilio-video-ios", + "kind" : "remoteSourceControl", + "location" : "git@github.com:twilio/twilio-video-ios.git", + "state" : { + "revision" : "4951033ac1469d80deb3c2313bc11a4d486fc610", + "version" : "5.2.1" + } } ], "version" : 2 diff --git a/VideoApp/VideoApp/PictureInPicture/Test/PictureInPictureViewController.swift b/VideoApp/VideoApp/PictureInPicture/Test/PictureInPictureViewController.swift index 5ab4fd19..6773a5f8 100644 --- a/VideoApp/VideoApp/PictureInPicture/Test/PictureInPictureViewController.swift +++ b/VideoApp/VideoApp/PictureInPicture/Test/PictureInPictureViewController.swift @@ -24,37 +24,28 @@ class PictureInPictureViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - - - let sampleBufferVideoCallView = SampleBufferVideoView() - + let sampleBufferVideoCallView = SampleBufferVideoView() sampleBufferVideoCallView.contentMode = .scaleAspectFit - - pipVideoCallViewController = AVPictureInPictureVideoCallViewController() // Pretty much just for aspect ratio, normally used for pop-over - pipVideoCallViewController.preferredContentSize = CGSize(width: 100, height: 200) -// pipVideoCallViewController.preferredContentSize = CGSize(width: 1080, height: 1920) + pipVideoCallViewController.preferredContentSize = CGSize(width: 1080, height: 1920) pipVideoCallViewController.view.addSubview(sampleBufferVideoCallView) - - let placeholderView = PIPPlaceholderView() - pipVideoCallViewController.view.addSubview(placeholderView) -// sampleBufferVideoCallView.translatesAutoresizingMaskIntoConstraints = false -// let constraints = [ -// sampleBufferVideoCallView.leadingAnchor.constraint(equalTo: view.leadingAnchor), -// sampleBufferVideoCallView.trailingAnchor.constraint(equalTo: view.trailingAnchor), -// sampleBufferVideoCallView.topAnchor.constraint(equalTo: view.topAnchor), -// sampleBufferVideoCallView.bottomAnchor.constraint(equalTo: view.bottomAnchor) -// ] -// NSLayoutConstraint.activate(constraints) + sampleBufferVideoCallView.translatesAutoresizingMaskIntoConstraints = false + let constraints = [ + sampleBufferVideoCallView.leadingAnchor.constraint(equalTo: pipVideoCallViewController.view.leadingAnchor), + sampleBufferVideoCallView.trailingAnchor.constraint(equalTo: pipVideoCallViewController.view.trailingAnchor), + sampleBufferVideoCallView.topAnchor.constraint(equalTo: pipVideoCallViewController.view.topAnchor), + sampleBufferVideoCallView.bottomAnchor.constraint(equalTo: pipVideoCallViewController.view.bottomAnchor) + ] + NSLayoutConstraint.activate(constraints) -// sampleBufferVideoCallView.bounds = pipVideoCallViewController.view.frame + sampleBufferVideoCallView.bounds = pipVideoCallViewController.view.frame let pipContentSource = AVPictureInPictureController.ContentSource( activeVideoCallSourceView: videoView, diff --git a/VideoApp/VideoApp/SwiftUI/Screens/Home/HomeView.swift b/VideoApp/VideoApp/SwiftUI/Screens/Home/HomeView.swift index b52496ae..5b184c78 100644 --- a/VideoApp/VideoApp/SwiftUI/Screens/Home/HomeView.swift +++ b/VideoApp/VideoApp/SwiftUI/Screens/Home/HomeView.swift @@ -55,6 +55,7 @@ struct HomeView: View { isShowingPictureInPicture = true } .buttonStyle(.borderedProminent) + .hidden() } .toolbar { Button(action: { isShowingSettings.toggle() }) { diff --git a/VideoApp/VideoApp/SwiftUI/Views/Core/SwiftUIVideoView.swift b/VideoApp/VideoApp/SwiftUI/Views/Core/SwiftUIVideoView.swift index 5ce5a39b..4fe92fc3 100644 --- a/VideoApp/VideoApp/SwiftUI/Views/Core/SwiftUIVideoView.swift +++ b/VideoApp/VideoApp/SwiftUI/Views/Core/SwiftUIVideoView.swift @@ -46,7 +46,7 @@ struct SwiftUIVideoView: UIViewRepresentable { /// /// It also provides a `VideoTrack` reference to `SwiftUIVideoView` so that `SwiftUIVideoView` can /// remove the `VideoView` from the `VideoTrack` when `dismantleUIView` is called.` -class VideoTrackStoringVideoView: SampleBufferVideoView { +class VideoTrackStoringVideoView: VideoView { var videoTrack: VideoTrack? { didSet { guard oldValue != videoTrack else { return } diff --git a/VideoApp/VideoApp/TwilioVideo/Room/RoomManager.swift b/VideoApp/VideoApp/TwilioVideo/Room/RoomManager.swift index ef151890..7fb9e34d 100644 --- a/VideoApp/VideoApp/TwilioVideo/Room/RoomManager.swift +++ b/VideoApp/VideoApp/TwilioVideo/Room/RoomManager.swift @@ -40,7 +40,7 @@ class RoomManager: NSObject, ObservableObject { self.localParticipant = localParticipant } - func connect(roomName: String, accessToken: String, uuid: UUID) { + func connect(roomName: String, accessToken: String, uuid: UUID) { let options = ConnectOptionsFactory().makeConnectOptions( accessToken: accessToken, roomName: roomName, From 9e12357ba695b20b082ea57639afd2da6469a0ef Mon Sep 17 00:00:00 2001 From: Piyush Tank Date: Wed, 26 Oct 2022 21:24:40 -0700 Subject: [PATCH 6/8] VIDEO-11812 - Added entitlements for PiP iOS 15 (#219) --- VideoApp/VideoApp/Video.entitlements | 2 ++ 1 file changed, 2 insertions(+) diff --git a/VideoApp/VideoApp/Video.entitlements b/VideoApp/VideoApp/Video.entitlements index a40d345e..22fe1a61 100755 --- a/VideoApp/VideoApp/Video.entitlements +++ b/VideoApp/VideoApp/Video.entitlements @@ -6,5 +6,7 @@ applinks:twilio-video-react.appspot.com + com.apple.developer.avfoundation.multitasking-camera-access + From 6debf70b716064110748198459e1644275bd9cc1 Mon Sep 17 00:00:00 2001 From: Piyush Tank Date: Mon, 7 Nov 2022 19:34:33 -0800 Subject: [PATCH 7/8] Prepare for merge pip to master (#221) --- VideoApp/VideoApp.xcodeproj/project.pbxproj | 64 ++++++++----------- .../xcshareddata/swiftpm/Package.resolved | 4 +- 2 files changed, 29 insertions(+), 39 deletions(-) diff --git a/VideoApp/VideoApp.xcodeproj/project.pbxproj b/VideoApp/VideoApp.xcodeproj/project.pbxproj index a4734738..b1209d06 100644 --- a/VideoApp/VideoApp.xcodeproj/project.pbxproj +++ b/VideoApp/VideoApp.xcodeproj/project.pbxproj @@ -9,7 +9,6 @@ /* Begin PBXBuildFile section */ 24C5217A20FFAD030018E2D8 /* EmailPasswordLoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24C5217820FFAD030018E2D8 /* EmailPasswordLoginViewController.swift */; }; 24C5217B20FFAD030018E2D8 /* EmailPasswordLoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24C5217820FFAD030018E2D8 /* EmailPasswordLoginViewController.swift */; }; - 2FAF8BA028F7351600285505 /* TwilioVideo in Frameworks */ = {isa = PBXBuildFile; productRef = 2FAF8B9F28F7351600285505 /* TwilioVideo */; }; 4B0012261FBA52C4004A587E /* StatsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 24A723E21ECE1C8500486E7A /* StatsViewController.m */; }; 4B0012291FBA52C4004A587E /* TrackStatsTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 24A723EC1ECE1D2A00486E7A /* TrackStatsTableViewCell.m */; }; 4B00122C1FBA52C4004A587E /* StatsUIModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 24A723E61ECE1CBD00486E7A /* StatsUIModel.m */; }; @@ -266,6 +265,8 @@ DCB3BAD8282DA2C400AF7072 /* AppCenterDistribute in Frameworks */ = {isa = PBXBuildFile; productRef = DCB3BAD7282DA2C400AF7072 /* AppCenterDistribute */; }; DCB3BADB282DA30B00AF7072 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = DCB3BADA282DA30B00AF7072 /* KeychainAccess */; }; DCB3BADD282DA31400AF7072 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = DCB3BADC282DA31400AF7072 /* KeychainAccess */; }; + DCB3BAE0282DA34400AF7072 /* TwilioVideo in Frameworks */ = {isa = PBXBuildFile; productRef = DCB3BADF282DA34400AF7072 /* TwilioVideo */; }; + DCB3BAE2282DA35100AF7072 /* TwilioVideo in Frameworks */ = {isa = PBXBuildFile; productRef = DCB3BAE1282DA35100AF7072 /* TwilioVideo */; }; DCB64B502409976F00E090BE /* APIRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCB64B4E2409976F00E090BE /* APIRequest.swift */; }; DCB64B512409976F00E090BE /* APIRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCB64B4E2409976F00E090BE /* APIRequest.swift */; }; DCB64B562409BC4E00E090BE /* APIConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCB64B542409BC4E00E090BE /* APIConfig.swift */; }; @@ -446,19 +447,6 @@ }; /* End PBXContainerItemProxy section */ -/* Begin PBXCopyFilesBuildPhase section */ - DC5E3DA128B6D21C009E3CF2 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - /* Begin PBXFileReference section */ 241282441E36A6AB002198BE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 241282461E36A6AB002198BE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -475,7 +463,6 @@ 24A723EC1ECE1D2A00486E7A /* TrackStatsTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TrackStatsTableViewCell.m; sourceTree = ""; }; 24C5217720FFAD030018E2D8 /* Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = ""; }; 24C5217820FFAD030018E2D8 /* EmailPasswordLoginViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailPasswordLoginViewController.swift; sourceTree = ""; }; - 2FFFBC0628ECE5B000210E81 /* TwilioVideo.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = TwilioVideo.xcframework; path = "../../video-ios-internal/temp/Artifacts/Debug/Dynamic/TwilioVideo.xcframework"; sourceTree = ""; }; 4B00124E1FBA52C4004A587E /* Video-Internal.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Video-Internal.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 4B00128B1FBA52E5004A587E /* Video-Community.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Video-Community.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 8A206F0D263912B600EFCD97 /* ClientTrackSwitchOffControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientTrackSwitchOffControl.swift; sourceTree = ""; }; @@ -706,11 +693,11 @@ files = ( DCB3BAD6282DA2B300AF7072 /* AppCenterDistribute in Frameworks */, DC699C87282DB4E9009D3C1A /* GoogleSignIn in Frameworks */, - 2FAF8BA028F7351600285505 /* TwilioVideo in Frameworks */, DC699C97282EB231009D3C1A /* FirebaseAnalytics in Frameworks */, DC699C7C282DB045009D3C1A /* FirebaseAuth in Frameworks */, DCB3BAD0282DA20000AF7072 /* Alamofire in Frameworks */, DC699C7E282DB045009D3C1A /* FirebaseCrashlytics in Frameworks */, + DCB3BAE0282DA34400AF7072 /* TwilioVideo in Frameworks */, DCB3BADB282DA30B00AF7072 /* KeychainAccess in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -722,6 +709,7 @@ DCB3BAD8282DA2C400AF7072 /* AppCenterDistribute in Frameworks */, DC699C89282DB536009D3C1A /* GoogleSignIn in Frameworks */, DCB3BAD3282DA23200AF7072 /* Alamofire in Frameworks */, + DCB3BAE2282DA35100AF7072 /* TwilioVideo in Frameworks */, DC699C82282DB084009D3C1A /* FirebaseCrashlytics in Frameworks */, DCB3BADD282DA31400AF7072 /* KeychainAccess in Frameworks */, DC699C84282DB08B009D3C1A /* FirebaseAuth in Frameworks */, @@ -1388,7 +1376,6 @@ DCB3BAD1282DA23200AF7072 /* Frameworks */ = { isa = PBXGroup; children = ( - 2FFFBC0628ECE5B000210E81 /* TwilioVideo.xcframework */, ); name = Frameworks; sourceTree = ""; @@ -1622,11 +1609,11 @@ DCB3BACF282DA20000AF7072 /* Alamofire */, DCB3BAD5282DA2B300AF7072 /* AppCenterDistribute */, DCB3BADA282DA30B00AF7072 /* KeychainAccess */, + DCB3BADF282DA34400AF7072 /* TwilioVideo */, DC699C7B282DB045009D3C1A /* FirebaseAuth */, DC699C7D282DB045009D3C1A /* FirebaseCrashlytics */, DC699C86282DB4E9009D3C1A /* GoogleSignIn */, DC699C96282EB231009D3C1A /* FirebaseAnalytics */, - 2FAF8B9F28F7351600285505 /* TwilioVideo */, ); productName = VideoApp; productReference = 4B00124E1FBA52C4004A587E /* Video-Internal.app */; @@ -1639,7 +1626,6 @@ 4B0012601FBA52E5004A587E /* Sources */, 4B0012721FBA52E5004A587E /* Frameworks */, 4B00127D1FBA52E5004A587E /* Resources */, - DC5E3DA128B6D21C009E3CF2 /* Embed Frameworks */, ); buildRules = ( ); @@ -1650,6 +1636,7 @@ DCB3BAD2282DA23200AF7072 /* Alamofire */, DCB3BAD7282DA2C400AF7072 /* AppCenterDistribute */, DCB3BADC282DA31400AF7072 /* KeychainAccess */, + DCB3BAE1282DA35100AF7072 /* TwilioVideo */, DC699C7F282DB07D009D3C1A /* FirebaseAnalyticsSwift */, DC699C81282DB084009D3C1A /* FirebaseCrashlytics */, DC699C83282DB08B009D3C1A /* FirebaseAuth */, @@ -1777,11 +1764,11 @@ DCB3BACE282DA20000AF7072 /* XCRemoteSwiftPackageReference "Alamofire" */, DCB3BAD4282DA2B300AF7072 /* XCRemoteSwiftPackageReference "appcenter-sdk-apple" */, DCB3BAD9282DA30B00AF7072 /* XCRemoteSwiftPackageReference "KeychainAccess" */, + DCB3BADE282DA34400AF7072 /* XCRemoteSwiftPackageReference "twilio-video-ios" */, DC699C78282DB045009D3C1A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, DC699C85282DB4E9009D3C1A /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */, DC699C8A282EAD46009D3C1A /* XCRemoteSwiftPackageReference "Quick" */, DC699C8F282EAE21009D3C1A /* XCRemoteSwiftPackageReference "Nimble" */, - 2FAF8B9E28F7351600285505 /* XCRemoteSwiftPackageReference "twilio-video-ios" */, ); productRefGroup = 241282381E36A6AB002198BE /* Products */; projectDirPath = ""; @@ -2354,7 +2341,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.5; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; OTHER_SWIFT_FLAGS = "-D DEBUG"; @@ -2411,7 +2398,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.5; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = NO; OTHER_SWIFT_FLAGS = ""; PACKAGE_ROOT = "$(SRCROOT)/.."; @@ -2436,7 +2423,6 @@ CURRENT_PROJECT_VERSION = 95; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = SX5J6BN2KX; - ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", @@ -2478,7 +2464,6 @@ CURRENT_PROJECT_VERSION = 95; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = SX5J6BN2KX; - ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; INFOPLIST_FILE = VideoApp/Info.plist; @@ -2817,14 +2802,6 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - 2FAF8B9E28F7351600285505 /* XCRemoteSwiftPackageReference "twilio-video-ios" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "git@github.com:twilio/twilio-video-ios.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 5.0.0; - }; - }; DC699C78282DB045009D3C1A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/firebase/firebase-ios-sdk.git"; @@ -2881,14 +2858,17 @@ minimumVersion = 4.0.0; }; }; + DCB3BADE282DA34400AF7072 /* XCRemoteSwiftPackageReference "twilio-video-ios" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/twilio/twilio-video-ios"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 5.3.0; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - 2FAF8B9F28F7351600285505 /* TwilioVideo */ = { - isa = XCSwiftPackageProductDependency; - package = 2FAF8B9E28F7351600285505 /* XCRemoteSwiftPackageReference "twilio-video-ios" */; - productName = TwilioVideo; - }; DC699C7B282DB045009D3C1A /* FirebaseAuth */ = { isa = XCSwiftPackageProductDependency; package = DC699C78282DB045009D3C1A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; @@ -2984,6 +2964,16 @@ package = DCB3BAD9282DA30B00AF7072 /* XCRemoteSwiftPackageReference "KeychainAccess" */; productName = KeychainAccess; }; + DCB3BADF282DA34400AF7072 /* TwilioVideo */ = { + isa = XCSwiftPackageProductDependency; + package = DCB3BADE282DA34400AF7072 /* XCRemoteSwiftPackageReference "twilio-video-ios" */; + productName = TwilioVideo; + }; + DCB3BAE1282DA35100AF7072 /* TwilioVideo */ = { + isa = XCSwiftPackageProductDependency; + package = DCB3BADE282DA34400AF7072 /* XCRemoteSwiftPackageReference "twilio-video-ios" */; + productName = TwilioVideo; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 2412822F1E36A6AB002198BE /* Project object */; diff --git a/VideoApp/VideoApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/VideoApp/VideoApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 0c85da3a..84596fcd 100644 --- a/VideoApp/VideoApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/VideoApp/VideoApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -212,8 +212,8 @@ "kind" : "remoteSourceControl", "location" : "git@github.com:twilio/twilio-video-ios.git", "state" : { - "revision" : "4951033ac1469d80deb3c2313bc11a4d486fc610", - "version" : "5.2.1" + "revision" : "4f3c31a7f49bc92738faca8612532aa1d1590573", + "version" : "5.3.0" } } ], From 085652ddc0b0fa411ad12dca29b585467b46f5c1 Mon Sep 17 00:00:00 2001 From: Piyush Tank Date: Mon, 7 Nov 2022 19:36:19 -0800 Subject: [PATCH 8/8] Update package.resolved --- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VideoApp/VideoApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/VideoApp/VideoApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 84596fcd..1cc3e2b2 100644 --- a/VideoApp/VideoApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/VideoApp/VideoApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -210,7 +210,7 @@ { "identity" : "twilio-video-ios", "kind" : "remoteSourceControl", - "location" : "git@github.com:twilio/twilio-video-ios.git", + "location" : "https://github.com/twilio/twilio-video-ios", "state" : { "revision" : "4f3c31a7f49bc92738faca8612532aa1d1590573", "version" : "5.3.0"